use of org.apache.calcite.sql.SqlIdentifier in project flink by apache.
the class SqlValidatorImpl method validateJoin.
protected void validateJoin(SqlJoin join, SqlValidatorScope scope) {
SqlNode left = join.getLeft();
SqlNode right = join.getRight();
SqlNode condition = join.getCondition();
boolean natural = join.isNatural();
final JoinType joinType = join.getJoinType();
final JoinConditionType conditionType = join.getConditionType();
final SqlValidatorScope joinScope = scopes.get(join);
validateFrom(left, unknownType, joinScope);
validateFrom(right, unknownType, joinScope);
// Validate condition.
switch(conditionType) {
case NONE:
Preconditions.checkArgument(condition == null);
break;
case ON:
Preconditions.checkArgument(condition != null);
SqlNode expandedCondition = expand(condition, joinScope);
join.setOperand(5, expandedCondition);
condition = join.getCondition();
validateWhereOrOn(joinScope, condition, "ON");
checkRollUp(null, join, condition, joinScope, "ON");
break;
case USING:
SqlNodeList list = (SqlNodeList) condition;
// Parser ensures that using clause is not empty.
Preconditions.checkArgument(list.size() > 0, "Empty USING clause");
for (SqlNode node : list) {
SqlIdentifier id = (SqlIdentifier) node;
final RelDataType leftColType = validateUsingCol(id, left);
final RelDataType rightColType = validateUsingCol(id, right);
if (!SqlTypeUtil.isComparable(leftColType, rightColType)) {
throw newValidationError(id, RESOURCE.naturalOrUsingColumnNotCompatible(id.getSimple(), leftColType.toString(), rightColType.toString()));
}
checkRollUpInUsing(id, left, scope);
checkRollUpInUsing(id, right, scope);
}
break;
default:
throw Util.unexpected(conditionType);
}
// Validate NATURAL.
if (natural) {
if (condition != null) {
throw newValidationError(condition, RESOURCE.naturalDisallowsOnOrUsing());
}
// Join on fields that occur exactly once on each side. Ignore
// fields that occur more than once on either side.
final RelDataType leftRowType = getNamespace(left).getRowType();
final RelDataType rightRowType = getNamespace(right).getRowType();
final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
List<String> naturalColumnNames = SqlValidatorUtil.deriveNaturalJoinColumnList(nameMatcher, leftRowType, rightRowType);
// Check compatibility of the chosen columns.
for (String name : naturalColumnNames) {
final RelDataType leftColType = nameMatcher.field(leftRowType, name).getType();
final RelDataType rightColType = nameMatcher.field(rightRowType, name).getType();
if (!SqlTypeUtil.isComparable(leftColType, rightColType)) {
throw newValidationError(join, RESOURCE.naturalOrUsingColumnNotCompatible(name, leftColType.toString(), rightColType.toString()));
}
}
}
// a NATURAL keyword?
switch(joinType) {
case LEFT_SEMI_JOIN:
if (!this.config.sqlConformance().isLiberal()) {
throw newValidationError(join.getJoinTypeNode(), RESOURCE.dialectDoesNotSupportFeature("LEFT SEMI JOIN"));
}
// fall through
case INNER:
case LEFT:
case RIGHT:
case FULL:
if ((condition == null) && !natural) {
throw newValidationError(join, RESOURCE.joinRequiresCondition());
}
break;
case COMMA:
case CROSS:
if (condition != null) {
throw newValidationError(join.getConditionTypeNode(), RESOURCE.crossJoinDisallowsCondition());
}
if (natural) {
throw newValidationError(join.getConditionTypeNode(), RESOURCE.crossJoinDisallowsCondition());
}
break;
default:
throw Util.unexpected(joinType);
}
}
use of org.apache.calcite.sql.SqlIdentifier in project flink by apache.
the class SqlValidatorImpl method lookupFromHints.
private void lookupFromHints(SqlNode node, SqlValidatorScope scope, SqlParserPos pos, Collection<SqlMoniker> hintList) {
if (node == null) {
// This can happen in cases like "select * _suggest_", so from clause is absent
return;
}
final SqlValidatorNamespace ns = getNamespace(node);
if (ns.isWrapperFor(IdentifierNamespace.class)) {
IdentifierNamespace idNs = ns.unwrap(IdentifierNamespace.class);
final SqlIdentifier id = idNs.getId();
for (int i = 0; i < id.names.size(); i++) {
if (pos.toString().equals(id.getComponent(i).getParserPosition().toString())) {
final List<SqlMoniker> objNames = new ArrayList<>();
SqlValidatorUtil.getSchemaObjectMonikers(getCatalogReader(), id.names.subList(0, i + 1), objNames);
for (SqlMoniker objName : objNames) {
if (objName.getType() != SqlMonikerType.FUNCTION) {
hintList.add(objName);
}
}
return;
}
}
}
switch(node.getKind()) {
case JOIN:
lookupJoinHints((SqlJoin) node, scope, pos, hintList);
break;
default:
lookupSelectHints(ns, pos, hintList);
break;
}
}
use of org.apache.calcite.sql.SqlIdentifier in project flink by apache.
the class SqlValidatorImpl method expandExprFromJoin.
private static SqlNode expandExprFromJoin(SqlJoin join, SqlIdentifier identifier, SelectScope scope) {
if (join.getConditionType() != JoinConditionType.USING) {
return identifier;
}
for (SqlNode node : (SqlNodeList) join.getCondition()) {
final String name = ((SqlIdentifier) node).getSimple();
if (identifier.getSimple().equals(name)) {
final List<SqlNode> qualifiedNode = new ArrayList<>();
for (ScopeChild child : scope.children) {
if (child.namespace.getRowType().getFieldNames().indexOf(name) >= 0) {
final SqlIdentifier exp = new SqlIdentifier(ImmutableList.of(child.name, name), identifier.getParserPosition());
qualifiedNode.add(exp);
}
}
assert qualifiedNode.size() == 2;
final SqlNode finalNode = SqlStdOperatorTable.AS.createCall(SqlParserPos.ZERO, SqlStdOperatorTable.COALESCE.createCall(SqlParserPos.ZERO, qualifiedNode.get(0), qualifiedNode.get(1)), new SqlIdentifier(name, SqlParserPos.ZERO));
return finalNode;
}
}
// Only need to try to expand the expr from the left input of join
// since it is always left-deep join.
final SqlNode node = join.getLeft();
if (node instanceof SqlJoin) {
return expandExprFromJoin((SqlJoin) node, identifier, scope);
} else {
return identifier;
}
}
use of org.apache.calcite.sql.SqlIdentifier in project flink by apache.
the class SqlValidatorImpl method registerFrom.
/**
* Registers scopes and namespaces implied a relational expression in the FROM clause.
*
* <p>{@code parentScope} and {@code usingScope} are often the same. They differ when the
* namespace are not visible within the parent. (Example needed.)
*
* <p>Likewise, {@code enclosingNode} and {@code node} are often the same. {@code enclosingNode}
* is the topmost node within the FROM clause, from which any decorations like an alias (<code>
* AS alias</code>) or a table sample clause are stripped away to get {@code node}. Both are
* recorded in the namespace.
*
* @param parentScope Parent scope which this scope turns to in order to resolve objects
* @param usingScope Scope whose child list this scope should add itself to
* @param register Whether to register this scope as a child of {@code usingScope}
* @param node Node which namespace is based on
* @param enclosingNode Outermost node for namespace, including decorations such as alias and
* sample clause
* @param alias Alias
* @param extendList Definitions of extended columns
* @param forceNullable Whether to force the type of namespace to be nullable because it is in
* an outer join
* @param lateral Whether LATERAL is specified, so that items to the left of this in the JOIN
* tree are visible in the scope
* @return registered node, usually the same as {@code node}
*/
private SqlNode registerFrom(SqlValidatorScope parentScope, SqlValidatorScope usingScope, boolean register, final SqlNode node, SqlNode enclosingNode, String alias, 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 = deriveAlias(node, -1);
if (alias == null) {
alias = deriveAlias(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 MATCH_RECOGNIZE:
// give this anonymous construct a name since later
// query processing stages rely on it
alias = deriveAlias(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;
}
}
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(parentScope, node2);
if (usingScope instanceof ListScope) {
for (ScopeChild child : ((ListScope) usingScope).children) {
tableScope.addChild(child.namespace, child.name, child.nullable);
}
}
parentScope = tableScope;
}
SqlCall call;
SqlNode operand;
SqlNode newOperand;
switch(kind) {
case AS:
call = (SqlCall) node;
if (alias == null) {
alias = call.operand(1).toString();
}
final boolean needAlias = call.operandCount() > 2;
expr = call.operand(0);
newExpr = registerFrom(parentScope, usingScope, !needAlias, expr, enclosingNode, alias, extendList, forceNullable, lateral);
if (newExpr != expr) {
call.setOperand(0, newExpr);
}
// column names. We skipped registering it just now.
if (needAlias) {
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 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:
forceRightNullable = true;
break;
case RIGHT:
forceLeftNullable = true;
break;
case FULL:
forceLeftNullable = true;
forceRightNullable = true;
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);
}
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, alias, forceNullable);
if (extendList != null && extendList.size() != 0) {
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);
}
// 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 = deriveAlias(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.size() != 0) {
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);
}
}
use of org.apache.calcite.sql.SqlIdentifier in project flink by apache.
the class SqlValidatorImpl method resolveWindow.
public SqlWindow resolveWindow(SqlNode windowOrRef, SqlValidatorScope scope) {
SqlWindow window;
if (windowOrRef instanceof SqlIdentifier) {
window = getWindowByName((SqlIdentifier) windowOrRef, scope);
} else {
window = (SqlWindow) windowOrRef;
}
while (true) {
final SqlIdentifier refId = window.getRefName();
if (refId == null) {
break;
}
final String refName = refId.getSimple();
SqlWindow refWindow = scope.lookupWindow(refName);
if (refWindow == null) {
throw newValidationError(refId, RESOURCE.windowNotFound(refName));
}
window = window.overlay(refWindow, this);
}
return window;
}
Aggregations