use of org.apache.calcite.sql.SqlCall in project calcite 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 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
* @return registered node, usually the same as {@code node}
*/
private SqlNode registerFrom(SqlValidatorScope parentScope, SqlValidatorScope usingScope, final SqlNode node, SqlNode enclosingNode, String alias, SqlNodeList extendList, boolean forceNullable) {
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 (shouldExpandIdentifiers()) {
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 MATCH_RECOGNIZE:
// give this anonymous construct a name since later
// query processing stages rely on it
alias = deriveAlias(node, nextGeneratedId++);
if (shouldExpandIdentifiers()) {
// 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;
}
}
SqlCall call;
SqlNode operand;
SqlNode newOperand;
switch(kind) {
case AS:
call = (SqlCall) node;
if (alias == null) {
alias = call.operand(1).toString();
}
SqlValidatorScope usingScope2 = usingScope;
if (call.operandCount() > 2) {
usingScope2 = null;
}
expr = call.operand(0);
newExpr = registerFrom(parentScope, usingScope2, expr, enclosingNode, alias, extendList, forceNullable);
if (newExpr != expr) {
call.setOperand(0, newExpr);
}
// column names.
if (call.operandCount() > 2) {
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 TABLESAMPLE:
call = (SqlCall) node;
expr = call.operand(0);
newExpr = registerFrom(parentScope, usingScope, expr, enclosingNode, alias, extendList, forceNullable);
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();
final boolean rightIsLateral = isLateral(right);
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, left, left, null, null, forceLeftNullable);
if (newLeft != left) {
join.setLeft(newLeft);
}
final SqlValidatorScope rightParentScope;
if (rightIsLateral) {
rightParentScope = joinScope;
} else {
rightParentScope = parentScope;
}
final SqlNode newRight = registerFrom(rightParentScope, joinScope, right, right, null, null, forceRightNullable);
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(usingScope, 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:
if (tableScope != null) {
tableScope.meetLateral();
}
return registerFrom(parentScope, usingScope, ((SqlCall) node).operand(0), enclosingNode, alias, extendList, forceNullable);
case COLLECTION_TABLE:
call = (SqlCall) node;
operand = call.operand(0);
newOperand = registerFrom(tableScope == null ? parentScope : tableScope, usingScope, operand, enclosingNode, alias, extendList, forceNullable);
if (newOperand != operand) {
call.setOperand(0, newOperand);
}
scopes.put(node, parentScope);
return newNode;
case SELECT:
case UNION:
case INTERSECT:
case EXCEPT:
case VALUES:
case WITH:
case UNNEST:
case OTHER_FUNCTION:
if (alias == null) {
alias = deriveAlias(node, nextGeneratedId++);
}
registerQuery(parentScope, usingScope, 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, operand, enclosingNode, alias, extendList, forceNullable);
if (newOperand != operand) {
call.setOperand(0, newOperand);
}
for (ScopeChild child : overScope.children) {
registerNamespace(usingScope, child.name, child.namespace, forceNullable);
}
return newNode;
case EXTEND:
final SqlCall extend = (SqlCall) node;
return registerFrom(parentScope, usingScope, extend.getOperandList().get(0), extend, alias, (SqlNodeList) extend.getOperandList().get(1), forceNullable);
default:
throw Util.unexpected(kind);
}
}
use of org.apache.calcite.sql.SqlCall in project calcite by apache.
the class SqlValidatorImpl method validateMatchRecognize.
@Override
public void validateMatchRecognize(SqlCall call) {
final SqlMatchRecognize matchRecognize = (SqlMatchRecognize) call;
final MatchRecognizeScope scope = (MatchRecognizeScope) getMatchRecognizeScope(matchRecognize);
final MatchRecognizeNamespace ns = getNamespace(call).unwrap(MatchRecognizeNamespace.class);
assert ns.rowType == null;
// rows per match
final SqlLiteral rowsPerMatch = matchRecognize.getRowsPerMatch();
final boolean allRows = rowsPerMatch != null && rowsPerMatch.getValue() == SqlMatchRecognize.RowsPerMatchOption.ALL_ROWS;
final RelDataTypeFactory.Builder typeBuilder = typeFactory.builder();
// parse PARTITION BY column
SqlNodeList partitionBy = matchRecognize.getPartitionList();
if (partitionBy != null) {
for (SqlNode node : partitionBy) {
SqlIdentifier identifier = (SqlIdentifier) node;
identifier.validate(this, scope);
RelDataType type = deriveType(scope, identifier);
String name = identifier.names.get(1);
typeBuilder.add(name, type);
}
}
// parse ORDER BY column
SqlNodeList orderBy = matchRecognize.getOrderList();
if (orderBy != null) {
for (SqlNode node : orderBy) {
node.validate(this, scope);
SqlIdentifier identifier = null;
if (node instanceof SqlBasicCall) {
identifier = (SqlIdentifier) ((SqlBasicCall) node).getOperands()[0];
} else {
identifier = (SqlIdentifier) node;
}
if (allRows) {
RelDataType type = deriveType(scope, identifier);
String name = identifier.names.get(1);
if (!typeBuilder.nameExists(name)) {
typeBuilder.add(name, type);
}
}
}
}
if (allRows) {
final SqlValidatorNamespace sqlNs = getNamespace(matchRecognize.getTableRef());
final RelDataType inputDataType = sqlNs.getRowType();
for (RelDataTypeField fs : inputDataType.getFieldList()) {
if (!typeBuilder.nameExists(fs.getName())) {
typeBuilder.add(fs);
}
}
}
// retrieve pattern variables used in pattern and subset
SqlNode pattern = matchRecognize.getPattern();
PatternVarVisitor visitor = new PatternVarVisitor(scope);
pattern.accept(visitor);
SqlLiteral interval = matchRecognize.getInterval();
if (interval != null) {
interval.validate(this, scope);
if (((SqlIntervalLiteral) interval).signum() < 0) {
throw newValidationError(interval, RESOURCE.intervalMustBeNonNegative(interval.toValue()));
}
if (orderBy == null || orderBy.size() == 0) {
throw newValidationError(interval, RESOURCE.cannotUseWithinWithoutOrderBy());
}
SqlNode firstOrderByColumn = orderBy.getList().get(0);
SqlIdentifier identifier;
if (firstOrderByColumn instanceof SqlBasicCall) {
identifier = (SqlIdentifier) ((SqlBasicCall) firstOrderByColumn).getOperands()[0];
} else {
identifier = (SqlIdentifier) firstOrderByColumn;
}
RelDataType firstOrderByColumnType = deriveType(scope, identifier);
if (firstOrderByColumnType.getSqlTypeName() != SqlTypeName.TIMESTAMP) {
throw newValidationError(interval, RESOURCE.firstColumnOfOrderByMustBeTimestamp());
}
SqlNode expand = expand(interval, scope);
RelDataType type = deriveType(scope, expand);
setValidatedNodeType(interval, type);
}
validateDefinitions(matchRecognize, scope);
SqlNodeList subsets = matchRecognize.getSubsetList();
if (subsets != null && subsets.size() > 0) {
for (SqlNode node : subsets) {
List<SqlNode> operands = ((SqlCall) node).getOperandList();
String leftString = ((SqlIdentifier) operands.get(0)).getSimple();
if (scope.getPatternVars().contains(leftString)) {
throw newValidationError(operands.get(0), RESOURCE.patternVarAlreadyDefined(leftString));
}
scope.addPatternVar(leftString);
for (SqlNode right : (SqlNodeList) operands.get(1)) {
SqlIdentifier id = (SqlIdentifier) right;
if (!scope.getPatternVars().contains(id.getSimple())) {
throw newValidationError(id, RESOURCE.unknownPattern(id.getSimple()));
}
scope.addPatternVar(id.getSimple());
}
}
}
// validate AFTER ... SKIP TO
final SqlNode skipTo = matchRecognize.getAfter();
if (skipTo instanceof SqlCall) {
final SqlCall skipToCall = (SqlCall) skipTo;
final SqlIdentifier id = skipToCall.operand(0);
if (!scope.getPatternVars().contains(id.getSimple())) {
throw newValidationError(id, RESOURCE.unknownPattern(id.getSimple()));
}
}
List<Map.Entry<String, RelDataType>> measureColumns = validateMeasure(matchRecognize, scope, allRows);
for (Map.Entry<String, RelDataType> c : measureColumns) {
if (!typeBuilder.nameExists(c.getKey())) {
typeBuilder.add(c.getKey(), c.getValue());
}
}
final RelDataType rowType = typeBuilder.build();
if (matchRecognize.getMeasureList().size() == 0) {
ns.setType(getNamespace(matchRecognize.getTableRef()).getRowType());
} else {
ns.setType(rowType);
}
}
use of org.apache.calcite.sql.SqlCall in project calcite by apache.
the class SqlValidatorImpl method validateModality.
/**
* Validates that a query can deliver the modality it promises. Only called
* on the top-most SELECT or set operator in the tree.
*/
private void validateModality(SqlNode query) {
final SqlModality modality = deduceModality(query);
if (query instanceof SqlSelect) {
final SqlSelect select = (SqlSelect) query;
validateModality(select, modality, true);
} else if (query.getKind() == SqlKind.VALUES) {
switch(modality) {
case STREAM:
throw newValidationError(query, Static.RESOURCE.cannotStreamValues());
}
} else {
assert query.isA(SqlKind.SET_QUERY);
final SqlCall call = (SqlCall) query;
for (SqlNode operand : call.getOperandList()) {
if (deduceModality(operand) != modality) {
throw newValidationError(operand, Static.RESOURCE.streamSetOpInconsistentInputs());
}
validateModality(operand);
}
}
}
use of org.apache.calcite.sql.SqlCall in project calcite by apache.
the class SqlValidatorImpl method checkRollUp.
private void checkRollUp(SqlNode grandParent, SqlNode parent, SqlNode current, SqlValidatorScope scope, String optionalClause) {
current = stripAs(current);
if (current instanceof SqlCall && !(current instanceof SqlSelect)) {
// Validate OVER separately
checkRollUpInWindow(getWindowInOver(current), scope);
current = stripOver(current);
List<SqlNode> children = ((SqlCall) stripDot(current)).getOperandList();
for (SqlNode child : children) {
checkRollUp(parent, current, child, scope, optionalClause);
}
} else if (current instanceof SqlIdentifier) {
SqlIdentifier id = (SqlIdentifier) current;
if (!id.isStar() && isRolledUpColumn(id, scope)) {
if (!isAggregation(parent.getKind()) || !isRolledUpColumnAllowedInAgg(id, scope, (SqlCall) parent, grandParent)) {
String context = optionalClause != null ? optionalClause : parent.getKind().toString();
throw newValidationError(id, RESOURCE.rolledUpNotAllowed(deriveAlias(id, 0), context));
}
}
}
}
use of org.apache.calcite.sql.SqlCall in project calcite by apache.
the class SqlValidatorImpl method validateMeasure.
private List<Map.Entry<String, RelDataType>> validateMeasure(SqlMatchRecognize mr, MatchRecognizeScope scope, boolean allRows) {
final List<String> aliases = new ArrayList<>();
final List<SqlNode> sqlNodes = new ArrayList<>();
final SqlNodeList measures = mr.getMeasureList();
final List<Map.Entry<String, RelDataType>> fields = new ArrayList<>();
for (SqlNode measure : measures) {
assert measure instanceof SqlCall;
final String alias = deriveAlias(measure, aliases.size());
aliases.add(alias);
SqlNode expand = expand(measure, scope);
expand = navigationInMeasure(expand, allRows);
setOriginal(expand, measure);
inferUnknownTypes(unknownType, scope, expand);
final RelDataType type = deriveType(scope, expand);
setValidatedNodeType(measure, type);
fields.add(Pair.of(alias, type));
sqlNodes.add(SqlStdOperatorTable.AS.createCall(SqlParserPos.ZERO, expand, new SqlIdentifier(alias, SqlParserPos.ZERO)));
}
SqlNodeList list = new SqlNodeList(sqlNodes, measures.getParserPosition());
inferUnknownTypes(unknownType, scope, list);
for (SqlNode node : list) {
validateExpr(node, scope);
}
mr.setOperand(SqlMatchRecognize.OPERAND_MEASURES, list);
return fields;
}
Aggregations