in core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java [2049:2156]
private void findSubQueries(
Blackboard bb,
SqlNode node,
RelOptUtil.Logic logic,
boolean registerOnlyScalarSubQueries) {
// If node is structurally identical to one of the group-by,
// then it is not a sub-query; it is a reference.
if (bb.agg != null && bb.agg.lookupGroupExpr(node) != -1) {
return;
}
final SqlKind kind = node.getKind();
switch (kind) {
case EXISTS:
case UNIQUE:
case SELECT:
case MULTISET_QUERY_CONSTRUCTOR:
case MULTISET_VALUE_CONSTRUCTOR:
case ARRAY_QUERY_CONSTRUCTOR:
case MAP_QUERY_CONSTRUCTOR:
case CURSOR:
case SET_SEMANTICS_TABLE:
case SCALAR_QUERY:
if (!registerOnlyScalarSubQueries
|| (kind == SqlKind.SCALAR_QUERY)) {
bb.registerSubQuery(node, RelOptUtil.Logic.TRUE_FALSE);
}
return;
case IN:
break;
case NOT_IN:
case NOT:
logic = logic.negate();
break;
default:
break;
}
if (node instanceof SqlCall) {
switch (kind) {
// Do no change logic for AND, IN and NOT IN expressions;
// but do change logic for OR, NOT and others;
// EXISTS was handled already.
case AND:
case IN:
case NOT_IN:
break;
default:
logic = RelOptUtil.Logic.TRUE_FALSE_UNKNOWN;
break;
}
for (SqlNode operand : ((SqlCall) node).getOperandList()) {
if (operand != null) {
// In the case of an IN expression, locate scalar
// sub-queries so we can convert them to constants
findSubQueries(bb, operand, logic,
kind == SqlKind.IN || kind == SqlKind.NOT_IN
|| kind == SqlKind.SOME || kind == SqlKind.ALL
|| registerOnlyScalarSubQueries);
}
}
} else if (node instanceof SqlNodeList) {
for (SqlNode child : (SqlNodeList) node) {
findSubQueries(bb, child, logic,
kind == SqlKind.IN || kind == SqlKind.NOT_IN
|| kind == SqlKind.SOME || kind == SqlKind.ALL
|| registerOnlyScalarSubQueries);
}
}
// Now that we've located any scalar sub-queries inside the IN
// expression, register the IN expression itself. We need to
// register the scalar sub-queries first so they can be converted
// before the IN expression is converted.
switch (kind) {
case IN:
case NOT_IN:
case SOME:
case ALL:
switch (logic) {
case TRUE_FALSE_UNKNOWN:
RelDataType type = validator().getValidatedNodeTypeIfKnown(node);
if (type == null) {
// The node might not be validated if we still don't know type of the node.
// Therefore return directly.
return;
} else {
break;
}
case UNKNOWN_AS_FALSE:
logic = RelOptUtil.Logic.TRUE;
break;
default:
break;
}
if (node instanceof SqlBasicCall
&& ((SqlCall) node).getOperator() instanceof SqlQuantifyOperator
&& ((SqlQuantifyOperator) ((SqlCall) node).getOperator())
.tryDeriveTypeForCollection(bb.getValidator(), bb.scope,
(SqlCall) node) != null) {
findSubQueries(bb, ((SqlCall) node).operand(0), logic, registerOnlyScalarSubQueries);
findSubQueries(bb, ((SqlCall) node).operand(1), logic, registerOnlyScalarSubQueries);
break;
}
bb.registerSubQuery(node, logic);
break;
default:
break;
}
}