in core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java [2888:3227]
private void registerQuery(
SqlValidatorScope parentScope,
@Nullable SqlValidatorScope usingScope,
SqlNode node,
SqlNode enclosingNode,
@Nullable String alias,
boolean forceNullable,
boolean checkUpdate) {
requireNonNull(node, "node");
requireNonNull(enclosingNode, "enclosingNode");
checkArgument(usingScope == null || alias != null);
SqlCall call;
List<SqlNode> operands;
switch (node.getKind()) {
case SELECT:
final SqlSelect select = (SqlSelect) node;
final SelectNamespace selectNs =
createSelectNamespace(select, enclosingNode);
registerNamespace(usingScope, alias, selectNs, forceNullable);
final SqlValidatorScope windowParentScope =
first(usingScope, parentScope);
SelectScope selectScope =
new SelectScope(parentScope, windowParentScope, select);
scopes.put(select, selectScope);
// Start by registering the WHERE clause
clauseScopes.put(IdPair.of(select, Clause.WHERE), selectScope);
registerOperandSubQueries(
selectScope,
select,
SqlSelect.WHERE_OPERAND);
// Register subqueries in the QUALIFY clause
registerOperandSubQueries(
selectScope,
select,
SqlSelect.QUALIFY_OPERAND);
// Register FROM with the inherited scope 'parentScope', not
// 'selectScope', otherwise tables in the FROM clause would be
// able to see each other.
final SqlNode from = select.getFrom();
if (from != null) {
final SqlNode newFrom =
registerFrom(
parentScope,
selectScope,
true,
from,
from,
null,
null,
false,
false);
if (newFrom != from) {
select.setFrom(newFrom);
}
}
// If this is an aggregate query, the SELECT list and HAVING
// clause use a different scope, where you can only reference
// columns which are in the GROUP BY clause.
final SqlValidatorScope selectScope2 =
isAggregate(select)
? new AggregatingSelectScope(selectScope, select, false)
: selectScope;
clauseScopes.put(IdPair.of(select, Clause.SELECT), selectScope2);
clauseScopes.put(IdPair.of(select, Clause.MEASURE),
new MeasureScope(selectScope, select));
if (select.getGroup() != null) {
GroupByScope groupByScope =
new GroupByScope(selectScope, select.getGroup(), select);
clauseScopes.put(IdPair.of(select, Clause.GROUP_BY), groupByScope);
registerSubQueries(groupByScope, select.getGroup());
}
registerOperandSubQueries(
selectScope2,
select,
SqlSelect.HAVING_OPERAND);
registerSubQueries(selectScope2,
SqlNonNullableAccessors.getSelectList(select));
final SqlNodeList orderList = select.getOrderList();
if (orderList != null) {
// If the query is 'SELECT DISTINCT', restrict the columns
// available to the ORDER BY clause.
final SqlValidatorScope selectScope3 =
select.isDistinct()
? new AggregatingSelectScope(selectScope, select, true)
: selectScope2;
OrderByScope orderScope =
new OrderByScope(selectScope3, orderList, select);
clauseScopes.put(IdPair.of(select, Clause.ORDER), orderScope);
registerSubQueries(orderScope, orderList);
if (!isAggregate(select)) {
// Since this is not an aggregate query,
// there cannot be any aggregates in the ORDER BY clause.
SqlNode agg = aggFinder.findAgg(orderList);
if (agg != null) {
throw newValidationError(agg, RESOURCE.aggregateIllegalInOrderBy());
}
}
}
break;
case INTERSECT:
validateFeature(RESOURCE.sQLFeature_F302(), node.getParserPosition());
registerSetop(
parentScope,
usingScope,
node,
node,
alias,
forceNullable);
break;
case EXCEPT:
validateFeature(RESOURCE.sQLFeature_E071_03(), node.getParserPosition());
registerSetop(
parentScope,
usingScope,
node,
node,
alias,
forceNullable);
break;
case UNION:
registerSetop(
parentScope,
usingScope,
node,
enclosingNode,
alias,
forceNullable);
break;
case LAMBDA:
call = (SqlCall) node;
SqlLambdaScope lambdaScope =
new SqlLambdaScope(parentScope, (SqlLambda) call);
scopes.put(call, lambdaScope);
final LambdaNamespace lambdaNamespace =
new LambdaNamespace(this, (SqlLambda) call, node);
registerNamespace(
usingScope,
alias,
lambdaNamespace,
forceNullable);
operands = call.getOperandList();
for (int i = 0; i < operands.size(); i++) {
registerOperandSubQueries(parentScope, call, i);
}
break;
case WITH:
registerWith(parentScope, usingScope, (SqlWith) node, enclosingNode,
alias, forceNullable, checkUpdate);
break;
case VALUES:
call = (SqlCall) node;
scopes.put(call, parentScope);
final TableConstructorNamespace tableConstructorNamespace =
new TableConstructorNamespace(
this,
call,
parentScope,
enclosingNode);
registerNamespace(
usingScope,
alias,
tableConstructorNamespace,
forceNullable);
operands = call.getOperandList();
for (int i = 0; i < operands.size(); ++i) {
assert operands.get(i).getKind() == SqlKind.ROW;
// FIXME jvs 9-Feb-2005: Correlation should
// be illegal in these sub-queries. Same goes for
// any non-lateral SELECT in the FROM list.
registerOperandSubQueries(parentScope, call, i);
}
break;
case INSERT:
SqlInsert insertCall = (SqlInsert) node;
InsertNamespace insertNs =
new InsertNamespace(
this,
insertCall,
enclosingNode,
parentScope);
registerNamespace(usingScope, null, insertNs, forceNullable);
registerQuery(
parentScope,
usingScope,
insertCall.getSource(),
enclosingNode,
null,
false);
break;
case DELETE:
SqlDelete deleteCall = (SqlDelete) node;
DeleteNamespace deleteNs =
new DeleteNamespace(
this,
deleteCall,
enclosingNode,
parentScope);
registerNamespace(usingScope, null, deleteNs, forceNullable);
registerQuery(
parentScope,
usingScope,
SqlNonNullableAccessors.getSourceSelect(deleteCall),
enclosingNode,
null,
false);
break;
case UPDATE:
if (checkUpdate) {
validateFeature(RESOURCE.sQLFeature_E101_03(),
node.getParserPosition());
}
SqlUpdate updateCall = (SqlUpdate) node;
UpdateNamespace updateNs =
new UpdateNamespace(
this,
updateCall,
enclosingNode,
parentScope);
registerNamespace(usingScope, null, updateNs, forceNullable);
registerQuery(
parentScope,
usingScope,
SqlNonNullableAccessors.getSourceSelect(updateCall),
enclosingNode,
null,
false);
break;
case MERGE:
validateFeature(RESOURCE.sQLFeature_F312(), node.getParserPosition());
SqlMerge mergeCall = (SqlMerge) node;
MergeNamespace mergeNs =
new MergeNamespace(
this,
mergeCall,
enclosingNode,
parentScope);
registerNamespace(usingScope, null, mergeNs, forceNullable);
registerQuery(
parentScope,
usingScope,
SqlNonNullableAccessors.getSourceSelect(mergeCall),
enclosingNode,
null,
false);
// update call can reference either the source table reference
// or the target table, so set its parent scope to the merge's
// source select; when validating the update, skip the feature
// validation check
SqlUpdate mergeUpdateCall = mergeCall.getUpdateCall();
if (mergeUpdateCall != null) {
registerQuery(
getScope(SqlNonNullableAccessors.getSourceSelect(mergeCall), Clause.WHERE),
null,
mergeUpdateCall,
enclosingNode,
null,
false,
false);
}
SqlInsert mergeInsertCall = mergeCall.getInsertCall();
if (mergeInsertCall != null) {
registerQuery(
parentScope,
null,
mergeInsertCall,
enclosingNode,
null,
false);
}
break;
case UNNEST:
call = (SqlCall) node;
final UnnestNamespace unnestNs =
new UnnestNamespace(this, call, parentScope, enclosingNode);
registerNamespace(
usingScope,
alias,
unnestNs,
forceNullable);
registerOperandSubQueries(parentScope, call, 0);
scopes.put(node, parentScope);
break;
case OTHER_FUNCTION:
call = (SqlCall) node;
ProcedureNamespace procNs =
new ProcedureNamespace(
this,
parentScope,
call,
enclosingNode);
registerNamespace(
usingScope,
alias,
procNs,
forceNullable);
registerSubQueries(parentScope, call);
break;
case MULTISET_QUERY_CONSTRUCTOR:
case MULTISET_VALUE_CONSTRUCTOR:
validateFeature(RESOURCE.sQLFeature_S271(), node.getParserPosition());
call = (SqlCall) node;
CollectScope cs = new CollectScope(parentScope, usingScope, call);
final CollectNamespace tableConstructorNs =
new CollectNamespace(call, cs, enclosingNode);
final String alias2 = SqlValidatorUtil.alias(node, nextGeneratedId++);
registerNamespace(
usingScope,
alias2,
tableConstructorNs,
forceNullable);
operands = call.getOperandList();
for (int i = 0; i < operands.size(); i++) {
registerOperandSubQueries(parentScope, call, i);
}
break;
default:
throw Util.unexpected(node.getKind());
}
}