You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2016/07/27 07:07:47 UTC

calcite git commit: [CALCITE-1321] When converting IN-list to join, make minimum list size configurable (Gautam Parai)

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 <gp...@maprtech.com>
Authored: Tue Jul 19 14:56:32 2016 -0700
Committer: Julian Hyde <jh...@apache.org>
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"));
   }