Repository: calcite Updated Branches: refs/heads/master a0c98837c -> c933c79f7
[CALCITE-1321] When converting IN-list to join, make minimum list size configurable (Gautam Parai) Add SqlToRelConverter.Config, so that all configuration parameters can be passed as a single immutable object. Close apache/calcite#257 Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/c933c79f Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/c933c79f Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/c933c79f Branch: refs/heads/master Commit: c933c79f7ba2ccedd109c4a3cb55e23cfbe90f2b Parents: a0c9883 Author: Gautam Parai <[email protected]> Authored: Tue Jul 19 14:56:32 2016 -0700 Committer: Julian Hyde <[email protected]> Committed: Tue Jul 26 21:42:51 2016 -0700 ---------------------------------------------------------------------- .../calcite/prepare/CalciteMaterializer.java | 5 +- .../calcite/prepare/CalcitePrepareImpl.java | 25 +- .../org/apache/calcite/prepare/PlannerImpl.java | 13 +- .../org/apache/calcite/prepare/Prepare.java | 25 +- .../calcite/sql2rel/SqlToRelConverter.java | 304 ++++++++++++++----- .../apache/calcite/test/RelOptRulesTest.java | 8 +- .../calcite/test/SqlToRelConverterTest.java | 41 ++- .../apache/calcite/test/SqlToRelTestBase.java | 43 ++- .../calcite/test/SqlToRelConverterTest.xml | 22 ++ .../java/org/apache/calcite/test/CsvTest.java | 8 +- 10 files changed, 365 insertions(+), 129 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/c933c79f/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java b/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java index e5a5d57..ec986c8 100644 --- a/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java +++ b/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java @@ -76,9 +76,10 @@ class CalciteMaterializer extends CalcitePrepareImpl.CalcitePreparingStmt { } catch (SqlParseException e) { throw new RuntimeException("parse failed", e); } - + final SqlToRelConverter.Config config = SqlToRelConverter.configBuilder() + .withTrimUnusedFields(true).build(); SqlToRelConverter sqlToRelConverter2 = - getSqlToRelConverter(getSqlValidator(), catalogReader); + getSqlToRelConverter(getSqlValidator(), catalogReader, config); materialization.queryRel = sqlToRelConverter2.convertQuery(node, true, true).rel; http://git-wip-us.apache.org/repos/asf/calcite/blob/c933c79f/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java index bfc1ce2..46290fa 100644 --- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java +++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java @@ -297,14 +297,20 @@ public class CalcitePrepareImpl implements CalcitePrepare { : EnumerableConvention.INSTANCE; final HepPlanner planner = new HepPlanner(new HepProgramBuilder().build()); planner.addRelTraitDef(ConventionTraitDef.INSTANCE); + + final SqlToRelConverter.ConfigBuilder configBuilder = + SqlToRelConverter.configBuilder().withTrimUnusedFields(true); + if (analyze) { + configBuilder.withConvertTableAccess(false); + } + final CalcitePreparingStmt preparingStmt = new CalcitePreparingStmt(this, context, catalogReader, typeFactory, context.getRootSchema(), null, planner, resultConvention); final SqlToRelConverter converter = - preparingStmt.getSqlToRelConverter(validator, catalogReader); - if (analyze) { - converter.enableTableAccessConversion(false); - } + preparingStmt.getSqlToRelConverter(validator, catalogReader, + configBuilder.build()); + final RelRoot root = converter.convertQuery(sqlNode1, false, true); if (analyze) { return analyze_(validator, sql, sqlNode1, root, fail); @@ -1095,12 +1101,12 @@ public class CalcitePrepareImpl implements CalcitePrepare { @Override protected SqlToRelConverter getSqlToRelConverter( SqlValidator validator, - CatalogReader catalogReader) { + CatalogReader catalogReader, + SqlToRelConverter.Config config) { final RelOptCluster cluster = prepare.createCluster(planner, rexBuilder); SqlToRelConverter sqlToRelConverter = new SqlToRelConverter(this, validator, catalogReader, cluster, - StandardConvertletTable.INSTANCE); - sqlToRelConverter.setTrimUnusedFields(true); + StandardConvertletTable.INSTANCE, config); return sqlToRelConverter; } @@ -1135,9 +1141,10 @@ public class CalcitePrepareImpl implements CalcitePrepare { this.catalogReader.withSchemaPath(schemaPath); SqlValidator validator = createSqlValidator(catalogReader); SqlNode sqlNode1 = validator.validate(sqlNode); - + final SqlToRelConverter.Config config = SqlToRelConverter.configBuilder() + .withTrimUnusedFields(true).build(); SqlToRelConverter sqlToRelConverter = - getSqlToRelConverter(validator, catalogReader); + getSqlToRelConverter(validator, catalogReader, config); RelRoot root = sqlToRelConverter.convertQuery(sqlNode1, true, false); http://git-wip-us.apache.org/repos/asf/calcite/blob/c933c79f/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java index a472683..7fab355 100644 --- a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java +++ b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java @@ -218,11 +218,11 @@ public class PlannerImpl implements Planner { assert validatedSqlNode != null; final RexBuilder rexBuilder = createRexBuilder(); final RelOptCluster cluster = RelOptCluster.create(planner, rexBuilder); + final SqlToRelConverter.Config config = SqlToRelConverter.configBuilder() + .withTrimUnusedFields(false).withConvertTableAccess(false).build(); final SqlToRelConverter sqlToRelConverter = new SqlToRelConverter(new ViewExpanderImpl(), validator, - createCatalogReader(), cluster, convertletTable); - sqlToRelConverter.setTrimUnusedFields(false); - sqlToRelConverter.enableTableAccessConversion(false); + createCatalogReader(), cluster, convertletTable, config); root = sqlToRelConverter.convertQuery(validatedSqlNode, false, true); root = root.withRel(sqlToRelConverter.flattenTypes(root.rel, true)); @@ -255,12 +255,11 @@ public class PlannerImpl implements Planner { final RexBuilder rexBuilder = createRexBuilder(); final RelOptCluster cluster = RelOptCluster.create(planner, rexBuilder); + final SqlToRelConverter.Config config = SqlToRelConverter.configBuilder() + .withTrimUnusedFields(false).withConvertTableAccess(false).build(); final SqlToRelConverter sqlToRelConverter = new SqlToRelConverter(new ViewExpanderImpl(), validator, - catalogReader, cluster, convertletTable); - - sqlToRelConverter.setTrimUnusedFields(false); - sqlToRelConverter.enableTableAccessConversion(false); + catalogReader, cluster, convertletTable, config); root = sqlToRelConverter.convertQuery(validatedSqlNode, true, false); root = root.withRel(sqlToRelConverter.flattenTypes(root.rel, true)); http://git-wip-us.apache.org/repos/asf/calcite/blob/c933c79f/core/src/main/java/org/apache/calcite/prepare/Prepare.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/Prepare.java b/core/src/main/java/org/apache/calcite/prepare/Prepare.java index e95495d..8af414d 100644 --- a/core/src/main/java/org/apache/calcite/prepare/Prepare.java +++ b/core/src/main/java/org/apache/calcite/prepare/Prepare.java @@ -208,16 +208,21 @@ public abstract class Prepare { init(runtimeContextClass); - SqlToRelConverter sqlToRelConverter = - getSqlToRelConverter(validator, catalogReader); - sqlToRelConverter.setExpand(THREAD_EXPAND.get()); + final SqlToRelConverter.ConfigBuilder builder = + SqlToRelConverter.configBuilder() + .withTrimUnusedFields(true) + .withExpand(THREAD_EXPAND.get()) + .withExplain(sqlQuery.getKind() == SqlKind.EXPLAIN); + final SqlToRelConverter sqlToRelConverter = + getSqlToRelConverter(validator, catalogReader, builder.build()); SqlExplain sqlExplain = null; if (sqlQuery.getKind() == SqlKind.EXPLAIN) { // dig out the underlying SQL statement sqlExplain = (SqlExplain) sqlQuery; sqlQuery = sqlExplain.getExplicandum(); - sqlToRelConverter.setIsExplain(sqlExplain.getDynamicParamCount()); + sqlToRelConverter.setDynamicParamCountInExplain( + sqlExplain.getDynamicParamCount()); } RelRoot root = @@ -319,7 +324,8 @@ public abstract class Prepare { */ protected abstract SqlToRelConverter getSqlToRelConverter( SqlValidator validator, - CatalogReader catalogReader); + CatalogReader catalogReader, + SqlToRelConverter.Config config); public abstract RelNode flattenTypes( RelNode rootRel, @@ -342,11 +348,12 @@ public abstract class Prepare { * @return Trimmed relational expression */ protected RelRoot trimUnusedFields(RelRoot root) { + final SqlToRelConverter.Config config = SqlToRelConverter.configBuilder() + .withTrimUnusedFields(shouldTrim(root.rel)) + .withExpand(THREAD_EXPAND.get()) + .build(); final SqlToRelConverter converter = - getSqlToRelConverter( - getSqlValidator(), catalogReader); - converter.setTrimUnusedFields(shouldTrim(root.rel)); - converter.setExpand(THREAD_EXPAND.get()); + getSqlToRelConverter(getSqlValidator(), catalogReader, config); final boolean ordered = !root.collation.getFieldCollations().isEmpty(); final boolean dml = SqlKind.DML.contains(root.kind); return root.withRel(converter.trimUnusedFields(dml || ordered, root.rel)); http://git-wip-us.apache.org/repos/asf/calcite/blob/c933c79f/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java index 898e402..d450a34 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java @@ -207,7 +207,7 @@ public class SqlToRelConverter { /** Size of the smallest IN list that will be converted to a semijoin to a * static table. */ - public static final int IN_SUBQUERY_THRESHOLD = 20; + public static final int DEFAULT_IN_SUBQUERY_THRESHOLD = 20; //~ Instance fields -------------------------------------------------------- @@ -220,14 +220,10 @@ public class SqlToRelConverter { protected final List<RelNode> leaves = new ArrayList<>(); private final List<SqlDynamicParam> dynamicParamSqlNodes = new ArrayList<>(); private final SqlOperatorTable opTab; - private boolean shouldConvertTableAccess; protected final RelDataTypeFactory typeFactory; private final SqlNodeToRexConverter exprConverter; - private boolean decorrelationEnabled; - private boolean trimUnusedFields; - private boolean shouldCreateValuesRel; - private boolean isExplain; - private int nDynamicParamsInExplain; + private int explainParamCount; + public final SqlToRelConverter.Config config; /** * Fields used in name resolution for correlated subqueries. @@ -251,10 +247,6 @@ public class SqlToRelConverter { public final RelOptTable.ViewExpander viewExpander; - /** Whether to expand sub-queries. If false, each sub-query becomes a - * {@link org.apache.calcite.rex.RexSubQuery}. */ - private boolean expand = true; - //~ Constructors ----------------------------------------------------------- /** * Creates a converter. @@ -266,7 +258,7 @@ public class SqlToRelConverter { * @param rexBuilder Rex builder * @param convertletTable Expression converter */ - @Deprecated // will be removed before 2.0 + @Deprecated // to be removed before 2.0 public SqlToRelConverter( RelOptTable.ViewExpander viewExpander, SqlValidator validator, @@ -275,16 +267,29 @@ public class SqlToRelConverter { RexBuilder rexBuilder, SqlRexConvertletTable convertletTable) { this(viewExpander, validator, catalogReader, - RelOptCluster.create(planner, rexBuilder), convertletTable); + RelOptCluster.create(planner, rexBuilder), convertletTable, + Config.DEFAULT); } - /* Creates a converter. */ + @Deprecated // to be removed before 2.0 public SqlToRelConverter( RelOptTable.ViewExpander viewExpander, SqlValidator validator, Prepare.CatalogReader catalogReader, RelOptCluster cluster, SqlRexConvertletTable convertletTable) { + this(viewExpander, validator, catalogReader, cluster, convertletTable, + Config.DEFAULT); + } + + /* Creates a converter. */ + public SqlToRelConverter( + RelOptTable.ViewExpander viewExpander, + SqlValidator validator, + Prepare.CatalogReader catalogReader, + RelOptCluster cluster, + SqlRexConvertletTable convertletTable, + Config config) { this.viewExpander = viewExpander; this.opTab = (validator @@ -297,14 +302,9 @@ public class SqlToRelConverter { this.rexBuilder = cluster.getRexBuilder(); this.typeFactory = rexBuilder.getTypeFactory(); this.cluster = Preconditions.checkNotNull(cluster); - this.shouldConvertTableAccess = true; - this.exprConverter = - new SqlNodeToRexConverterImpl(convertletTable); - decorrelationEnabled = true; - trimUnusedFields = false; - shouldCreateValuesRel = true; - isExplain = false; - nDynamicParamsInExplain = 0; + this.exprConverter = new SqlNodeToRexConverterImpl(convertletTable); + this.explainParamCount = 0; + this.config = new ConfigBuilder().withConfig(config).build(); } //~ Methods ---------------------------------------------------------------- @@ -355,9 +355,9 @@ public class SqlToRelConverter { * @return the current count before the optional increment */ public int getDynamicParamCountInExplain(boolean increment) { - int retVal = nDynamicParamsInExplain; + int retVal = explainParamCount; if (increment) { - ++nDynamicParamsInExplain; + ++explainParamCount; } return retVal; } @@ -403,41 +403,14 @@ public class SqlToRelConverter { } /** - * Indicates that the current statement is part of an EXPLAIN PLAN statement + * Sets the number of dynamic parameters in the current EXPLAIN PLAN + * statement. * - * @param nDynamicParams number of dynamic parameters in the statement + * @param explainParamCount number of dynamic parameters in the statement */ - public void setIsExplain(int nDynamicParams) { - isExplain = true; - nDynamicParamsInExplain = nDynamicParams; - } - - /** - * Controls whether table access references are converted to physical rels - * immediately. The optimizer doesn't like leaf rels to have - * {@link Convention#NONE}. However, if we are doing further conversion - * passes (e.g. {@link RelStructuredTypeFlattener}), then we may need to - * defer conversion. To have any effect, this must be called before any - * convert method. - * - * @param enabled true for immediate conversion (the default); false to - * generate logical LogicalTableScan instances - */ - public void enableTableAccessConversion(boolean enabled) { - shouldConvertTableAccess = enabled; - } - - /** - * Controls whether instances of - * {@link org.apache.calcite.rel.logical.LogicalValues} are generated. These - * may not be supported by all physical implementations. To have any effect, - * this must be called before any convert method. - * - * @param enabled true to allow LogicalValues to be generated (the default); - * false to force substitution of Project+OneRow instead - */ - public void enableValuesRelCreation(boolean enabled) { - shouldCreateValuesRel = enabled; + public void setDynamicParamCountInExplain(int explainParamCount) { + assert config.isExplain(); + this.explainParamCount = explainParamCount; } private void checkConvertedType(SqlNode query, RelNode result) { @@ -1041,7 +1014,7 @@ public class SqlToRelConverter { case IN: call = (SqlBasicCall) subQuery.node; query = call.operand(1); - if (!expand && !(query instanceof SqlNodeList)) { + if (!config.isExpand() && !(query instanceof SqlNodeList)) { return; } final SqlNode leftKeyNode = call.operand(0); @@ -1153,7 +1126,7 @@ public class SqlToRelConverter { // boolean indicating whether the subquery returned 0 or >= 1 row. call = (SqlBasicCall) subQuery.node; query = call.operand(0); - if (!expand) { + if (!config.isExpand()) { return; } converted = convertExists(query, RelOptUtil.SubqueryType.EXISTS, @@ -1168,7 +1141,7 @@ public class SqlToRelConverter { case SCALAR_QUERY: // Convert the subquery. If it's non-correlated, convert it // to a constant expression. - if (!expand) { + if (!config.isExpand()) { return; } call = (SqlBasicCall) subQuery.node; @@ -1331,7 +1304,7 @@ public class SqlToRelConverter { call, this, isExists, - isExplain); + config.isExplain()); } if (constExpr != null) { subQuery.expr = constExpr; @@ -1465,10 +1438,11 @@ public class SqlToRelConverter { * predicate. A threshold of 0 forces usage of an inline table in all cases; a * threshold of Integer.MAX_VALUE forces usage of OR in all cases * - * @return threshold, default {@link #IN_SUBQUERY_THRESHOLD} + * @return threshold, default {@link #DEFAULT_IN_SUBQUERY_THRESHOLD} */ + @Deprecated // to be removed before 2.0 protected int getInSubqueryThreshold() { - return IN_SUBQUERY_THRESHOLD; + return config.getInSubqueryThreshold(); } /** @@ -1568,7 +1542,7 @@ public class SqlToRelConverter { if ((rexLiteral == null) && allowLiteralsOnly) { return null; } - if ((rexLiteral == null) || !shouldCreateValuesRel) { + if ((rexLiteral == null) || !config.isCreateValuesRel()) { // fallback to convertRowConstructor tuple = null; break; @@ -1586,7 +1560,7 @@ public class SqlToRelConverter { bb, rowType, 0); - if ((rexLiteral != null) && shouldCreateValuesRel) { + if ((rexLiteral != null) && config.isCreateValuesRel()) { tupleList.add(ImmutableList.of(rexLiteral)); continue; } else { @@ -1977,7 +1951,7 @@ public class SqlToRelConverter { datasetName, usedDataset); final RelNode tableRel; - if (shouldConvertTableAccess) { + if (config.isConvertTableAccess()) { tableRel = toRel(table); } else { tableRel = LogicalTableScan.create(cluster, table); @@ -2840,10 +2814,11 @@ public class SqlToRelConverter { } } + @Deprecated // to be removed before 2.0 protected boolean enableDecorrelation() { // disable subquery decorrelation when needed. // e.g. if outer joins are not supported. - return decorrelationEnabled; + return config.isDecorrelationEnabled(); } protected RelNode decorrelateQuery(RelNode rootRel) { @@ -2851,25 +2826,13 @@ public class SqlToRelConverter { } /** - * Sets whether to trim unused fields as part of the conversion process. - * - * @param trim Whether to trim unused fields - */ - public void setTrimUnusedFields(boolean trim) { - this.trimUnusedFields = trim; - } - - /** * Returns whether to trim unused fields as part of the conversion process. * * @return Whether to trim unused fields */ + @Deprecated // to be removed before 2.0 public boolean isTrimUnusedFields() { - return trimUnusedFields; - } - - public void setExpand(boolean expand) { - this.expand = expand; + return config.isTrimUnusedFields(); } /** @@ -4092,7 +4055,7 @@ public class SqlToRelConverter { // expressions. final SqlKind kind = expr.getKind(); final SubQuery subQuery; - if (!expand) { + if (!config.isExpand()) { final SqlCall call; final SqlNode query; final RelRoot root; @@ -5063,6 +5026,183 @@ public class SqlToRelConverter { this.r = r; } } + + /** Creates a builder for a {@link Config}. */ + public static ConfigBuilder configBuilder() { + return new ConfigBuilder(); + } + + /** + * Interface to define the configuration for a SqlToRelConverter. + * Provides methods to set each configuration option. + * + * @see ConfigBuilder + * @see SqlToRelConverter#configBuilder() + */ + public interface Config { + /** Default configuration. */ + Config DEFAULT = configBuilder().build(); + + /** Returns the {@code convertTableAccess} option. Controls whether table + * access references are converted to physical rels immediately. The + * optimizer doesn't like leaf rels to have {@link Convention#NONE}. + * However, if we are doing further conversion passes (e.g. + * {@link RelStructuredTypeFlattener}), then we may need to defer + * conversion. */ + boolean isConvertTableAccess(); + + /** Returns the {@code decorrelationEnabled} option. Controls whether to + * disable subquery decorrelation when needed. e.g. if outer joins are not + * supported. */ + boolean isDecorrelationEnabled(); + + /** Returns the {@code trimUnusedFields} option. Controls whether to trim + * unused fields as part of the conversion process. */ + boolean isTrimUnusedFields(); + + /** Returns the {@code createValuesRel} option. Controls whether instances + * of {@link org.apache.calcite.rel.logical.LogicalValues} are generated. + * These may not be supported by all physical implementations. */ + boolean isCreateValuesRel(); + + /** Returns the {@code explain} option. Describes whether the current + * statement is part of an EXPLAIN PLAN statement. */ + boolean isExplain(); + + /** Returns the {@code expand} option. Controls whether to expand + * sub-queries. If false, each sub-query becomes a + * {@link org.apache.calcite.rex.RexSubQuery}. */ + boolean isExpand(); + + /** Returns the {@code inSubqueryThreshold} option, + * default {@link #DEFAULT_IN_SUBQUERY_THRESHOLD}. Controls the list size + * threshold under which {@link #convertInToOr} is used. Lists of this size + * or greater will instead be converted to use a join against an inline + * table ({@link org.apache.calcite.rel.logical.LogicalValues}) rather than + * a predicate. A threshold of 0 forces usage of an inline table in all + * cases; a threshold of {@link Integer#MAX_VALUE} forces usage of OR in all + * cases. */ + int getInSubqueryThreshold(); + } + + /** Builder for a {@link Config}. */ + public static class ConfigBuilder { + private boolean convertTableAccess = true; + private boolean decorrelationEnabled = true; + private boolean trimUnusedFields = false; + private boolean createValuesRel = true; + private boolean explain; + private boolean expand = true; + private int inSubqueryThreshold = DEFAULT_IN_SUBQUERY_THRESHOLD; + + private ConfigBuilder() {} + + /** Sets configuration identical to a given {@link Config}. */ + public ConfigBuilder withConfig(Config config) { + this.convertTableAccess = config.isConvertTableAccess(); + this.decorrelationEnabled = config.isDecorrelationEnabled(); + this.trimUnusedFields = config.isTrimUnusedFields(); + this.createValuesRel = config.isCreateValuesRel(); + this.explain = config.isExplain(); + this.expand = config.isExpand(); + this.inSubqueryThreshold = config.getInSubqueryThreshold(); + return this; + } + + public ConfigBuilder withConvertTableAccess(boolean convertTableAccess) { + this.convertTableAccess = convertTableAccess; + return this; + } + + public ConfigBuilder withDecorrelationEnabled(boolean enabled) { + this.decorrelationEnabled = enabled; + return this; + } + + public ConfigBuilder withTrimUnusedFields(boolean trimUnusedFields) { + this.trimUnusedFields = trimUnusedFields; + return this; + } + + public ConfigBuilder withCreateValuesRel(boolean createValuesRel) { + this.createValuesRel = createValuesRel; + return this; + } + + public ConfigBuilder withExplain(boolean explain) { + this.explain = explain; + return this; + } + + public ConfigBuilder withExpand(boolean expand) { + this.expand = expand; + return this; + } + + public ConfigBuilder withInSubqueryThreshold(int inSubqueryThreshold) { + this.inSubqueryThreshold = inSubqueryThreshold; + return this; + } + + /** Builds a {@link Config}. */ + public Config build() { + return new ConfigImpl(convertTableAccess, decorrelationEnabled, + trimUnusedFields, createValuesRel, explain, expand, + inSubqueryThreshold); + } + } + + /** Implementation of {@link Config}. + * Called by builder; all values are in private final fields. */ + private static class ConfigImpl implements Config { + private final boolean convertTableAccess; + private final boolean decorrelationEnabled; + private final boolean trimUnusedFields; + private final boolean createValuesRel; + private final boolean explain; + private final int inSubqueryThreshold; + private final boolean expand; + + private ConfigImpl(boolean convertTableAccess, boolean decorrelationEnabled, + boolean trimUnusedFields, boolean createValuesRel, boolean explain, + boolean expand, int inSubqueryThreshold) { + this.convertTableAccess = convertTableAccess; + this.decorrelationEnabled = decorrelationEnabled; + this.trimUnusedFields = trimUnusedFields; + this.createValuesRel = createValuesRel; + this.explain = explain; + this.expand = expand; + this.inSubqueryThreshold = inSubqueryThreshold; + } + + public boolean isConvertTableAccess() { + return convertTableAccess; + } + + public boolean isDecorrelationEnabled() { + return decorrelationEnabled; + } + + public boolean isTrimUnusedFields() { + return trimUnusedFields; + } + + public boolean isCreateValuesRel() { + return createValuesRel; + } + + public boolean isExplain() { + return explain; + } + + public boolean isExpand() { + return expand; + } + + public int getInSubqueryThreshold() { + return inSubqueryThreshold; + } + } } // End SqlToRelConverter.java http://git-wip-us.apache.org/repos/asf/calcite/blob/c933c79f/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java index d9340fb..b8de6ca 100644 --- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java @@ -519,11 +519,12 @@ public class RelOptRulesTest extends RelOptTestBase { final SqlValidator validator = t.createValidator( catalogReader, typeFactory); - final SqlToRelConverter converter = + SqlToRelConverter converter = t.createSqlToRelConverter( validator, catalogReader, - typeFactory); + typeFactory, + SqlToRelConverter.Config.DEFAULT); final SqlNode sqlQuery; try { @@ -551,7 +552,8 @@ public class RelOptRulesTest extends RelOptTestBase { String planBefore = NL + RelOptUtil.toString(root.rel); diffRepos.assertEquals("planBefore", "${planBefore}", planBefore); - converter.setTrimUnusedFields(true); + converter = t.createSqlToRelConverter(validator, catalogReader, typeFactory, + SqlToRelConverter.configBuilder().withTrimUnusedFields(true).build()); root = root.withRel(converter.trimUnusedFields(false, root.rel)); String planAfter = NL + RelOptUtil.toString(root.rel); diffRepos.assertEquals("planAfter", "${planAfter}", planAfter); http://git-wip-us.apache.org/repos/asf/calcite/blob/c933c79f/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java index 5162577..e07c77b 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java @@ -24,6 +24,7 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.sql.SqlExplainLevel; import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.sql2rel.SqlToRelConverter; import org.apache.calcite.util.Bug; import org.apache.calcite.util.Litmus; import org.apache.calcite.util.TestUtil; @@ -53,7 +54,8 @@ public class SqlToRelConverterTest extends SqlToRelTestBase { /** Sets the SQL statement for a test. */ public final Sql sql(String sql) { - return new Sql(sql, true, true, tester, false); + return new Sql(sql, true, true, tester, false, + SqlToRelConverter.Config.DEFAULT); } protected final void check( @@ -1819,6 +1821,24 @@ public class SqlToRelConverterTest extends SqlToRelTestBase { sql(sql).with(getTesterWithDynamicTable()).ok(); } + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-1321">[CALCITE-1321] + * Configurable IN list size when converting IN clause to join</a>. */ + @Test public void testInToSemiJoin() { + final String sql = "SELECT empno" + + " FROM emp AS e" + + " WHERE cast(e.empno as bigint) in (130, 131, 132, 133, 134)"; + // No conversion to join since less than IN-list size threshold 10 + SqlToRelConverter.Config noConvertConfig = SqlToRelConverter.configBuilder(). + + withInSubqueryThreshold(10).build(); + sql(sql).withConfig(noConvertConfig).convertsTo("${planNotConverted}"); + // Conversion to join since greater than IN-list size threshold 2 + SqlToRelConverter.Config convertConfig = SqlToRelConverter.configBuilder(). + withInSubqueryThreshold(2).build(); + sql(sql).withConfig(convertConfig).convertsTo("${planConverted}"); + } + private Tester getTesterWithDynamicTable() { return tester.withCatalogReaderFactory( new Function<RelDataTypeFactory, Prepare.CatalogReader>() { @@ -1881,15 +1901,17 @@ public class SqlToRelConverterTest extends SqlToRelTestBase { private final boolean expand; private final boolean decorrelate; private final Tester tester; - private boolean trim; + private final boolean trim; + private final SqlToRelConverter.Config config; Sql(String sql, boolean expand, boolean decorrelate, Tester tester, - boolean trim) { + boolean trim, SqlToRelConverter.Config config) { this.sql = sql; this.expand = expand; this.decorrelate = decorrelate; this.tester = tester; this.trim = trim; + this.config = config; } public void ok() { @@ -1899,23 +1921,28 @@ public class SqlToRelConverterTest extends SqlToRelTestBase { public void convertsTo(String plan) { tester.withExpand(expand) .withDecorrelation(decorrelate) + .withConfig(config) .assertConvertsTo(sql, plan, trim); } + public Sql withConfig(SqlToRelConverter.Config config) { + return new Sql(sql, expand, decorrelate, tester, trim, config); + } + public Sql expand(boolean expand) { - return new Sql(sql, expand, decorrelate, tester, trim); + return new Sql(sql, expand, decorrelate, tester, trim, config); } public Sql decorrelate(boolean decorrelate) { - return new Sql(sql, expand, decorrelate, tester, trim); + return new Sql(sql, expand, decorrelate, tester, trim, config); } public Sql with(Tester tester) { - return new Sql(sql, expand, decorrelate, tester, trim); + return new Sql(sql, expand, decorrelate, tester, trim, config); } public Sql trim(boolean trim) { - return new Sql(sql, expand, decorrelate, tester, trim); + return new Sql(sql, expand, decorrelate, tester, trim, config); } } } http://git-wip-us.apache.org/repos/asf/calcite/blob/c933c79f/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java b/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java index 40c6d47..d8e0ff0 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java +++ b/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java @@ -216,6 +216,10 @@ public abstract class SqlToRelTestBase { * @see Prepare#THREAD_EXPAND */ Tester withExpand(boolean expand); + /** Returns a tester that optionally uses a + * {@code SqlToRelConverter.Config}. */ + Tester withConfig(SqlToRelConverter.Config config); + Tester withCatalogReaderFactory( Function<RelDataTypeFactory, Prepare.CatalogReader> factory); @@ -467,6 +471,7 @@ public abstract class SqlToRelTestBase { catalogReaderFactory; private final Function<RelOptCluster, RelOptCluster> clusterFactory; private RelDataTypeFactory typeFactory; + public final SqlToRelConverter.Config config; /** * Creates a TesterImpl. @@ -483,17 +488,29 @@ public abstract class SqlToRelTestBase { Function<RelDataTypeFactory, Prepare.CatalogReader> catalogReaderFactory, Function<RelOptCluster, RelOptCluster> clusterFactory) { + this(diffRepos, enableDecorrelate, enableTrim, enableExpand, + catalogReaderFactory, clusterFactory, SqlToRelConverter.Config.DEFAULT); + } + + protected TesterImpl(DiffRepository diffRepos, boolean enableDecorrelate, + boolean enableTrim, boolean enableExpand, + Function<RelDataTypeFactory, Prepare.CatalogReader> + catalogReaderFactory, + Function<RelOptCluster, RelOptCluster> clusterFactory, + SqlToRelConverter.Config config) { this.diffRepos = diffRepos; this.enableDecorrelate = enableDecorrelate; this.enableTrim = enableTrim; this.enableExpand = enableExpand; this.catalogReaderFactory = catalogReaderFactory; this.clusterFactory = clusterFactory; + this.config = config; } public RelRoot convertSqlToRel(String sql) { Util.pre(sql != null, "sql != null"); final SqlNode sqlQuery; + final SqlToRelConverter.Config localConfig; try { sqlQuery = parseQuery(sql); } catch (Exception e) { @@ -505,13 +522,20 @@ public abstract class SqlToRelTestBase { final SqlValidator validator = createValidator( catalogReader, typeFactory); + if (config == SqlToRelConverter.Config.DEFAULT) { + localConfig = SqlToRelConverter.configBuilder() + .withTrimUnusedFields(true).withExpand(enableExpand).build(); + } else { + localConfig = config; + } + final SqlToRelConverter converter = createSqlToRelConverter( validator, catalogReader, - typeFactory); - converter.setTrimUnusedFields(true); - converter.setExpand(enableExpand); + typeFactory, + localConfig); + final SqlNode validatedQuery = validator.validate(sqlQuery); RelRoot root = converter.convertQuery(validatedQuery, false, true); @@ -523,7 +547,6 @@ public abstract class SqlToRelTestBase { root = root.withRel(converter.decorrelate(sqlQuery, root.rel)); } if (enableTrim) { - converter.setTrimUnusedFields(true); root = root.withRel(converter.trimUnusedFields(true, root.rel)); } return root; @@ -532,7 +555,8 @@ public abstract class SqlToRelTestBase { protected SqlToRelConverter createSqlToRelConverter( final SqlValidator validator, final Prepare.CatalogReader catalogReader, - final RelDataTypeFactory typeFactory) { + final RelDataTypeFactory typeFactory, + final SqlToRelConverter.Config config) { final RexBuilder rexBuilder = new RexBuilder(typeFactory); RelOptCluster cluster = RelOptCluster.create(getPlanner(), rexBuilder); @@ -540,7 +564,7 @@ public abstract class SqlToRelTestBase { cluster = clusterFactory.apply(cluster); } return new SqlToRelConverter(null, validator, catalogReader, cluster, - StandardConvertletTable.INSTANCE); + StandardConvertletTable.INSTANCE, config); } protected final RelDataTypeFactory getTypeFactory() { @@ -671,6 +695,13 @@ public abstract class SqlToRelTestBase { catalogReaderFactory, clusterFactory); } + public TesterImpl withConfig(SqlToRelConverter.Config config) { + return this.config == config + ? this + : new TesterImpl(diffRepos, enableDecorrelate, enableTrim, + enableExpand, catalogReaderFactory, clusterFactory, config); + } + public Tester withTrim(boolean enable) { return this.enableTrim == enable ? this http://git-wip-us.apache.org/repos/asf/calcite/blob/c933c79f/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml ---------------------------------------------------------------------- diff --git a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml index 0241f7e..b3f8a84 100644 --- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml @@ -3456,4 +3456,26 @@ LogicalProject(**=[$0]) ]]> </Resource> </TestCase> + <TestCase name="testInToSemiJoin"> + <Resource name="sql"> + <![CDATA[SELECT * from SALES.NATION order by n_nationkey]]> + </Resource> + <Resource name="planNotConverted"> + <![CDATA[ +LogicalProject(EMPNO=[$0]) + LogicalFilter(condition=[OR(=(CAST($0):BIGINT NOT NULL, 130), =(CAST($0):BIGINT NOT NULL, 131), =(CAST($0):BIGINT NOT NULL, 132), =(CAST($0):BIGINT NOT NULL, 133), =(CAST($0):BIGINT NOT NULL, 134))]) + LogicalTableScan(table=[[CATALOG, SALES, EMP]]) +]]> + </Resource> + <Resource name="planConverted"> + <![CDATA[ +LogicalProject(EMPNO=[$0]) + LogicalJoin(condition=[=($9, $10)], joinType=[inner]) + LogicalProject($f0=[$0], $f1=[$1], $f2=[$2], $f3=[$3], $f4=[$4], $f5=[$5], $f6=[$6], $f7=[$7], $f8=[$8], $f9=[CAST($0):BIGINT NOT NULL]) + LogicalTableScan(table=[[CATALOG, SALES, EMP]]) + LogicalAggregate(group=[{0}]) + LogicalValues(tuples=[[{ 130 }, { 131 }, { 132 }, { 133 }, { 134 }]]) +]]> + </Resource> + </TestCase> </Root> http://git-wip-us.apache.org/repos/asf/calcite/blob/c933c79f/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java ---------------------------------------------------------------------- diff --git a/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java b/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java index dbb52d5..1d35ec5 100644 --- a/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java +++ b/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java @@ -371,11 +371,11 @@ public class CsvTest { final String sql = "SELECT e.name\n" + "FROM emps AS e\n" + "WHERE cast(e.empno as bigint) in "; - checkSql(sql + range(130, SqlToRelConverter.IN_SUBQUERY_THRESHOLD - 5), + checkSql(sql + range(130, SqlToRelConverter.DEFAULT_IN_SUBQUERY_THRESHOLD - 5), "smart", expect("NAME=Alice")); - checkSql(sql + range(130, SqlToRelConverter.IN_SUBQUERY_THRESHOLD), + checkSql(sql + range(130, SqlToRelConverter.DEFAULT_IN_SUBQUERY_THRESHOLD), "smart", expect("NAME=Alice")); - checkSql(sql + range(130, SqlToRelConverter.IN_SUBQUERY_THRESHOLD + 1000), + checkSql(sql + range(130, SqlToRelConverter.DEFAULT_IN_SUBQUERY_THRESHOLD + 1000), "smart", expect("NAME=Alice")); } @@ -386,7 +386,7 @@ public class CsvTest { final String sql = "SELECT e.name\n" + "FROM emps AS e\n" + "WHERE e.empno in " - + range(130, SqlToRelConverter.IN_SUBQUERY_THRESHOLD); + + range(130, SqlToRelConverter.DEFAULT_IN_SUBQUERY_THRESHOLD); checkSql(sql, "smart", expect("NAME=Alice")); }
