/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.sql.optimizer.rule; import static com.foundationdb.util.FileTestUtils.printClickableFile; import com.foundationdb.server.types.service.TypesRegistryServiceImpl; import com.foundationdb.sql.TestBase; import com.foundationdb.sql.optimizer.FunctionsTypeComputer; import com.foundationdb.sql.optimizer.NestedResultSetTypeComputer; import com.foundationdb.sql.optimizer.OptimizerTestBase; import com.foundationdb.sql.optimizer.plan.AST; import com.foundationdb.sql.optimizer.plan.PlanNode; import com.foundationdb.sql.parser.DMLStatementNode; import com.foundationdb.sql.parser.StatementNode; import com.foundationdb.ais.model.AkibanInformationSchema; import com.foundationdb.junit.SelectedParameterizedRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized.Parameters; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Properties; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.IOException; @RunWith(SelectedParameterizedRunner.class) public class RulesTest extends OptimizerTestBase implements TestBase.GenerateAndCheckResult { public static final File RESOURCE_DIR = new File(OptimizerTestBase.RESOURCE_DIR, "rule"); protected File rulesFile, schemaFile, indexFile, statsFile, propertiesFile, extraDDL; @Parameters(name="{0}") public static Iterable<Object[]> statements() throws Exception { Collection<Object[]> result = new ArrayList<>(); for (File subdir : RESOURCE_DIR.listFiles(new FileFilter() { public boolean accept(File file) { return file.isDirectory(); } })) { File rulesFile = new File (subdir, "rules.yml"); File schemaFile = new File(subdir, "schema.ddl"); if (rulesFile.exists() && schemaFile.exists()) { File defaultStatsFile = new File(subdir, "stats.yaml"); File defaultPropertiesFile = new File(subdir, "compiler.properties"); File defaultExtraDDL = new File(subdir, "schema-extra.ddl"); if (!defaultStatsFile.exists()) defaultStatsFile = null; if (!defaultPropertiesFile.exists()) defaultPropertiesFile = null; if (!defaultExtraDDL.exists()) defaultExtraDDL = null; for (Object[] args : sqlAndExpected(subdir)) { File statsFile = new File(subdir, args[0] + ".stats.yaml"); File propertiesFile = new File(subdir, args[0] + ".properties"); File extraDDL = new File(subdir, args[0] + ".ddl"); if (!statsFile.exists()) statsFile = defaultStatsFile; if (!propertiesFile.exists()) propertiesFile = defaultPropertiesFile; if (!extraDDL.exists()) extraDDL = defaultExtraDDL; Object[] nargs = new Object[args.length+5]; nargs[0] = subdir.getName() + "/" + args[0]; nargs[1] = rulesFile; nargs[2] = schemaFile; nargs[3] = statsFile; nargs[4] = propertiesFile; nargs[5] = extraDDL; System.arraycopy(args, 1, nargs, 6, args.length-1); result.add(nargs); } } } return result; } public RulesTest(String caseName, File rulesFile, File schemaFile, File statsFile, File propertiesFile, File extraDDL, String sql, String expected, String error) { super(caseName, sql, expected, error); this.rulesFile = rulesFile; this.schemaFile = schemaFile; this.statsFile = statsFile; this.propertiesFile = propertiesFile; this.extraDDL = extraDDL; } protected Properties properties; protected RulesContext rules; @Before public void loadDDL() throws Exception { List<File> schemaFiles = new ArrayList<>(2); schemaFiles.add(schemaFile); if (extraDDL != null) schemaFiles.add(extraDDL); AkibanInformationSchema ais = loadSchema(schemaFiles); properties = new Properties(); if (propertiesFile != null) { FileInputStream fstr = new FileInputStream(propertiesFile); try { properties.load(fstr); } finally { fstr.close(); } } rules = RulesTestContext.create(ais, statsFile, extraDDL != null, RulesTestHelper.loadRules(rulesFile), properties); // Normally set as a consequence of OutputFormat. if (Boolean.parseBoolean(properties.getProperty("allowSubqueryMultipleColumns", "false"))) { binder.setAllowSubqueryMultipleColumns(true); typeComputer = new NestedResultSetTypeComputer(TypesRegistryServiceImpl.createRegistryService()); } if (Boolean.parseBoolean(properties.getProperty("resultColumnsAvailableBroadly", "false"))) { binder.setResultColumnsAvailableBroadly(true); } } @Test public void testRules() throws Exception { try { generateAndCheckResult(); } catch (Throwable e) { System.err.println("Failed Rules test (note: line number is always 1)"); String filePathPrefix = RESOURCE_DIR + "/" + caseName; printClickableFile(filePathPrefix, "sql", 1); printClickableFile(filePathPrefix, "expected", 1); throw e; } } @Override public String generateResult() throws Exception { StatementNode stmt = parser.parseStatement(sql); binder.bind(stmt); stmt = booleanNormalizer.normalize(stmt); typeComputer.compute(stmt); stmt = subqueryFlattener.flatten((DMLStatementNode)stmt); // Turn parsed AST into intermediate form as starting point. AST ast = new AST((DMLStatementNode)stmt, parser.getParameterList()); PlanContext plan = new PlanContext(rules, ast); rules.applyRules(plan); PlanNode.SummaryConfiguration configuration = new PlanNode.SummaryConfiguration( Boolean.parseBoolean(properties.getProperty("showRowTypes", "false")), Boolean.parseBoolean(properties.getProperty("includeIndexTableNames", "false"))); String result = plan.planString(configuration); if (Boolean.parseBoolean(properties.getProperty("showParameterTypes", "false"))) result = ast.formatParameterTypes() + result; return result; } @Override public void checkResult(String result) throws IOException { assertEqualsWithoutHashes(caseName, expected, result); } }