use of org.apache.calcite.sql.SqlNodeList in project calcite by apache.
the class SqlValidatorImpl method validateOrderList.
/**
* Validates the ORDER BY clause of a SELECT statement.
*
* @param select Select statement
*/
protected void validateOrderList(SqlSelect select) {
// ORDER BY is validated in a scope where aliases in the SELECT clause
// are visible. For example, "SELECT empno AS x FROM emp ORDER BY x"
// is valid.
SqlNodeList orderList = select.getOrderList();
if (orderList == null) {
return;
}
if (!shouldAllowIntermediateOrderBy()) {
if (!cursorSet.contains(select)) {
throw newValidationError(select, RESOURCE.invalidOrderByPos());
}
}
final SqlValidatorScope orderScope = getOrderScope(select);
Preconditions.checkNotNull(orderScope);
List<SqlNode> expandList = new ArrayList<>();
for (SqlNode orderItem : orderList) {
SqlNode expandedOrderItem = expand(orderItem, orderScope);
expandList.add(expandedOrderItem);
}
SqlNodeList expandedOrderList = new SqlNodeList(expandList, orderList.getParserPosition());
select.setOrderBy(expandedOrderList);
for (SqlNode orderItem : expandedOrderList) {
validateOrderItem(select, orderItem);
}
}
use of org.apache.calcite.sql.SqlNodeList in project calcite by apache.
the class SqlValidatorImpl method registerSubQueries.
private void registerSubQueries(SqlValidatorScope parentScope, SqlNode node) {
if (node == null) {
return;
}
if (node.getKind().belongsTo(SqlKind.QUERY) || node.getKind() == SqlKind.MULTISET_QUERY_CONSTRUCTOR || node.getKind() == SqlKind.MULTISET_VALUE_CONSTRUCTOR) {
registerQuery(parentScope, null, node, node, null, false);
} else if (node instanceof SqlCall) {
validateNodeFeature(node);
SqlCall call = (SqlCall) node;
for (int i = 0; i < call.operandCount(); i++) {
registerOperandSubQueries(parentScope, call, i);
}
} else if (node instanceof SqlNodeList) {
SqlNodeList list = (SqlNodeList) node;
for (int i = 0, count = list.size(); i < count; i++) {
SqlNode listNode = list.get(i);
if (listNode.getKind().belongsTo(SqlKind.QUERY)) {
listNode = SqlStdOperatorTable.SCALAR_QUERY.createCall(listNode.getParserPosition(), listNode);
list.set(i, listNode);
}
registerSubQueries(parentScope, listNode);
}
} else {
// atomic node -- can be ignored
}
}
use of org.apache.calcite.sql.SqlNodeList in project calcite by apache.
the class SqlValidatorImpl method validateModality.
public boolean validateModality(SqlSelect select, SqlModality modality, boolean fail) {
final SelectScope scope = getRawSelectScope(select);
switch(modality) {
case STREAM:
if (scope.children.size() == 1) {
for (ScopeChild child : scope.children) {
if (!child.namespace.supportsModality(modality)) {
if (fail) {
throw newValidationError(child.namespace.getNode(), Static.RESOURCE.cannotConvertToStream(child.name));
} else {
return false;
}
}
}
} else {
int supportsModalityCount = 0;
for (ScopeChild child : scope.children) {
if (child.namespace.supportsModality(modality)) {
++supportsModalityCount;
}
}
if (supportsModalityCount == 0) {
if (fail) {
String inputs = Joiner.on(", ").join(scope.getChildNames());
throw newValidationError(select, Static.RESOURCE.cannotStreamResultsForNonStreamingInputs(inputs));
} else {
return false;
}
}
}
break;
default:
for (ScopeChild child : scope.children) {
if (!child.namespace.supportsModality(modality)) {
if (fail) {
throw newValidationError(child.namespace.getNode(), Static.RESOURCE.cannotConvertToRelation(child.name));
} else {
return false;
}
}
}
}
// Make sure that aggregation is possible.
final SqlNode aggregateNode = getAggregate(select);
if (aggregateNode != null) {
switch(modality) {
case STREAM:
SqlNodeList groupList = select.getGroup();
if (groupList == null || !SqlValidatorUtil.containsMonotonic(scope, groupList)) {
if (fail) {
throw newValidationError(aggregateNode, Static.RESOURCE.streamMustGroupByMonotonic());
} else {
return false;
}
}
}
}
// Make sure that ORDER BY is possible.
final SqlNodeList orderList = select.getOrderList();
if (orderList != null && orderList.size() > 0) {
switch(modality) {
case STREAM:
if (!hasSortedPrefix(scope, orderList)) {
if (fail) {
throw newValidationError(orderList.get(0), Static.RESOURCE.streamMustOrderByMonotonic());
} else {
return false;
}
}
}
}
return true;
}
use of org.apache.calcite.sql.SqlNodeList in project calcite 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 (int i = 0; i < list.size(); i++) {
SqlIdentifier id = (SqlIdentifier) list.get(i);
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);
checkRollUpInUsing(id, right);
}
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();
List<String> naturalColumnNames = SqlValidatorUtil.deriveNaturalJoinColumnList(leftRowType, rightRowType);
// Check compatibility of the chosen columns.
final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
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 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.SqlNodeList in project calcite by apache.
the class SqlValidatorImpl method registerQuery.
/**
* Registers a query in a parent scope.
*
* @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 Query node
* @param alias Name of this query within its parent. Must be specified
* if usingScope != null
* @param checkUpdate if true, validate that the update feature is supported
* if validating the update statement
*/
private void registerQuery(SqlValidatorScope parentScope, SqlValidatorScope usingScope, SqlNode node, SqlNode enclosingNode, String alias, boolean forceNullable, boolean checkUpdate) {
Preconditions.checkNotNull(node);
Preconditions.checkNotNull(enclosingNode);
Preconditions.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 = (usingScope != null) ? usingScope : parentScope;
SelectScope selectScope = new SelectScope(parentScope, windowParentScope, select);
scopes.put(select, selectScope);
// Start by registering the WHERE clause
whereScopes.put(select, selectScope);
registerOperandSubQueries(selectScope, select, SqlSelect.WHERE_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, from, from, null, null, false);
if (newFrom != from) {
select.setFrom(newFrom);
}
}
// If this is an aggregating query, the SELECT list and HAVING
// clause use a different scope, where you can only reference
// columns which are in the GROUP BY clause.
SqlValidatorScope aggScope = selectScope;
if (isAggregate(select)) {
aggScope = new AggregatingSelectScope(selectScope, select, false);
selectScopes.put(select, aggScope);
} else {
selectScopes.put(select, selectScope);
}
if (select.getGroup() != null) {
GroupByScope groupByScope = new GroupByScope(selectScope, select.getGroup(), select);
groupByScopes.put(select, groupByScope);
registerSubQueries(groupByScope, select.getGroup());
}
registerOperandSubQueries(aggScope, select, SqlSelect.HAVING_OPERAND);
registerSubQueries(aggScope, select.getSelectList());
final SqlNodeList orderList = select.getOrderList();
if (orderList != null) {
// available to the ORDER BY clause.
if (select.isDistinct()) {
aggScope = new AggregatingSelectScope(selectScope, select, true);
}
OrderByScope orderScope = new OrderByScope(aggScope, orderList, select);
orderScopes.put(select, orderScope);
registerSubQueries(orderScope, orderList);
if (!isAggregate(select)) {
// Since this is not an aggregating 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, node, alias, forceNullable);
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, deleteCall.getSourceSelect(), 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, updateCall.getSourceSelect(), 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, mergeCall.getSourceSelect(), enclosingNode, null, false);
// validation check
if (mergeCall.getUpdateCall() != null) {
registerQuery(whereScopes.get(mergeCall.getSourceSelect()), null, mergeCall.getUpdateCall(), enclosingNode, null, false, false);
}
if (mergeCall.getInsertCall() != null) {
registerQuery(parentScope, null, mergeCall.getInsertCall(), 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 = deriveAlias(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());
}
}
Aggregations