in core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java [2433:2812]
private SqlNode registerFrom(
SqlValidatorScope parentScope0,
SqlValidatorScope usingScope,
boolean register,
final SqlNode node,
SqlNode enclosingNode,
@Nullable String alias,
@Nullable SqlNodeList extendList,
boolean forceNullable,
final boolean lateral) {
final SqlKind kind = node.getKind();
SqlNode expr;
SqlNode newExpr;
// Add an alias if necessary.
SqlNode newNode = node;
if (alias == null) {
switch (kind) {
case IDENTIFIER:
case OVER:
alias = SqlValidatorUtil.alias(node);
if (alias == null) {
alias = SqlValidatorUtil.alias(node, nextGeneratedId++);
}
if (config.identifierExpansion()) {
newNode = SqlValidatorUtil.addAlias(node, alias);
}
break;
case SELECT:
case UNION:
case INTERSECT:
case EXCEPT:
case VALUES:
case UNNEST:
case OTHER_FUNCTION:
case COLLECTION_TABLE:
case PIVOT:
case UNPIVOT:
case MATCH_RECOGNIZE:
case WITH:
// give this anonymous construct a name since later
// query processing stages rely on it
alias = SqlValidatorUtil.alias(node, nextGeneratedId++);
if (config.identifierExpansion()) {
// Since we're expanding identifiers, we should make the
// aliases explicit too, otherwise the expanded query
// will not be consistent if we convert back to SQL, e.g.
// "select EXPR$1.EXPR$2 from values (1)".
newNode = SqlValidatorUtil.addAlias(node, alias);
}
break;
default:
break;
}
}
final SqlValidatorScope parentScope;
if (lateral) {
SqlValidatorScope s = usingScope;
while (s instanceof JoinScope) {
s = ((JoinScope) s).getUsingScope();
}
final SqlNode node2 = s != null ? s.getNode() : node;
final TableScope tableScope = new TableScope(parentScope0, node2);
if (usingScope instanceof ListScope) {
for (ScopeChild child : ((ListScope) usingScope).children) {
tableScope.addChild(child.namespace, child.name, child.nullable);
}
}
parentScope = tableScope;
} else {
parentScope = parentScope0;
}
SqlCall call;
SqlNode operand;
SqlNode newOperand;
switch (kind) {
case AS:
call = (SqlCall) node;
if (alias == null) {
alias = String.valueOf(call.operand(1));
}
expr = call.operand(0);
final boolean needAliasNamespace = call.operandCount() > 2
|| expr.getKind() == SqlKind.VALUES || expr.getKind() == SqlKind.UNNEST
|| expr.getKind() == SqlKind.COLLECTION_TABLE;
newExpr =
registerFrom(
parentScope,
usingScope,
!needAliasNamespace,
expr,
enclosingNode,
alias,
extendList,
forceNullable,
lateral);
if (newExpr != expr) {
call.setOperand(0, newExpr);
}
// If alias has a column list, introduce a namespace to translate
// column names. We skipped registering it just now.
if (needAliasNamespace) {
registerNamespace(
usingScope,
alias,
new AliasNamespace(this, call, enclosingNode),
forceNullable);
}
return node;
case MATCH_RECOGNIZE:
registerMatchRecognize(parentScope, usingScope,
(SqlMatchRecognize) node, enclosingNode, alias, forceNullable);
return node;
case PIVOT:
registerPivot(parentScope, usingScope, (SqlPivot) node, enclosingNode,
alias, forceNullable);
return node;
case UNPIVOT:
registerUnpivot(parentScope, usingScope, (SqlUnpivot) node, enclosingNode,
alias, forceNullable);
return node;
case TABLESAMPLE:
call = (SqlCall) node;
expr = call.operand(0);
newExpr =
registerFrom(
parentScope,
usingScope,
true,
expr,
enclosingNode,
alias,
extendList,
forceNullable,
lateral);
if (newExpr != expr) {
call.setOperand(0, newExpr);
}
return node;
case JOIN:
final SqlJoin join = (SqlJoin) node;
final JoinScope joinScope =
new JoinScope(parentScope, usingScope, join);
scopes.put(join, joinScope);
final SqlNode left = join.getLeft();
final SqlNode right = join.getRight();
boolean forceLeftNullable = forceNullable;
boolean forceRightNullable = forceNullable;
switch (join.getJoinType()) {
case LEFT:
case LEFT_ASOF:
forceRightNullable = true;
break;
case RIGHT:
forceLeftNullable = true;
break;
case FULL:
forceLeftNullable = true;
forceRightNullable = true;
break;
default:
break;
}
final SqlNode newLeft =
registerFrom(
parentScope,
joinScope,
true,
left,
left,
null,
null,
forceLeftNullable,
lateral);
if (newLeft != left) {
join.setLeft(newLeft);
}
final SqlNode newRight =
registerFrom(
parentScope,
joinScope,
true,
right,
right,
null,
null,
forceRightNullable,
lateral);
if (newRight != right) {
join.setRight(newRight);
}
scopes.putIfAbsent(stripAs(join.getRight()), parentScope);
scopes.putIfAbsent(stripAs(join.getLeft()), parentScope);
registerSubQueries(joinScope, join.getCondition());
final JoinNamespace joinNamespace = new JoinNamespace(this, join);
registerNamespace(null, null, joinNamespace, forceNullable);
return join;
case IDENTIFIER:
final SqlIdentifier id = (SqlIdentifier) node;
final IdentifierNamespace newNs =
new IdentifierNamespace(
this, id, extendList, enclosingNode,
parentScope);
registerNamespace(register ? usingScope : null, alias, newNs,
forceNullable);
if (tableScope == null) {
tableScope = new TableScope(parentScope, node);
}
tableScope.addChild(newNs, requireNonNull(alias, "alias"), forceNullable);
if (extendList != null && !extendList.isEmpty()) {
return enclosingNode;
}
return newNode;
case LATERAL:
return registerFrom(
parentScope,
usingScope,
register,
((SqlCall) node).operand(0),
enclosingNode,
alias,
extendList,
forceNullable,
true);
case COLLECTION_TABLE:
call = (SqlCall) node;
operand = call.operand(0);
newOperand =
registerFrom(
parentScope,
usingScope,
register,
operand,
enclosingNode,
alias,
extendList,
forceNullable, lateral);
if (newOperand != operand) {
call.setOperand(0, newOperand);
}
// If the operator is SqlWindowTableFunction, restricts the scope as
// its first operand's (the table) scope.
if (operand instanceof SqlBasicCall) {
final SqlBasicCall call1 = (SqlBasicCall) operand;
final SqlOperator op = call1.getOperator();
if (op instanceof SqlWindowTableFunction
&& call1.operand(0).getKind() == SqlKind.SELECT) {
scopes.put(node, getSelectScope(call1.operand(0)));
return newNode;
}
}
// Put the usingScope which can be a JoinScope
// or a SelectScope, in order to see the left items
// of the JOIN tree.
scopes.put(node, usingScope);
return newNode;
case UNNEST:
if (!lateral) {
return registerFrom(parentScope, usingScope, register, node,
enclosingNode, alias, extendList, forceNullable, true);
}
// fall through
case SELECT:
case UNION:
case INTERSECT:
case EXCEPT:
case VALUES:
case WITH:
case OTHER_FUNCTION:
if (alias == null) {
alias = SqlValidatorUtil.alias(node, nextGeneratedId++);
}
registerQuery(
parentScope,
register ? usingScope : null,
node,
enclosingNode,
alias,
forceNullable);
return newNode;
case OVER:
if (!shouldAllowOverRelation()) {
throw Util.unexpected(kind);
}
call = (SqlCall) node;
final OverScope overScope = new OverScope(usingScope, call);
scopes.put(call, overScope);
operand = call.operand(0);
newOperand =
registerFrom(
parentScope,
overScope,
true,
operand,
enclosingNode,
alias,
extendList,
forceNullable,
lateral);
if (newOperand != operand) {
call.setOperand(0, newOperand);
}
for (ScopeChild child : overScope.children) {
registerNamespace(register ? usingScope : null, child.name,
child.namespace, forceNullable);
}
return newNode;
case TABLE_REF:
call = (SqlCall) node;
registerFrom(parentScope,
usingScope,
register,
call.operand(0),
enclosingNode,
alias,
extendList,
forceNullable,
lateral);
if (extendList != null && !extendList.isEmpty()) {
return enclosingNode;
}
return newNode;
case EXTEND:
final SqlCall extend = (SqlCall) node;
return registerFrom(parentScope,
usingScope,
true,
extend.getOperandList().get(0),
extend,
alias,
(SqlNodeList) extend.getOperandList().get(1),
forceNullable,
lateral);
case SNAPSHOT:
call = (SqlCall) node;
operand = call.operand(0);
newOperand =
registerFrom(parentScope,
usingScope,
register,
operand,
enclosingNode,
alias,
extendList,
forceNullable,
lateral);
if (newOperand != operand) {
call.setOperand(0, newOperand);
}
// Put the usingScope which can be a JoinScope
// or a SelectScope, in order to see the left items
// of the JOIN tree.
scopes.put(node, usingScope);
return newNode;
default:
throw Util.unexpected(kind);
}
}