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 2015/12/10 05:53:13 UTC
[4/5] calcite git commit: Move code from JdbcImplementor and
JdbcRules to new class SqlImplementor
Move code from JdbcImplementor and JdbcRules to new class SqlImplementor
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/980d9f81
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/980d9f81
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/980d9f81
Branch: refs/heads/master
Commit: 980d9f811af4e0ff32fc1487e469a51585db48a6
Parents: eedd3dc
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Dec 7 17:47:31 2015 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Dec 9 13:40:24 2015 -0800
----------------------------------------------------------------------
.../calcite/adapter/jdbc/JdbcImplementor.java | 549 +-----------
.../apache/calcite/adapter/jdbc/JdbcRules.java | 301 +------
.../calcite/rel/rel2sql/RelToSqlConverter.java | 8 +-
.../calcite/rel/rel2sql/SqlImplementor.java | 826 +++++++++++++++++++
4 files changed, 858 insertions(+), 826 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/980d9f81/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcImplementor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcImplementor.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcImplementor.java
index 02f9e07..f66c5f1 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcImplementor.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcImplementor.java
@@ -17,566 +17,23 @@
package org.apache.calcite.adapter.jdbc;
import org.apache.calcite.adapter.java.JavaTypeFactory;
-import org.apache.calcite.linq4j.tree.Expressions;
-import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.core.AggregateCall;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeField;
-import org.apache.calcite.rex.RexCall;
-import org.apache.calcite.rex.RexInputRef;
-import org.apache.calcite.rex.RexLiteral;
-import org.apache.calcite.rex.RexLocalRef;
-import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.rex.RexProgram;
-import org.apache.calcite.sql.SqlBinaryOperator;
-import org.apache.calcite.sql.SqlCall;
-import org.apache.calcite.sql.SqlDataTypeSpec;
+import org.apache.calcite.rel.rel2sql.SqlImplementor;
import org.apache.calcite.sql.SqlDialect;
-import org.apache.calcite.sql.SqlFunction;
-import org.apache.calcite.sql.SqlFunctionCategory;
-import org.apache.calcite.sql.SqlIdentifier;
-import org.apache.calcite.sql.SqlJoin;
-import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlLiteral;
-import org.apache.calcite.sql.SqlNode;
-import org.apache.calcite.sql.SqlNodeList;
-import org.apache.calcite.sql.SqlOperator;
-import org.apache.calcite.sql.SqlSelect;
-import org.apache.calcite.sql.SqlSelectKeyword;
-import org.apache.calcite.sql.SqlSetOperator;
-import org.apache.calcite.sql.fun.SqlCase;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
-import org.apache.calcite.sql.fun.SqlSumEmptyIsZeroAggFunction;
-import org.apache.calcite.sql.parser.SqlParserPos;
-import org.apache.calcite.sql.type.BasicSqlType;
-import org.apache.calcite.sql.type.ReturnTypes;
-import org.apache.calcite.sql.type.SqlTypeName;
-import org.apache.calcite.sql.validate.SqlValidatorUtil;
-import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
-import com.google.common.collect.ImmutableList;
-
-import java.util.AbstractList;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
/**
* State for generating a SQL statement.
*/
-public class JdbcImplementor {
- public static final SqlParserPos POS = SqlParserPos.ZERO;
-
- /** Oracle's {@code SUBSTR} function.
- * Oracle does not support {@link SqlStdOperatorTable#SUBSTRING}. */
- public static final SqlFunction ORACLE_SUBSTR =
- new SqlFunction("SUBSTR", SqlKind.OTHER_FUNCTION,
- ReturnTypes.ARG0_NULLABLE_VARYING, null, null,
- SqlFunctionCategory.STRING);
-
- final SqlDialect dialect;
- private final Set<String> aliasSet = new LinkedHashSet<>();
-
+public class JdbcImplementor extends SqlImplementor {
public JdbcImplementor(SqlDialect dialect, JavaTypeFactory typeFactory) {
- this.dialect = dialect;
+ super(dialect);
Util.discard(typeFactory);
}
- /** Creates a result based on a single relational expression. */
- public Result result(SqlNode node, Collection<Clause> clauses, RelNode rel) {
- final String alias2 = SqlValidatorUtil.getAlias(node, -1);
- final String alias3 = alias2 != null ? alias2 : "t";
- final String alias4 =
- SqlValidatorUtil.uniquify(
- alias3, aliasSet, SqlValidatorUtil.EXPR_SUGGESTER);
- final String alias5 = alias2 == null || !alias2.equals(alias4) ? alias4
- : null;
- return new Result(node, clauses, alias5,
- Collections.singletonList(Pair.of(alias4, rel.getRowType())));
- }
-
- /** Creates a result based on a join. (Each join could contain one or more
- * relational expressions.) */
- public Result result(SqlNode join, Result leftResult, Result rightResult) {
- final List<Pair<String, RelDataType>> list = new ArrayList<>();
- list.addAll(leftResult.aliases);
- list.addAll(rightResult.aliases);
- return new Result(join, Expressions.list(Clause.FROM), null, list);
- }
-
- /** Wraps a node in a SELECT statement that has no clauses:
- * "SELECT ... FROM (node)". */
- SqlSelect wrapSelect(SqlNode node) {
- assert node instanceof SqlJoin
- || node instanceof SqlIdentifier
- || node instanceof SqlCall
- && (((SqlCall) node).getOperator() instanceof SqlSetOperator
- || ((SqlCall) node).getOperator() == SqlStdOperatorTable.AS)
- : node;
- return new SqlSelect(POS, SqlNodeList.EMPTY, null, node, null, null, null,
- SqlNodeList.EMPTY, null, null, null);
- }
-
public Result visitChild(int i, RelNode e) {
return ((JdbcRel) e).implement(this);
}
-
- /** Context for translating a {@link RexNode} expression (within a
- * {@link RelNode}) into a {@link SqlNode} expression (within a SQL parse
- * tree). */
- public abstract class Context {
- final int fieldCount;
-
- protected Context(int fieldCount) {
- this.fieldCount = fieldCount;
- }
-
- public abstract SqlNode field(int ordinal);
-
- /** Converts an expression from {@link RexNode} to {@link SqlNode}
- * format. */
- SqlNode toSql(RexProgram program, RexNode rex) {
- switch (rex.getKind()) {
- case LOCAL_REF:
- final int index = ((RexLocalRef) rex).getIndex();
- return toSql(program, program.getExprList().get(index));
-
- case INPUT_REF:
- return field(((RexInputRef) rex).getIndex());
-
- case LITERAL:
- final RexLiteral literal = (RexLiteral) rex;
- if (literal.getTypeName() == SqlTypeName.SYMBOL) {
- final SqlLiteral.SqlSymbol symbol =
- (SqlLiteral.SqlSymbol) literal.getValue();
- return SqlLiteral.createSymbol(symbol, POS);
- }
- switch (literal.getTypeName().getFamily()) {
- case CHARACTER:
- return SqlLiteral.createCharString((String) literal.getValue2(), POS);
- case NUMERIC:
- case EXACT_NUMERIC:
- return SqlLiteral.createExactNumeric(literal.getValue().toString(),
- POS);
- case APPROXIMATE_NUMERIC:
- return SqlLiteral.createApproxNumeric(
- literal.getValue().toString(), POS);
- case BOOLEAN:
- return SqlLiteral.createBoolean((Boolean) literal.getValue(), POS);
- case DATE:
- return SqlLiteral.createDate((Calendar) literal.getValue(), POS);
- case TIME:
- return SqlLiteral.createTime((Calendar) literal.getValue(),
- literal.getType().getPrecision(), POS);
- case TIMESTAMP:
- return SqlLiteral.createTimestamp((Calendar) literal.getValue(),
- literal.getType().getPrecision(), POS);
- case ANY:
- case NULL:
- switch (literal.getTypeName()) {
- case NULL:
- return SqlLiteral.createNull(POS);
- // fall through
- }
- default:
- throw new AssertionError(literal + ": " + literal.getTypeName());
- }
- case CASE:
- final RexCall caseCall = (RexCall) rex;
- final List<SqlNode> caseNodeList =
- toSql(program, caseCall.getOperands());
- final SqlNode valueNode;
- final List<SqlNode> whenList = Expressions.list();
- final List<SqlNode> thenList = Expressions.list();
- final SqlNode elseNode;
- if (caseNodeList.size() % 2 == 0) {
- // switched:
- // "case x when v1 then t1 when v2 then t2 ... else e end"
- valueNode = caseNodeList.get(0);
- for (int i = 1; i < caseNodeList.size() - 1; i += 2) {
- whenList.add(caseNodeList.get(i));
- thenList.add(caseNodeList.get(i + 1));
- }
- } else {
- // other: "case when w1 then t1 when w2 then t2 ... else e end"
- valueNode = null;
- for (int i = 0; i < caseNodeList.size() - 1; i += 2) {
- whenList.add(caseNodeList.get(i));
- thenList.add(caseNodeList.get(i + 1));
- }
- }
- elseNode = caseNodeList.get(caseNodeList.size() - 1);
- return new SqlCase(POS, valueNode, new SqlNodeList(whenList, POS),
- new SqlNodeList(thenList, POS), elseNode);
-
- default:
- final RexCall call = (RexCall) rex;
- final SqlOperator op = call.getOperator();
- final List<SqlNode> nodeList = toSql(program, call.getOperands());
- switch (rex.getKind()) {
- case CAST:
- nodeList.add(toSql(call.getType()));
- }
- if (op instanceof SqlBinaryOperator && nodeList.size() > 2) {
- // In RexNode trees, OR and AND have any number of children;
- // SqlCall requires exactly 2. So, convert to a left-deep binary tree.
- return createLeftCall(op, nodeList);
- }
- if (op == SqlStdOperatorTable.SUBSTRING) {
- switch (dialect.getDatabaseProduct()) {
- case ORACLE:
- return ORACLE_SUBSTR.createCall(new SqlNodeList(nodeList, POS));
- }
- }
- return op.createCall(new SqlNodeList(nodeList, POS));
- }
- }
-
- private SqlNode createLeftCall(SqlOperator op, List<SqlNode> nodeList) {
- if (nodeList.size() == 2) {
- return op.createCall(new SqlNodeList(nodeList, POS));
- }
- final List<SqlNode> butLast = Util.skipLast(nodeList);
- final SqlNode last = nodeList.get(nodeList.size() - 1);
- final SqlNode call = createLeftCall(op, butLast);
- return op.createCall(new SqlNodeList(ImmutableList.of(call, last), POS));
- }
-
- private SqlNode toSql(RelDataType type) {
- switch (dialect.getDatabaseProduct()) {
- case MYSQL:
- switch (type.getSqlTypeName()) {
- case VARCHAR:
- // MySQL doesn't have a VARCHAR type, only CHAR.
- return new SqlDataTypeSpec(new SqlIdentifier("CHAR", POS),
- type.getPrecision(), -1, null, null, POS);
- case INTEGER:
- return new SqlDataTypeSpec(new SqlIdentifier("_UNSIGNED", POS),
- type.getPrecision(), -1, null, null, POS);
- }
- break;
- }
- if (type instanceof BasicSqlType) {
- return new SqlDataTypeSpec(
- new SqlIdentifier(type.getSqlTypeName().name(), POS),
- type.getPrecision(),
- type.getScale(),
- type.getCharset() != null
- && dialect.supportsCharSet()
- ? type.getCharset().name()
- : null,
- null,
- POS);
- }
- throw new AssertionError(type); // TODO: implement
- }
-
- private List<SqlNode> toSql(RexProgram program, List<RexNode> operandList) {
- final List<SqlNode> list = new ArrayList<>();
- for (RexNode rex : operandList) {
- list.add(toSql(program, rex));
- }
- return list;
- }
-
- public List<SqlNode> fieldList() {
- return new AbstractList<SqlNode>() {
- public SqlNode get(int index) {
- return field(index);
- }
-
- public int size() {
- return fieldCount;
- }
- };
- }
-
- /** Converts a call to an aggregate function to an expression. */
- public SqlNode toSql(AggregateCall aggCall) {
- SqlOperator op = aggCall.getAggregation();
- if (op instanceof SqlSumEmptyIsZeroAggFunction) {
- op = SqlStdOperatorTable.SUM;
- }
- final List<SqlNode> operands = Expressions.list();
- for (int arg : aggCall.getArgList()) {
- operands.add(field(arg));
- }
- return op.createCall(
- aggCall.isDistinct() ? SqlSelectKeyword.DISTINCT.symbol(POS) : null,
- POS, operands.toArray(new SqlNode[operands.size()]));
- }
-
- /** Converts a collation to an ORDER BY item. */
- public SqlNode toSql(RelFieldCollation collation) {
- SqlNode node = field(collation.getFieldIndex());
- switch (collation.getDirection()) {
- case DESCENDING:
- case STRICTLY_DESCENDING:
- node = SqlStdOperatorTable.DESC.createCall(POS, node);
- }
- switch (collation.nullDirection) {
- case FIRST:
- node = SqlStdOperatorTable.NULLS_FIRST.createCall(POS, node);
- break;
- case LAST:
- node = SqlStdOperatorTable.NULLS_LAST.createCall(POS, node);
- break;
- }
- return node;
- }
-
- public JdbcImplementor implementor() {
- return JdbcImplementor.this;
- }
- }
-
- private static int computeFieldCount(
- List<Pair<String, RelDataType>> aliases) {
- int x = 0;
- for (Pair<String, RelDataType> alias : aliases) {
- x += alias.right.getFieldCount();
- }
- return x;
- }
-
- Context aliasContext(List<Pair<String, RelDataType>> aliases,
- boolean qualified) {
- return new AliasContext(aliases, qualified);
- }
-
- Context joinContext(Context leftContext, Context rightContext) {
- return new JoinContext(leftContext, rightContext);
- }
-
- /** Implementation of Context that precedes field references with their
- * "table alias" based on the current sub-query's FROM clause. */
- public class AliasContext extends Context {
- private final boolean qualified;
- private final List<Pair<String, RelDataType>> aliases;
-
- /** Creates an AliasContext; use {@link #aliasContext(List, boolean)}. */
- private AliasContext(List<Pair<String, RelDataType>> aliases,
- boolean qualified) {
- super(computeFieldCount(aliases));
- this.aliases = aliases;
- this.qualified = qualified;
- }
-
- public SqlNode field(int ordinal) {
- for (Pair<String, RelDataType> alias : aliases) {
- final List<RelDataTypeField> fields = alias.right.getFieldList();
- if (ordinal < fields.size()) {
- RelDataTypeField field = fields.get(ordinal);
- return new SqlIdentifier(!qualified
- ? ImmutableList.of(field.getName())
- : ImmutableList.of(alias.left, field.getName()),
- POS);
- }
- ordinal -= fields.size();
- }
- throw new AssertionError(
- "field ordinal " + ordinal + " out of range " + aliases);
- }
- }
-
- /** Context for translating ON clause of a JOIN from {@link RexNode} to
- * {@link SqlNode}. */
- class JoinContext extends Context {
- private final JdbcImplementor.Context leftContext;
- private final JdbcImplementor.Context rightContext;
-
- /** Creates a JoinContext; use {@link #joinContext(Context, Context)}. */
- private JoinContext(Context leftContext, Context rightContext) {
- super(leftContext.fieldCount + rightContext.fieldCount);
- this.leftContext = leftContext;
- this.rightContext = rightContext;
- }
-
- public SqlNode field(int ordinal) {
- if (ordinal < leftContext.fieldCount) {
- return leftContext.field(ordinal);
- } else {
- return rightContext.field(ordinal - leftContext.fieldCount);
- }
- }
- }
-
- /** Result of implementing a node. */
- public class Result {
- final SqlNode node;
- private final String neededAlias;
- private final List<Pair<String, RelDataType>> aliases;
- final Expressions.FluentList<Clause> clauses;
-
- private Result(SqlNode node, Collection<Clause> clauses, String neededAlias,
- List<Pair<String, RelDataType>> aliases) {
- this.node = node;
- this.neededAlias = neededAlias;
- this.aliases = aliases;
- this.clauses = Expressions.list(clauses);
- }
-
- /** Once you have a Result of implementing a child relational expression,
- * call this method to create a Builder to implement the current relational
- * expression by adding additional clauses to the SQL query.
- *
- * <p>You need to declare which clauses you intend to add. If the clauses
- * are "later", you can add to the same query. For example, "GROUP BY" comes
- * after "WHERE". But if they are the same or earlier, this method will
- * start a new SELECT that wraps the previous result.</p>
- *
- * <p>When you have called
- * {@link Builder#setSelect(org.apache.calcite.sql.SqlNodeList)},
- * {@link Builder#setWhere(org.apache.calcite.sql.SqlNode)} etc. call
- * {@link Builder#result(org.apache.calcite.sql.SqlNode, java.util.Collection, org.apache.calcite.rel.RelNode)}
- * to fix the new query.</p>
- *
- * @param rel Relational expression being implemented
- * @param clauses Clauses that will be generated to implement current
- * relational expression
- * @return A builder
- */
- public Builder builder(JdbcRel rel, Clause... clauses) {
- final Clause maxClause = maxClause();
- boolean needNew = false;
- for (Clause clause : clauses) {
- if (maxClause.ordinal() >= clause.ordinal()) {
- needNew = true;
- }
- }
- SqlSelect select;
- Expressions.FluentList<Clause> clauseList = Expressions.list();
- if (needNew) {
- select = subSelect();
- } else {
- select = asSelect();
- clauseList.addAll(this.clauses);
- }
- clauseList.appendAll(clauses);
- Context newContext;
- final SqlNodeList selectList = select.getSelectList();
- if (selectList != null) {
- newContext = new Context(selectList.size()) {
- @Override public SqlNode field(int ordinal) {
- final SqlNode selectItem = selectList.get(ordinal);
- switch (selectItem.getKind()) {
- case AS:
- return ((SqlCall) selectItem).operand(0);
- }
- return selectItem;
- }
- };
- } else {
- newContext = aliasContext(aliases, aliases.size() > 1);
- }
- return new Builder(rel, clauseList, select, newContext);
- }
-
- // make private?
- public Clause maxClause() {
- Clause maxClause = null;
- for (Clause clause : clauses) {
- if (maxClause == null || clause.ordinal() > maxClause.ordinal()) {
- maxClause = clause;
- }
- }
- assert maxClause != null;
- return maxClause;
- }
-
- /** Returns a node that can be included in the FROM clause or a JOIN. It has
- * an alias that is unique within the query. The alias is implicit if it
- * can be derived using the usual rules (For example, "SELECT * FROM emp" is
- * equivalent to "SELECT * FROM emp AS emp".) */
- public SqlNode asFrom() {
- if (neededAlias != null) {
- return SqlStdOperatorTable.AS.createCall(POS, node,
- new SqlIdentifier(neededAlias, POS));
- }
- return node;
- }
-
- public SqlSelect subSelect() {
- return wrapSelect(asFrom());
- }
-
- /** Converts a non-query node into a SELECT node. Set operators (UNION,
- * INTERSECT, EXCEPT) remain as is. */
- SqlSelect asSelect() {
- if (node instanceof SqlSelect) {
- return (SqlSelect) node;
- }
- return wrapSelect(node);
- }
-
- /** Converts a non-query node into a SELECT node. Set operators (UNION,
- * INTERSECT, EXCEPT) remain as is. */
- public SqlNode asQuery() {
- if (node instanceof SqlCall
- && ((SqlCall) node).getOperator() instanceof SqlSetOperator) {
- return node;
- }
- return asSelect();
- }
-
- /** Returns a context that always qualifies identifiers. Useful if the
- * Context deals with just one arm of a join, yet we wish to generate
- * a join condition that qualifies column names to disambiguate them. */
- public Context qualifiedContext() {
- return aliasContext(aliases, true);
- }
- }
-
- /** Builder. */
- public class Builder {
- private final JdbcRel rel;
- private final List<Clause> clauses;
- private final SqlSelect select;
- public final Context context;
-
- public Builder(JdbcRel rel, List<Clause> clauses, SqlSelect select,
- Context context) {
- this.rel = rel;
- this.clauses = clauses;
- this.select = select;
- this.context = context;
- }
-
- public void setSelect(SqlNodeList nodeList) {
- select.setSelectList(nodeList);
- }
-
- public void setWhere(SqlNode node) {
- assert clauses.contains(Clause.WHERE);
- select.setWhere(node);
- }
-
- public void setGroupBy(SqlNodeList nodeList) {
- assert clauses.contains(Clause.GROUP_BY);
- select.setGroupBy(nodeList);
- }
-
- public void setOrderBy(SqlNodeList nodeList) {
- assert clauses.contains(Clause.ORDER_BY);
- select.setOrderBy(nodeList);
- }
-
- public Result result() {
- return JdbcImplementor.this.result(select, clauses, rel);
- }
- }
-
- /** Clauses in a SQL query. Ordered by evaluation order.
- * SELECT is set only when there is a NON-TRIVIAL SELECT clause. */
- enum Clause {
- FROM, WHERE, GROUP_BY, HAVING, SELECT, SET_OP, ORDER_BY
- }
}
// End JdbcImplementor.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/980d9f81/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
index cd91515..aaec1d9 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
@@ -16,7 +16,6 @@
*/
package org.apache.calcite.adapter.jdbc;
-import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.linq4j.Queryable;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.Expressions;
@@ -59,6 +58,7 @@ import org.apache.calcite.rel.logical.LogicalTableModify;
import org.apache.calcite.rel.logical.LogicalUnion;
import org.apache.calcite.rel.logical.LogicalValues;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.rel2sql.SqlImplementor;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexInputRef;
@@ -69,30 +69,18 @@ import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexProgram;
import org.apache.calcite.schema.ModifiableTable;
import org.apache.calcite.sql.JoinConditionType;
-import org.apache.calcite.sql.JoinType;
import org.apache.calcite.sql.SqlAggFunction;
-import org.apache.calcite.sql.SqlBasicCall;
-import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlDialect;
-import org.apache.calcite.sql.SqlFunction;
-import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlJoin;
-import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlLiteral;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
-import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlSelect;
import org.apache.calcite.sql.SqlSetOperator;
-import org.apache.calcite.sql.fun.SqlCase;
import org.apache.calcite.sql.fun.SqlSingleValueAggFunction;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
-import org.apache.calcite.sql.type.InferTypes;
-import org.apache.calcite.sql.type.OperandTypes;
-import org.apache.calcite.sql.type.ReturnTypes;
-import org.apache.calcite.sql.validate.SqlValidatorUtil;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
@@ -150,53 +138,6 @@ public class JdbcRules {
MYSQL_AGG_FUNCS = builder.build();
}
- private static void addSelect(
- List<SqlNode> selectList, SqlNode node, RelDataType rowType) {
- String name = rowType.getFieldNames().get(selectList.size());
- String alias = SqlValidatorUtil.getAlias(node, -1);
- if (alias == null || !alias.equals(name)) {
- node = SqlStdOperatorTable.AS.createCall(
- POS, node, new SqlIdentifier(name, POS));
- }
- selectList.add(node);
- }
-
- private static JdbcImplementor.Result setOpToSql(JdbcImplementor implementor,
- SqlSetOperator operator, JdbcRel rel) {
- List<SqlNode> list = Expressions.list();
- for (Ord<RelNode> input : Ord.zip(rel.getInputs())) {
- final JdbcImplementor.Result result =
- implementor.visitChild(input.i, input.e);
- list.add(result.asSelect());
- }
- final SqlCall node = operator.createCall(new SqlNodeList(list, POS));
- final List<JdbcImplementor.Clause> clauses =
- Expressions.list(JdbcImplementor.Clause.SET_OP);
- return implementor.result(node, clauses, rel);
- }
-
- private static boolean isStar(List<RexNode> exps, RelDataType inputRowType) {
- int i = 0;
- for (RexNode ref : exps) {
- if (!(ref instanceof RexInputRef)) {
- return false;
- } else if (((RexInputRef) ref).getIndex() != i++) {
- return false;
- }
- }
- return i == inputRowType.getFieldCount();
- }
-
- private static boolean isStar(RexProgram program) {
- int i = 0;
- for (RexLocalRef ref : program.getProjectList()) {
- if (ref.getIndex() != i++) {
- return false;
- }
- }
- return i == program.getInputRowType().getFieldCount();
- }
-
/** Abstract base class for rule that converts to JDBC. */
abstract static class JdbcConverterRule extends ConverterRule {
protected final JdbcConvention out;
@@ -247,7 +188,7 @@ public class JdbcRules {
* Returns whether a condition is supported by {@link JdbcJoin}.
*
* <p>Corresponds to the capabilities of
- * {@link JdbcJoin#convertConditionToSqlNode}.
+ * {@link SqlImplementor#convertConditionToSqlNode}.
*
* @param node Condition
* @return Whether condition is supported
@@ -334,7 +275,7 @@ public class JdbcRules {
final JdbcImplementor.Context leftContext = leftResult.qualifiedContext();
final JdbcImplementor.Context rightContext =
rightResult.qualifiedContext();
- SqlNode sqlCondition = convertConditionToSqlNode(condition,
+ SqlNode sqlCondition = SqlImplementor.convertConditionToSqlNode(condition,
leftContext,
rightContext,
left.getRowType().getFieldCount());
@@ -342,117 +283,13 @@ public class JdbcRules {
new SqlJoin(POS,
leftResult.asFrom(),
SqlLiteral.createBoolean(false, POS),
- joinType(joinType).symbol(POS),
+ SqlImplementor.joinType(joinType).symbol(POS),
rightResult.asFrom(),
JoinConditionType.ON.symbol(POS),
sqlCondition);
return implementor.result(join, leftResult, rightResult);
}
- /**
- * Convert {@link RexNode} condition into {@link SqlNode}
- *
- * @param node condition Node
- * @param leftContext LeftContext
- * @param rightContext RightContext
- * @param leftFieldCount Number of field on left result
- * @return SqlJoin which represent the condition
- */
- private SqlNode convertConditionToSqlNode(RexNode node,
- JdbcImplementor.Context leftContext,
- JdbcImplementor.Context rightContext, int leftFieldCount) {
- if (!(node instanceof RexCall)) {
- throw new AssertionError(node);
- }
- final List<RexNode> operands;
- final SqlOperator op;
- switch (node.getKind()) {
- case AND:
- case OR:
- operands = ((RexCall) node).getOperands();
- op = ((RexCall) node).getOperator();
- SqlNode sqlCondition = null;
- for (RexNode operand : operands) {
- SqlNode x = convertConditionToSqlNode(operand, leftContext,
- rightContext, leftFieldCount);
- if (sqlCondition == null) {
- sqlCondition = x;
- } else {
- sqlCondition = op.createCall(POS, sqlCondition, x);
- }
- }
- return sqlCondition;
-
- case EQUALS:
- case IS_NOT_DISTINCT_FROM:
- case NOT_EQUALS:
- case GREATER_THAN:
- case GREATER_THAN_OR_EQUAL:
- case LESS_THAN:
- case LESS_THAN_OR_EQUAL:
- operands = ((RexCall) node).getOperands();
- op = ((RexCall) node).getOperator();
- if (operands.size() == 2
- && operands.get(0) instanceof RexInputRef
- && operands.get(1) instanceof RexInputRef) {
- final RexInputRef op0 = (RexInputRef) operands.get(0);
- final RexInputRef op1 = (RexInputRef) operands.get(1);
-
- if (op0.getIndex() < leftFieldCount
- && op1.getIndex() >= leftFieldCount) {
- // Arguments were of form 'op0 = op1'
- return op.createCall(POS,
- leftContext.field(op0.getIndex()),
- rightContext.field(op1.getIndex() - leftFieldCount));
- }
- if (op1.getIndex() < leftFieldCount
- && op0.getIndex() >= leftFieldCount) {
- // Arguments were of form 'op1 = op0'
- return reverseOperatorDirection(op).createCall(POS,
- leftContext.field(op1.getIndex()),
- rightContext.field(op0.getIndex() - leftFieldCount));
- }
- }
- final JdbcImplementor.Context joinContext =
- leftContext.implementor().joinContext(leftContext, rightContext);
- return joinContext.toSql(null, node);
- }
- throw new AssertionError(node);
- }
-
- private static SqlOperator reverseOperatorDirection(SqlOperator op) {
- switch (op.kind) {
- case GREATER_THAN:
- return SqlStdOperatorTable.LESS_THAN;
- case GREATER_THAN_OR_EQUAL:
- return SqlStdOperatorTable.LESS_THAN_OR_EQUAL;
- case LESS_THAN:
- return SqlStdOperatorTable.GREATER_THAN;
- case LESS_THAN_OR_EQUAL:
- return SqlStdOperatorTable.GREATER_THAN_OR_EQUAL;
- case EQUALS:
- case IS_NOT_DISTINCT_FROM:
- case NOT_EQUALS:
- return op;
- default:
- throw new AssertionError(op);
- }
- }
-
- private static JoinType joinType(JoinRelType joinType) {
- switch (joinType) {
- case LEFT:
- return JoinType.LEFT;
- case RIGHT:
- return JoinType.RIGHT;
- case INNER:
- return JoinType.INNER;
- case FULL:
- return JoinType.FULL;
- default:
- throw new AssertionError(joinType);
- }
- }
}
/**
@@ -529,11 +366,11 @@ public class JdbcRules {
? x.builder(this, JdbcImplementor.Clause.FROM,
JdbcImplementor.Clause.WHERE)
: x.builder(this, JdbcImplementor.Clause.FROM);
- if (!isStar(program)) {
+ if (!SqlImplementor.isStar(program)) {
final List<SqlNode> selectList = new ArrayList<>();
for (RexLocalRef ref : program.getProjectList()) {
SqlNode sqlExpr = builder.context.toSql(program, ref);
- addSelect(selectList, sqlExpr, getRowType());
+ implementor.addSelect(selectList, sqlExpr, getRowType());
}
builder.setSelect(new SqlNodeList(selectList, POS));
}
@@ -602,7 +439,7 @@ public class JdbcRules {
public JdbcImplementor.Result implement(JdbcImplementor implementor) {
JdbcImplementor.Result x = implementor.visitChild(0, getInput());
- if (isStar(exps, getInput().getRowType())) {
+ if (SqlImplementor.isStar(exps, getInput().getRowType())) {
return x;
}
final JdbcImplementor.Builder builder =
@@ -610,7 +447,7 @@ public class JdbcRules {
final List<SqlNode> selectList = new ArrayList<>();
for (RexNode ref : exps) {
SqlNode sqlExpr = builder.context.toSql(null, ref);
- addSelect(selectList, sqlExpr, getRowType());
+ implementor.addSelect(selectList, sqlExpr, getRowType());
}
builder.setSelect(new SqlNodeList(selectList, POS));
return builder.result();
@@ -693,6 +530,18 @@ public class JdbcRules {
}
}
+ /** Returns whether this JDBC data source can implement a given aggregate
+ * function. */
+ private static boolean canImplement(SqlAggFunction aggregation,
+ SqlDialect sqlDialect) {
+ switch (sqlDialect.getDatabaseProduct()) {
+ case MYSQL:
+ return MYSQL_AGG_FUNCS.contains(aggregation);
+ default:
+ return AGG_FUNCS.contains(aggregation);
+ }
+ }
+
/** Aggregate operator implemented in JDBC convention. */
public static class JdbcAggregate extends Aggregate implements JdbcRel {
public JdbcAggregate(
@@ -717,18 +566,6 @@ public class JdbcRules {
}
}
- /** Returns whether this JDBC data source can implement a given aggregate
- * function. */
- private boolean canImplement(SqlAggFunction aggregation,
- SqlDialect sqlDialect) {
- switch (sqlDialect.getDatabaseProduct()) {
- case MYSQL:
- return MYSQL_AGG_FUNCS.contains(aggregation);
- default:
- return AGG_FUNCS.contains(aggregation);
- }
- }
-
@Override public JdbcAggregate copy(RelTraitSet traitSet, RelNode input,
boolean indicator, ImmutableBitSet groupSet,
List<ImmutableBitSet> groupSets, List<AggregateCall> aggCalls) {
@@ -751,16 +588,16 @@ public class JdbcRules {
final List<SqlNode> selectList = new ArrayList<>();
for (int group : groupSet) {
final SqlNode field = builder.context.field(group);
- addSelect(selectList, field, getRowType());
+ implementor.addSelect(selectList, field, getRowType());
groupByList.add(field);
}
for (AggregateCall aggCall : aggCalls) {
SqlNode aggCallSqlNode = builder.context.toSql(aggCall);
if (aggCall.getAggregation() instanceof SqlSingleValueAggFunction) {
aggCallSqlNode =
- rewriteSingleValueExpr(aggCallSqlNode, implementor.dialect);
+ SqlImplementor.rewriteSingleValueExpr(aggCallSqlNode, implementor.dialect);
}
- addSelect(selectList, aggCallSqlNode, getRowType());
+ implementor.addSelect(selectList, aggCallSqlNode, getRowType());
}
builder.setSelect(new SqlNodeList(selectList, POS));
if (!groupByList.isEmpty() || aggCalls.isEmpty()) {
@@ -771,84 +608,6 @@ public class JdbcRules {
return builder.result();
}
- /** Rewrite SINGLE_VALUE into expression based on database variants
- * E.g. HSQLDB, MYSQL, ORACLE, etc
- */
- private SqlNode rewriteSingleValueExpr(SqlNode aggCall,
- SqlDialect sqlDialect) {
- final SqlNode operand = ((SqlBasicCall) aggCall).operand(0);
- final SqlNode caseOperand;
- final SqlNode elseExpr;
- final SqlNode countCall =
- SqlStdOperatorTable.COUNT.createCall(POS, operand);
-
- final SqlLiteral nullLiteral = SqlLiteral.createNull(POS);
- final SqlNode wrappedOperand;
- switch (sqlDialect.getDatabaseProduct()) {
- case MYSQL:
- case HSQLDB:
- // For MySQL, generate
- // CASE COUNT(*)
- // WHEN 0 THEN NULL
- // WHEN 1 THEN <result>
- // ELSE (SELECT NULL UNION ALL SELECT NULL)
- // END
- //
- // For hsqldb, generate
- // CASE COUNT(*)
- // WHEN 0 THEN NULL
- // WHEN 1 THEN MIN(<result>)
- // ELSE (VALUES 1 UNION ALL VALUES 1)
- // END
- caseOperand = countCall;
-
- final SqlNodeList selectList = new SqlNodeList(POS);
- selectList.add(nullLiteral);
- final SqlNode unionOperand;
- switch (sqlDialect.getDatabaseProduct()) {
- case MYSQL:
- wrappedOperand = operand;
- unionOperand = new SqlSelect(POS, SqlNodeList.EMPTY, selectList,
- null, null, null, null, SqlNodeList.EMPTY, null, null, null);
- break;
- default:
- wrappedOperand = SqlStdOperatorTable.MIN.createCall(POS, operand);
- unionOperand = SqlStdOperatorTable.VALUES.createCall(POS,
- SqlLiteral.createApproxNumeric("0", POS));
- }
-
- SqlCall unionAll = SqlStdOperatorTable.UNION_ALL
- .createCall(POS, unionOperand, unionOperand);
-
- final SqlNodeList subQuery = new SqlNodeList(POS);
- subQuery.add(unionAll);
-
- final SqlNodeList selectList2 = new SqlNodeList(POS);
- selectList2.add(nullLiteral);
- elseExpr = SqlStdOperatorTable.SCALAR_QUERY.createCall(POS, subQuery);
- break;
-
- default:
- LOGGER.fine("SINGLE_VALUE rewrite not supported for "
- + sqlDialect.getDatabaseProduct());
- return aggCall;
- }
-
- final SqlNodeList whenList = new SqlNodeList(POS);
- whenList.add(SqlLiteral.createExactNumeric("0", POS));
- whenList.add(SqlLiteral.createExactNumeric("1", POS));
-
- final SqlNodeList thenList = new SqlNodeList(POS);
- thenList.add(nullLiteral);
- thenList.add(wrappedOperand);
-
- SqlNode caseExpr =
- new SqlCase(POS, caseOperand, whenList, thenList, elseExpr);
-
- LOGGER.fine("SINGLE_VALUE rewritten into [" + caseExpr + "]");
-
- return caseExpr;
- }
}
/**
@@ -905,7 +664,7 @@ public class JdbcRules {
&& implementor.dialect.getDatabaseProduct()
== SqlDialect.DatabaseProduct.MYSQL) {
orderByList.add(
- ISNULL_FUNCTION.createCall(POS,
+ SqlImplementor.ISNULL_FUNCTION.createCall(POS,
builder.context.field(fieldCollation.getFieldIndex())));
fieldCollation = new RelFieldCollation(fieldCollation.getFieldIndex(),
fieldCollation.getDirection());
@@ -917,12 +676,6 @@ public class JdbcRules {
}
}
- /** MySQL specific function. */
- private static final SqlFunction ISNULL_FUNCTION =
- new SqlFunction("ISNULL", SqlKind.OTHER_FUNCTION,
- ReturnTypes.BOOLEAN, InferTypes.FIRST_KNOWN,
- OperandTypes.ANY, SqlFunctionCategory.SYSTEM);
-
/**
* Rule to convert an {@link org.apache.calcite.rel.logical.LogicalUnion} to a
* {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcUnion}.
@@ -964,7 +717,7 @@ public class JdbcRules {
final SqlSetOperator operator = all
? SqlStdOperatorTable.UNION_ALL
: SqlStdOperatorTable.UNION;
- return setOpToSql(implementor, operator, this);
+ return SqlImplementor.setOpToSql(implementor, operator, this);
}
}
@@ -1008,7 +761,7 @@ public class JdbcRules {
}
public JdbcImplementor.Result implement(JdbcImplementor implementor) {
- return setOpToSql(implementor,
+ return SqlImplementor.setOpToSql(implementor,
all
? SqlStdOperatorTable.INTERSECT_ALL
: SqlStdOperatorTable.INTERSECT,
@@ -1051,7 +804,7 @@ public class JdbcRules {
}
public JdbcImplementor.Result implement(JdbcImplementor implementor) {
- return setOpToSql(implementor,
+ return SqlImplementor.setOpToSql(implementor,
all
? SqlStdOperatorTable.EXCEPT_ALL
: SqlStdOperatorTable.EXCEPT,
http://git-wip-us.apache.org/repos/asf/calcite/blob/980d9f81/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
index 2a5fdba..4e51116 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
@@ -86,7 +86,6 @@ import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -95,19 +94,16 @@ import java.util.logging.Logger;
/**
* Utility to convert RelNodes to SqlNode
*/
-public class RelToSqlConverter {
+public class RelToSqlConverter extends SqlImplementor {
private static final Logger LOGGER = Logger.getLogger(RelToSqlConverter.class.getName());
public static final SqlParserPos POS = SqlParserPos.ZERO;
- final SqlDialect dialect;
- private final Set<String> aliasSet = new LinkedHashSet<>();
-
private final Map<String, SqlNode> ordinalMap = new HashMap<>();
public RelToSqlConverter(SqlDialect dialect) {
- this.dialect = dialect;
+ super(dialect);
}
/**
http://git-wip-us.apache.org/repos/asf/calcite/blob/980d9f81/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
new file mode 100644
index 0000000..dcae6b1
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
@@ -0,0 +1,826 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.rel2sql;
+
+import org.apache.calcite.adapter.jdbc.JdbcRel;
+import org.apache.calcite.linq4j.Ord;
+import org.apache.calcite.linq4j.tree.Expressions;
+import org.apache.calcite.rel.RelFieldCollation;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.AggregateCall;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexLocalRef;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexProgram;
+import org.apache.calcite.sql.JoinType;
+import org.apache.calcite.sql.SqlBasicCall;
+import org.apache.calcite.sql.SqlBinaryOperator;
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlDataTypeSpec;
+import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.SqlFunction;
+import org.apache.calcite.sql.SqlFunctionCategory;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlJoin;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSelect;
+import org.apache.calcite.sql.SqlSelectKeyword;
+import org.apache.calcite.sql.SqlSetOperator;
+import org.apache.calcite.sql.fun.SqlCase;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.fun.SqlSumEmptyIsZeroAggFunction;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.BasicSqlType;
+import org.apache.calcite.sql.type.InferTypes;
+import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.validate.SqlValidatorUtil;
+import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.Util;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * State for generating a SQL statement.
+ */
+public abstract class SqlImplementor {
+ private static final Logger LOGGER =
+ Logger.getLogger(SqlImplementor.class.getName());
+
+ public static final SqlParserPos POS = SqlParserPos.ZERO;
+
+ /** Oracle's {@code SUBSTR} function.
+ * Oracle does not support {@link SqlStdOperatorTable#SUBSTRING}. */
+ public static final SqlFunction ORACLE_SUBSTR =
+ new SqlFunction("SUBSTR", SqlKind.OTHER_FUNCTION,
+ ReturnTypes.ARG0_NULLABLE_VARYING, null, null,
+ SqlFunctionCategory.STRING);
+
+ /** MySQL specific function. */
+ public static final SqlFunction ISNULL_FUNCTION =
+ new SqlFunction("ISNULL", SqlKind.OTHER_FUNCTION,
+ ReturnTypes.BOOLEAN, InferTypes.FIRST_KNOWN,
+ OperandTypes.ANY, SqlFunctionCategory.SYSTEM);
+
+ public final SqlDialect dialect;
+ protected final Set<String> aliasSet = new LinkedHashSet<>();
+
+ protected SqlImplementor(SqlDialect dialect) {
+ this.dialect = dialect;
+ }
+
+ public abstract Result visitChild(int i, RelNode e);
+
+ /** Rewrite SINGLE_VALUE into expression based on database variants
+ * E.g. HSQLDB, MYSQL, ORACLE, etc
+ */
+ public static SqlNode rewriteSingleValueExpr(SqlNode aggCall,
+ SqlDialect sqlDialect) {
+ final SqlNode operand = ((SqlBasicCall) aggCall).operand(0);
+ final SqlNode caseOperand;
+ final SqlNode elseExpr;
+ final SqlNode countCall =
+ SqlStdOperatorTable.COUNT.createCall(POS, operand);
+
+ final SqlLiteral nullLiteral = SqlLiteral.createNull(POS);
+ final SqlNode wrappedOperand;
+ switch (sqlDialect.getDatabaseProduct()) {
+ case MYSQL:
+ case HSQLDB:
+ // For MySQL, generate
+ // CASE COUNT(*)
+ // WHEN 0 THEN NULL
+ // WHEN 1 THEN <result>
+ // ELSE (SELECT NULL UNION ALL SELECT NULL)
+ // END
+ //
+ // For hsqldb, generate
+ // CASE COUNT(*)
+ // WHEN 0 THEN NULL
+ // WHEN 1 THEN MIN(<result>)
+ // ELSE (VALUES 1 UNION ALL VALUES 1)
+ // END
+ caseOperand = countCall;
+
+ final SqlNodeList selectList = new SqlNodeList(POS);
+ selectList.add(nullLiteral);
+ final SqlNode unionOperand;
+ switch (sqlDialect.getDatabaseProduct()) {
+ case MYSQL:
+ wrappedOperand = operand;
+ unionOperand = new SqlSelect(POS, SqlNodeList.EMPTY, selectList,
+ null, null, null, null, SqlNodeList.EMPTY, null, null, null);
+ break;
+ default:
+ wrappedOperand = SqlStdOperatorTable.MIN.createCall(POS, operand);
+ unionOperand = SqlStdOperatorTable.VALUES.createCall(POS,
+ SqlLiteral.createApproxNumeric("0", POS));
+ }
+
+ SqlCall unionAll = SqlStdOperatorTable.UNION_ALL
+ .createCall(POS, unionOperand, unionOperand);
+
+ final SqlNodeList subQuery = new SqlNodeList(POS);
+ subQuery.add(unionAll);
+
+ final SqlNodeList selectList2 = new SqlNodeList(POS);
+ selectList2.add(nullLiteral);
+ elseExpr = SqlStdOperatorTable.SCALAR_QUERY.createCall(POS, subQuery);
+ break;
+
+ default:
+ LOGGER.fine("SINGLE_VALUE rewrite not supported for "
+ + sqlDialect.getDatabaseProduct());
+ return aggCall;
+ }
+
+ final SqlNodeList whenList = new SqlNodeList(POS);
+ whenList.add(SqlLiteral.createExactNumeric("0", POS));
+ whenList.add(SqlLiteral.createExactNumeric("1", POS));
+
+ final SqlNodeList thenList = new SqlNodeList(POS);
+ thenList.add(nullLiteral);
+ thenList.add(wrappedOperand);
+
+ SqlNode caseExpr =
+ new SqlCase(POS, caseOperand, whenList, thenList, elseExpr);
+
+ LOGGER.fine("SINGLE_VALUE rewritten into [" + caseExpr + "]");
+
+ return caseExpr;
+ }
+
+ public void addSelect(List<SqlNode> selectList, SqlNode node,
+ RelDataType rowType) {
+ String name = rowType.getFieldNames().get(selectList.size());
+ String alias = SqlValidatorUtil.getAlias(node, -1);
+ if (alias == null || !alias.equals(name)) {
+ node = SqlStdOperatorTable.AS.createCall(
+ POS, node, new SqlIdentifier(name, POS));
+ }
+ selectList.add(node);
+ }
+
+ public static boolean isStar(List<RexNode> exps, RelDataType inputRowType) {
+ int i = 0;
+ for (RexNode ref : exps) {
+ if (!(ref instanceof RexInputRef)) {
+ return false;
+ } else if (((RexInputRef) ref).getIndex() != i++) {
+ return false;
+ }
+ }
+ return i == inputRowType.getFieldCount();
+ }
+
+ public static boolean isStar(RexProgram program) {
+ int i = 0;
+ for (RexLocalRef ref : program.getProjectList()) {
+ if (ref.getIndex() != i++) {
+ return false;
+ }
+ }
+ return i == program.getInputRowType().getFieldCount();
+ }
+
+ public static Result setOpToSql(SqlImplementor implementor,
+ SqlSetOperator operator, RelNode rel) {
+ List<SqlNode> list = Expressions.list();
+ for (Ord<RelNode> input : Ord.zip(rel.getInputs())) {
+ final Result result =
+ implementor.visitChild(input.i, input.e);
+ list.add(result.asSelect());
+ }
+ final SqlCall node = operator.createCall(new SqlNodeList(list, POS));
+ final List<Clause> clauses =
+ Expressions.list(Clause.SET_OP);
+ return implementor.result(node, clauses, rel);
+ }
+
+ /**
+ * Convert {@link RexNode} condition into {@link SqlNode}
+ *
+ * @param node condition Node
+ * @param leftContext LeftContext
+ * @param rightContext RightContext
+ * @param leftFieldCount Number of field on left result
+ * @return SqlJoin which represent the condition
+ */
+ public static SqlNode convertConditionToSqlNode(RexNode node,
+ Context leftContext,
+ Context rightContext, int leftFieldCount) {
+ if (!(node instanceof RexCall)) {
+ throw new AssertionError(node);
+ }
+ final List<RexNode> operands;
+ final SqlOperator op;
+ switch (node.getKind()) {
+ case AND:
+ case OR:
+ operands = ((RexCall) node).getOperands();
+ op = ((RexCall) node).getOperator();
+ SqlNode sqlCondition = null;
+ for (RexNode operand : operands) {
+ SqlNode x = convertConditionToSqlNode(operand, leftContext,
+ rightContext, leftFieldCount);
+ if (sqlCondition == null) {
+ sqlCondition = x;
+ } else {
+ sqlCondition = op.createCall(POS, sqlCondition, x);
+ }
+ }
+ return sqlCondition;
+
+ case EQUALS:
+ case IS_NOT_DISTINCT_FROM:
+ case NOT_EQUALS:
+ case GREATER_THAN:
+ case GREATER_THAN_OR_EQUAL:
+ case LESS_THAN:
+ case LESS_THAN_OR_EQUAL:
+ operands = ((RexCall) node).getOperands();
+ op = ((RexCall) node).getOperator();
+ if (operands.size() == 2
+ && operands.get(0) instanceof RexInputRef
+ && operands.get(1) instanceof RexInputRef) {
+ final RexInputRef op0 = (RexInputRef) operands.get(0);
+ final RexInputRef op1 = (RexInputRef) operands.get(1);
+
+ if (op0.getIndex() < leftFieldCount
+ && op1.getIndex() >= leftFieldCount) {
+ // Arguments were of form 'op0 = op1'
+ return op.createCall(POS,
+ leftContext.field(op0.getIndex()),
+ rightContext.field(op1.getIndex() - leftFieldCount));
+ }
+ if (op1.getIndex() < leftFieldCount
+ && op0.getIndex() >= leftFieldCount) {
+ // Arguments were of form 'op1 = op0'
+ return reverseOperatorDirection(op).createCall(POS,
+ leftContext.field(op1.getIndex()),
+ rightContext.field(op0.getIndex() - leftFieldCount));
+ }
+ }
+ final Context joinContext =
+ leftContext.implementor().joinContext(leftContext, rightContext);
+ return joinContext.toSql(null, node);
+ }
+ throw new AssertionError(node);
+ }
+
+ private static SqlOperator reverseOperatorDirection(SqlOperator op) {
+ switch (op.kind) {
+ case GREATER_THAN:
+ return SqlStdOperatorTable.LESS_THAN;
+ case GREATER_THAN_OR_EQUAL:
+ return SqlStdOperatorTable.LESS_THAN_OR_EQUAL;
+ case LESS_THAN:
+ return SqlStdOperatorTable.GREATER_THAN;
+ case LESS_THAN_OR_EQUAL:
+ return SqlStdOperatorTable.GREATER_THAN_OR_EQUAL;
+ case EQUALS:
+ case IS_NOT_DISTINCT_FROM:
+ case NOT_EQUALS:
+ return op;
+ default:
+ throw new AssertionError(op);
+ }
+ }
+
+ public static JoinType joinType(JoinRelType joinType) {
+ switch (joinType) {
+ case LEFT:
+ return JoinType.LEFT;
+ case RIGHT:
+ return JoinType.RIGHT;
+ case INNER:
+ return JoinType.INNER;
+ case FULL:
+ return JoinType.FULL;
+ default:
+ throw new AssertionError(joinType);
+ }
+ }
+
+ /** Creates a result based on a single relational expression. */
+ public Result result(SqlNode node, Collection<Clause> clauses, RelNode rel) {
+ final String alias2 = SqlValidatorUtil.getAlias(node, -1);
+ final String alias3 = alias2 != null ? alias2 : "t";
+ final String alias4 =
+ SqlValidatorUtil.uniquify(
+ alias3, aliasSet, SqlValidatorUtil.EXPR_SUGGESTER);
+ final String alias5 = alias2 == null || !alias2.equals(alias4) ? alias4
+ : null;
+ return new Result(node, clauses, alias5,
+ Collections.singletonList(Pair.of(alias4, rel.getRowType())));
+ }
+
+ /** Creates a result based on a join. (Each join could contain one or more
+ * relational expressions.) */
+ public Result result(SqlNode join, Result leftResult, Result rightResult) {
+ final List<Pair<String, RelDataType>> list = new ArrayList<>();
+ list.addAll(leftResult.aliases);
+ list.addAll(rightResult.aliases);
+ return new Result(join, Expressions.list(Clause.FROM), null, list);
+ }
+
+ /** Wraps a node in a SELECT statement that has no clauses:
+ * "SELECT ... FROM (node)". */
+ SqlSelect wrapSelect(SqlNode node) {
+ assert node instanceof SqlJoin
+ || node instanceof SqlIdentifier
+ || node instanceof SqlCall
+ && (((SqlCall) node).getOperator() instanceof SqlSetOperator
+ || ((SqlCall) node).getOperator() == SqlStdOperatorTable.AS)
+ : node;
+ return new SqlSelect(POS, SqlNodeList.EMPTY, null, node, null, null, null,
+ SqlNodeList.EMPTY, null, null, null);
+ }
+
+ /** Context for translating a {@link RexNode} expression (within a
+ * {@link RelNode}) into a {@link SqlNode} expression (within a SQL parse
+ * tree). */
+ public abstract class Context {
+ final int fieldCount;
+
+ protected Context(int fieldCount) {
+ this.fieldCount = fieldCount;
+ }
+
+ public abstract SqlNode field(int ordinal);
+
+ /** Converts an expression from {@link RexNode} to {@link SqlNode}
+ * format. */
+ public SqlNode toSql(RexProgram program, RexNode rex) {
+ switch (rex.getKind()) {
+ case LOCAL_REF:
+ final int index = ((RexLocalRef) rex).getIndex();
+ return toSql(program, program.getExprList().get(index));
+
+ case INPUT_REF:
+ return field(((RexInputRef) rex).getIndex());
+
+ case LITERAL:
+ final RexLiteral literal = (RexLiteral) rex;
+ if (literal.getTypeName() == SqlTypeName.SYMBOL) {
+ final SqlLiteral.SqlSymbol symbol =
+ (SqlLiteral.SqlSymbol) literal.getValue();
+ return SqlLiteral.createSymbol(symbol, POS);
+ }
+ switch (literal.getTypeName().getFamily()) {
+ case CHARACTER:
+ return SqlLiteral.createCharString((String) literal.getValue2(), POS);
+ case NUMERIC:
+ case EXACT_NUMERIC:
+ return SqlLiteral.createExactNumeric(literal.getValue().toString(),
+ POS);
+ case APPROXIMATE_NUMERIC:
+ return SqlLiteral.createApproxNumeric(
+ literal.getValue().toString(), POS);
+ case BOOLEAN:
+ return SqlLiteral.createBoolean((Boolean) literal.getValue(), POS);
+ case DATE:
+ return SqlLiteral.createDate((Calendar) literal.getValue(), POS);
+ case TIME:
+ return SqlLiteral.createTime((Calendar) literal.getValue(),
+ literal.getType().getPrecision(), POS);
+ case TIMESTAMP:
+ return SqlLiteral.createTimestamp((Calendar) literal.getValue(),
+ literal.getType().getPrecision(), POS);
+ case ANY:
+ case NULL:
+ switch (literal.getTypeName()) {
+ case NULL:
+ return SqlLiteral.createNull(POS);
+ // fall through
+ }
+ default:
+ throw new AssertionError(literal + ": " + literal.getTypeName());
+ }
+ case CASE:
+ final RexCall caseCall = (RexCall) rex;
+ final List<SqlNode> caseNodeList =
+ toSql(program, caseCall.getOperands());
+ final SqlNode valueNode;
+ final List<SqlNode> whenList = Expressions.list();
+ final List<SqlNode> thenList = Expressions.list();
+ final SqlNode elseNode;
+ if (caseNodeList.size() % 2 == 0) {
+ // switched:
+ // "case x when v1 then t1 when v2 then t2 ... else e end"
+ valueNode = caseNodeList.get(0);
+ for (int i = 1; i < caseNodeList.size() - 1; i += 2) {
+ whenList.add(caseNodeList.get(i));
+ thenList.add(caseNodeList.get(i + 1));
+ }
+ } else {
+ // other: "case when w1 then t1 when w2 then t2 ... else e end"
+ valueNode = null;
+ for (int i = 0; i < caseNodeList.size() - 1; i += 2) {
+ whenList.add(caseNodeList.get(i));
+ thenList.add(caseNodeList.get(i + 1));
+ }
+ }
+ elseNode = caseNodeList.get(caseNodeList.size() - 1);
+ return new SqlCase(POS, valueNode, new SqlNodeList(whenList, POS),
+ new SqlNodeList(thenList, POS), elseNode);
+
+ default:
+ final RexCall call = (RexCall) rex;
+ final SqlOperator op = call.getOperator();
+ final List<SqlNode> nodeList = toSql(program, call.getOperands());
+ switch (rex.getKind()) {
+ case CAST:
+ nodeList.add(toSql(call.getType()));
+ }
+ if (op instanceof SqlBinaryOperator && nodeList.size() > 2) {
+ // In RexNode trees, OR and AND have any number of children;
+ // SqlCall requires exactly 2. So, convert to a left-deep binary tree.
+ return createLeftCall(op, nodeList);
+ }
+ if (op == SqlStdOperatorTable.SUBSTRING) {
+ switch (dialect.getDatabaseProduct()) {
+ case ORACLE:
+ return ORACLE_SUBSTR.createCall(new SqlNodeList(nodeList, POS));
+ }
+ }
+ return op.createCall(new SqlNodeList(nodeList, POS));
+ }
+ }
+
+ private SqlNode createLeftCall(SqlOperator op, List<SqlNode> nodeList) {
+ if (nodeList.size() == 2) {
+ return op.createCall(new SqlNodeList(nodeList, POS));
+ }
+ final List<SqlNode> butLast = Util.skipLast(nodeList);
+ final SqlNode last = nodeList.get(nodeList.size() - 1);
+ final SqlNode call = createLeftCall(op, butLast);
+ return op.createCall(new SqlNodeList(ImmutableList.of(call, last), POS));
+ }
+
+ private SqlNode toSql(RelDataType type) {
+ switch (dialect.getDatabaseProduct()) {
+ case MYSQL:
+ switch (type.getSqlTypeName()) {
+ case VARCHAR:
+ // MySQL doesn't have a VARCHAR type, only CHAR.
+ return new SqlDataTypeSpec(new SqlIdentifier("CHAR", POS),
+ type.getPrecision(), -1, null, null, POS);
+ case INTEGER:
+ return new SqlDataTypeSpec(new SqlIdentifier("_UNSIGNED", POS),
+ type.getPrecision(), -1, null, null, POS);
+ }
+ break;
+ }
+ if (type instanceof BasicSqlType) {
+ return new SqlDataTypeSpec(
+ new SqlIdentifier(type.getSqlTypeName().name(), POS),
+ type.getPrecision(),
+ type.getScale(),
+ type.getCharset() != null
+ && dialect.supportsCharSet()
+ ? type.getCharset().name()
+ : null,
+ null,
+ POS);
+ }
+ throw new AssertionError(type); // TODO: implement
+ }
+
+ private List<SqlNode> toSql(RexProgram program, List<RexNode> operandList) {
+ final List<SqlNode> list = new ArrayList<>();
+ for (RexNode rex : operandList) {
+ list.add(toSql(program, rex));
+ }
+ return list;
+ }
+
+ public List<SqlNode> fieldList() {
+ return new AbstractList<SqlNode>() {
+ public SqlNode get(int index) {
+ return field(index);
+ }
+
+ public int size() {
+ return fieldCount;
+ }
+ };
+ }
+
+ /** Converts a call to an aggregate function to an expression. */
+ public SqlNode toSql(AggregateCall aggCall) {
+ SqlOperator op = aggCall.getAggregation();
+ if (op instanceof SqlSumEmptyIsZeroAggFunction) {
+ op = SqlStdOperatorTable.SUM;
+ }
+ final List<SqlNode> operands = Expressions.list();
+ for (int arg : aggCall.getArgList()) {
+ operands.add(field(arg));
+ }
+ return op.createCall(
+ aggCall.isDistinct() ? SqlSelectKeyword.DISTINCT.symbol(POS) : null,
+ POS, operands.toArray(new SqlNode[operands.size()]));
+ }
+
+ /** Converts a collation to an ORDER BY item. */
+ public SqlNode toSql(RelFieldCollation collation) {
+ SqlNode node = field(collation.getFieldIndex());
+ switch (collation.getDirection()) {
+ case DESCENDING:
+ case STRICTLY_DESCENDING:
+ node = SqlStdOperatorTable.DESC.createCall(POS, node);
+ }
+ switch (collation.nullDirection) {
+ case FIRST:
+ node = SqlStdOperatorTable.NULLS_FIRST.createCall(POS, node);
+ break;
+ case LAST:
+ node = SqlStdOperatorTable.NULLS_LAST.createCall(POS, node);
+ break;
+ }
+ return node;
+ }
+
+ public SqlImplementor implementor() {
+ return SqlImplementor.this;
+ }
+ }
+
+ private static int computeFieldCount(
+ List<Pair<String, RelDataType>> aliases) {
+ int x = 0;
+ for (Pair<String, RelDataType> alias : aliases) {
+ x += alias.right.getFieldCount();
+ }
+ return x;
+ }
+
+ public Context aliasContext(List<Pair<String, RelDataType>> aliases,
+ boolean qualified) {
+ return new AliasContext(aliases, qualified);
+ }
+
+ public Context joinContext(Context leftContext, Context rightContext) {
+ return new JoinContext(leftContext, rightContext);
+ }
+
+ /** Implementation of Context that precedes field references with their
+ * "table alias" based on the current sub-query's FROM clause. */
+ public class AliasContext extends Context {
+ private final boolean qualified;
+ private final List<Pair<String, RelDataType>> aliases;
+
+ /** Creates an AliasContext; use {@link #aliasContext(List, boolean)}. */
+ private AliasContext(List<Pair<String, RelDataType>> aliases,
+ boolean qualified) {
+ super(computeFieldCount(aliases));
+ this.aliases = aliases;
+ this.qualified = qualified;
+ }
+
+ public SqlNode field(int ordinal) {
+ for (Pair<String, RelDataType> alias : aliases) {
+ final List<RelDataTypeField> fields = alias.right.getFieldList();
+ if (ordinal < fields.size()) {
+ RelDataTypeField field = fields.get(ordinal);
+ return new SqlIdentifier(!qualified
+ ? ImmutableList.of(field.getName())
+ : ImmutableList.of(alias.left, field.getName()),
+ POS);
+ }
+ ordinal -= fields.size();
+ }
+ throw new AssertionError(
+ "field ordinal " + ordinal + " out of range " + aliases);
+ }
+ }
+
+ /** Context for translating ON clause of a JOIN from {@link RexNode} to
+ * {@link SqlNode}. */
+ class JoinContext extends Context {
+ private final SqlImplementor.Context leftContext;
+ private final SqlImplementor.Context rightContext;
+
+ /** Creates a JoinContext; use {@link #joinContext(Context, Context)}. */
+ private JoinContext(Context leftContext, Context rightContext) {
+ super(leftContext.fieldCount + rightContext.fieldCount);
+ this.leftContext = leftContext;
+ this.rightContext = rightContext;
+ }
+
+ public SqlNode field(int ordinal) {
+ if (ordinal < leftContext.fieldCount) {
+ return leftContext.field(ordinal);
+ } else {
+ return rightContext.field(ordinal - leftContext.fieldCount);
+ }
+ }
+ }
+
+ /** Result of implementing a node. */
+ public class Result {
+ final SqlNode node;
+ private final String neededAlias;
+ public final List<Pair<String, RelDataType>> aliases;
+ final Expressions.FluentList<Clause> clauses;
+
+ public Result(SqlNode node, Collection<Clause> clauses, String neededAlias,
+ List<Pair<String, RelDataType>> aliases) {
+ this.node = node;
+ this.neededAlias = neededAlias;
+ this.aliases = aliases;
+ this.clauses = Expressions.list(clauses);
+ }
+
+ /** Once you have a Result of implementing a child relational expression,
+ * call this method to create a Builder to implement the current relational
+ * expression by adding additional clauses to the SQL query.
+ *
+ * <p>You need to declare which clauses you intend to add. If the clauses
+ * are "later", you can add to the same query. For example, "GROUP BY" comes
+ * after "WHERE". But if they are the same or earlier, this method will
+ * start a new SELECT that wraps the previous result.</p>
+ *
+ * <p>When you have called
+ * {@link Builder#setSelect(SqlNodeList)},
+ * {@link Builder#setWhere(SqlNode)} etc. call
+ * {@link Builder#result(SqlNode, Collection, RelNode)}
+ * to fix the new query.</p>
+ *
+ * @param rel Relational expression being implemented
+ * @param clauses Clauses that will be generated to implement current
+ * relational expression
+ * @return A builder
+ */
+ public Builder builder(JdbcRel rel, Clause... clauses) {
+ final Clause maxClause = maxClause();
+ boolean needNew = false;
+ for (Clause clause : clauses) {
+ if (maxClause.ordinal() >= clause.ordinal()) {
+ needNew = true;
+ }
+ }
+ SqlSelect select;
+ Expressions.FluentList<Clause> clauseList = Expressions.list();
+ if (needNew) {
+ select = subSelect();
+ } else {
+ select = asSelect();
+ clauseList.addAll(this.clauses);
+ }
+ clauseList.appendAll(clauses);
+ Context newContext;
+ final SqlNodeList selectList = select.getSelectList();
+ if (selectList != null) {
+ newContext = new Context(selectList.size()) {
+ @Override public SqlNode field(int ordinal) {
+ final SqlNode selectItem = selectList.get(ordinal);
+ switch (selectItem.getKind()) {
+ case AS:
+ return ((SqlCall) selectItem).operand(0);
+ }
+ return selectItem;
+ }
+ };
+ } else {
+ newContext = aliasContext(aliases, aliases.size() > 1);
+ }
+ return new Builder(rel, clauseList, select, newContext);
+ }
+
+ // make private?
+ public Clause maxClause() {
+ Clause maxClause = null;
+ for (Clause clause : clauses) {
+ if (maxClause == null || clause.ordinal() > maxClause.ordinal()) {
+ maxClause = clause;
+ }
+ }
+ assert maxClause != null;
+ return maxClause;
+ }
+
+ /** Returns a node that can be included in the FROM clause or a JOIN. It has
+ * an alias that is unique within the query. The alias is implicit if it
+ * can be derived using the usual rules (For example, "SELECT * FROM emp" is
+ * equivalent to "SELECT * FROM emp AS emp".) */
+ public SqlNode asFrom() {
+ if (neededAlias != null) {
+ return SqlStdOperatorTable.AS.createCall(POS, node,
+ new SqlIdentifier(neededAlias, POS));
+ }
+ return node;
+ }
+
+ public SqlSelect subSelect() {
+ return wrapSelect(asFrom());
+ }
+
+ /** Converts a non-query node into a SELECT node. Set operators (UNION,
+ * INTERSECT, EXCEPT) remain as is. */
+ public SqlSelect asSelect() {
+ if (node instanceof SqlSelect) {
+ return (SqlSelect) node;
+ }
+ return wrapSelect(node);
+ }
+
+ /** Converts a non-query node into a SELECT node. Set operators (UNION,
+ * INTERSECT, EXCEPT) remain as is. */
+ public SqlNode asQuery() {
+ if (node instanceof SqlCall
+ && ((SqlCall) node).getOperator() instanceof SqlSetOperator) {
+ return node;
+ }
+ return asSelect();
+ }
+
+ /** Returns a context that always qualifies identifiers. Useful if the
+ * Context deals with just one arm of a join, yet we wish to generate
+ * a join condition that qualifies column names to disambiguate them. */
+ public Context qualifiedContext() {
+ return aliasContext(aliases, true);
+ }
+ }
+
+ /** Builder. */
+ public class Builder {
+ private final JdbcRel rel;
+ private final List<Clause> clauses;
+ private final SqlSelect select;
+ public final Context context;
+
+ public Builder(JdbcRel rel, List<Clause> clauses, SqlSelect select,
+ Context context) {
+ this.rel = rel;
+ this.clauses = clauses;
+ this.select = select;
+ this.context = context;
+ }
+
+ public void setSelect(SqlNodeList nodeList) {
+ select.setSelectList(nodeList);
+ }
+
+ public void setWhere(SqlNode node) {
+ assert clauses.contains(Clause.WHERE);
+ select.setWhere(node);
+ }
+
+ public void setGroupBy(SqlNodeList nodeList) {
+ assert clauses.contains(Clause.GROUP_BY);
+ select.setGroupBy(nodeList);
+ }
+
+ public void setOrderBy(SqlNodeList nodeList) {
+ assert clauses.contains(Clause.ORDER_BY);
+ select.setOrderBy(nodeList);
+ }
+
+ public Result result() {
+ return SqlImplementor.this.result(select, clauses, rel);
+ }
+ }
+
+ /** Clauses in a SQL query. Ordered by evaluation order.
+ * SELECT is set only when there is a NON-TRIVIAL SELECT clause. */
+ public enum Clause {
+ FROM, WHERE, GROUP_BY, HAVING, SELECT, SET_OP, ORDER_BY
+ }
+}
+
+// End SqlImplementor.java