in core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java [658:855]
public SqlNode toSql(@Nullable RexProgram program, RexNode rex) {
rex = dialect.prepareUnparse(rex);
final RexSubQuery subQuery;
final SqlNode sqlSubQuery;
final RexLiteral literal;
switch (rex.getKind()) {
case LOCAL_REF:
final int index = ((RexLocalRef) rex).getIndex();
return toSql(program, requireNonNull(program, "program").getExprList().get(index));
case INPUT_REF:
return field(((RexInputRef) rex).getIndex());
case FIELD_ACCESS:
final Deque<RexFieldAccess> accesses = new ArrayDeque<>();
RexNode referencedExpr = rex;
while (referencedExpr.getKind() == SqlKind.FIELD_ACCESS) {
accesses.offerLast((RexFieldAccess) referencedExpr);
referencedExpr = ((RexFieldAccess) referencedExpr).getReferenceExpr();
}
SqlIdentifier sqlIdentifier;
switch (referencedExpr.getKind()) {
case CORREL_VARIABLE:
final RexCorrelVariable variable = (RexCorrelVariable) referencedExpr;
final Context correlAliasContext = getAliasContext(variable);
final RexFieldAccess lastAccess = requireNonNull(accesses.pollLast());
SqlNode node = correlAliasContext
.field(lastAccess.getField().getIndex());
if (node instanceof SqlDynamicParam) {
return node;
}
sqlIdentifier = (SqlIdentifier) node;
break;
case ROW:
case ITEM:
final SqlNode expr = toSql(program, referencedExpr);
sqlIdentifier = new SqlIdentifier(expr.toString(), POS);
break;
default:
sqlIdentifier = (SqlIdentifier) toSql(program, referencedExpr);
}
int nameIndex = sqlIdentifier.names.size();
RexFieldAccess access;
while ((access = accesses.pollLast()) != null) {
sqlIdentifier = sqlIdentifier.add(nameIndex++, access.getField().getName(), POS);
}
return sqlIdentifier;
case PATTERN_INPUT_REF:
final RexPatternFieldRef ref = (RexPatternFieldRef) rex;
String pv = ref.getAlpha();
SqlNode refNode = field(ref.getIndex());
final SqlIdentifier id = (SqlIdentifier) refNode;
if (id.names.size() > 1) {
return id.setName(0, pv);
} else {
return new SqlIdentifier(ImmutableList.of(pv, id.names.get(0)), POS);
}
case LITERAL:
return SqlImplementor.toSql(program, (RexLiteral) rex);
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);
if (caseCall.getType().getSqlTypeName() == SqlTypeName.BOOLEAN
&& !dialect.supportBooleanCaseWhen()) {
// Transformed when expressions of boolean type in SqlCase
// For example, given
// CASE WHEN x > 1 THEN y > 1 ELSE y < 10 END
// Transformed:
// (CASE WHEN x > 1 THEN (CASE WHEN y > 1 THEN 1 ELSE 0 END)
// ELSE (CASE WHEN y < 10 THEN 1 ELSE 0 END) END) = 1
final List<SqlNode> thenList2 = thenList.stream()
.map(
thenNode -> new SqlCase(POS, null, SqlNodeList.of(thenNode),
SqlNodeList.of(ONE), SqlNodeList.of(ZERO)))
.collect(SqlNode.toList());
final SqlNode elseNode2 =
new SqlCase(POS, null, SqlNodeList.of(elseNode),
SqlNodeList.of(ONE), SqlNodeList.of(ZERO));
final SqlCase sqlCase =
new SqlCase(POS, valueNode,
new SqlNodeList(whenList, POS),
new SqlNodeList(thenList2, POS), elseNode2);
return SqlStdOperatorTable.EQUALS.createCall(POS, sqlCase, ONE);
}
return new SqlCase(POS, valueNode, new SqlNodeList(whenList, POS),
new SqlNodeList(thenList, POS), elseNode);
case DYNAMIC_PARAM:
final RexDynamicParam caseParam = (RexDynamicParam) rex;
if (caseParam.getIndex() >= JdbcCorrelationDataContext.OFFSET) {
throw new AssertionError("More than "
+ JdbcCorrelationDataContext.OFFSET
+ " dynamic parameters used in query");
}
return new SqlDynamicParam(caseParam.getIndex(), POS);
case IN:
case SOME:
case ALL:
subQuery = (RexSubQuery) rex;
sqlSubQuery = implementor().visitRoot(subQuery.rel).asQueryOrValues();
final List<RexNode> operands = subQuery.operands;
SqlNode op0;
if (operands.size() == 1) {
op0 = toSql(program, operands.get(0));
} else {
final List<SqlNode> cols = toSql(program, operands);
op0 = new SqlNodeList(cols, POS);
}
return subQuery.getOperator().createCall(POS, op0, sqlSubQuery);
case SEARCH:
final RexCall search = (RexCall) rex;
if (search.operands.get(1).getKind() == SqlKind.LITERAL) {
literal = (RexLiteral) search.operands.get(1);
final Sarg sarg = castNonNull(literal.getValueAs(Sarg.class));
//noinspection unchecked
return toSql(program, search.operands.get(0), literal.getType(), sarg);
}
return toSql(program, RexUtil.expandSearch(RexBuilder.DEFAULT, program, search));
case EXISTS:
case UNIQUE:
case SCALAR_QUERY:
subQuery = (RexSubQuery) rex;
sqlSubQuery =
implementor().visitRoot(subQuery.rel).asQueryOrValues();
return subQuery.getOperator().createCall(POS, sqlSubQuery);
case NOT:
RexNode operand = ((RexCall) rex).operands.get(0);
final SqlNode node = toSql(program, operand);
final SqlOperator inverseOperator = getInverseOperator(operand);
if (inverseOperator != null) {
switch (operand.getKind()) {
case IN:
assert operand instanceof RexSubQuery
: "scalar IN is no longer allowed in RexCall: " + rex;
break;
default:
break;
}
return inverseOperator.createCall(POS,
((SqlCall) node).getOperandList());
} else {
return SqlStdOperatorTable.NOT.createCall(POS, node);
}
case LAMBDA:
final RexLambda lambda = (RexLambda) rex;
final SqlNodeList parameters = new SqlNodeList(POS);
for (RexLambdaRef parameter : lambda.getParameters()) {
parameters.add(toSql(program, parameter));
}
final SqlNode expression = toSql(program, lambda.getExpression());
return new SqlLambda(POS, parameters, expression);
case LAMBDA_REF:
final RexLambdaRef lambdaRef = (RexLambdaRef) rex;
return new SqlIdentifier(lambdaRef.getName(), POS);
default:
if (rex instanceof RexOver) {
return toSql(program, (RexOver) rex);
}
return callToSql(program, (RexCall) rex, false);
}
}