use of org.apache.calcite.sql.SqlBasicCall in project calcite by apache.
the class SqlToRelConverter method substituteSubQuery.
private void substituteSubQuery(Blackboard bb, SubQuery subQuery) {
final RexNode expr = subQuery.expr;
if (expr != null) {
// Already done.
return;
}
final SqlBasicCall call;
final RelNode rel;
final SqlNode query;
final RelOptUtil.Exists converted;
switch(subQuery.node.getKind()) {
case CURSOR:
convertCursor(bb, subQuery);
return;
case ARRAY_QUERY_CONSTRUCTOR:
case MAP_QUERY_CONSTRUCTOR:
case MULTISET_QUERY_CONSTRUCTOR:
if (!config.isExpand()) {
return;
}
// fall through
case MULTISET_VALUE_CONSTRUCTOR:
rel = convertMultisets(ImmutableList.of(subQuery.node), bb);
subQuery.expr = bb.register(rel, JoinRelType.INNER);
return;
case IN:
case NOT_IN:
case SOME:
case ALL:
call = (SqlBasicCall) subQuery.node;
query = call.operand(1);
if (!config.isExpand() && !(query instanceof SqlNodeList)) {
return;
}
final SqlNode leftKeyNode = call.operand(0);
final List<RexNode> leftKeys;
switch(leftKeyNode.getKind()) {
case ROW:
leftKeys = new ArrayList<>();
for (SqlNode sqlExpr : ((SqlBasicCall) leftKeyNode).getOperandList()) {
leftKeys.add(bb.convertExpression(sqlExpr));
}
break;
default:
leftKeys = ImmutableList.of(bb.convertExpression(leftKeyNode));
}
if (query instanceof SqlNodeList) {
SqlNodeList valueList = (SqlNodeList) query;
// When the list size under the threshold or the list references columns, we convert to OR.
if (valueList.size() < config.getInSubQueryThreshold() || valueList.accept(new SqlIdentifierFinder())) {
subQuery.expr = convertInToOr(bb, leftKeys, valueList, (SqlInOperator) call.getOperator());
return;
}
// Otherwise, let convertExists translate
// values list into an inline table for the
// reference to Q below.
}
// and it makes no sense to do the sub-query substitution.
if (bb.root == null) {
return;
}
final RelDataType targetRowType = SqlTypeUtil.promoteToRowType(typeFactory, validator().getValidatedNodeType(leftKeyNode), null);
final boolean notIn = call.getOperator().kind == SqlKind.NOT_IN;
converted = convertExists(query, RelOptUtil.SubQueryType.IN, subQuery.logic, notIn, targetRowType);
if (converted.indicator) {
// Generate
// emp CROSS JOIN (SELECT COUNT(*) AS c,
// COUNT(deptno) AS ck FROM dept)
final RelDataType longType = typeFactory.createSqlType(SqlTypeName.BIGINT);
// fragile
final RelNode seek = converted.r.getInput(0);
final int keyCount = leftKeys.size();
final List<Integer> args = ImmutableIntList.range(0, keyCount);
LogicalAggregate aggregate = LogicalAggregate.create(seek, ImmutableList.of(), ImmutableBitSet.of(), null, ImmutableList.of(AggregateCall.create(SqlStdOperatorTable.COUNT, false, false, false, ImmutableList.of(), -1, null, RelCollations.EMPTY, longType, null), AggregateCall.create(SqlStdOperatorTable.COUNT, false, false, false, args, -1, null, RelCollations.EMPTY, longType, null)));
LogicalJoin join = LogicalJoin.create(bb.root(), aggregate, ImmutableList.of(), rexBuilder.makeLiteral(true), ImmutableSet.of(), JoinRelType.INNER);
bb.setRoot(join, false);
}
final RexNode rex = bb.register(converted.r, converted.outerJoin ? JoinRelType.LEFT : JoinRelType.INNER, leftKeys);
RelOptUtil.Logic logic = subQuery.logic;
switch(logic) {
case TRUE_FALSE_UNKNOWN:
case UNKNOWN_AS_TRUE:
if (!converted.indicator) {
logic = RelOptUtil.Logic.TRUE_FALSE;
}
break;
default:
break;
}
subQuery.expr = translateIn(logic, bb.root, rex);
if (notIn) {
subQuery.expr = rexBuilder.makeCall(SqlStdOperatorTable.NOT, subQuery.expr);
}
return;
case EXISTS:
// boolean indicating whether the sub-query returned 0 or >= 1 row.
if (!config.isExpand()) {
return;
}
call = (SqlBasicCall) subQuery.node;
query = call.operand(0);
final SqlValidatorScope seekScope = (query instanceof SqlSelect) ? validator().getSelectScope((SqlSelect) query) : null;
final Blackboard seekBb = createBlackboard(seekScope, null, false);
final RelNode seekRel = convertQueryOrInList(seekBb, query, null);
requireNonNull(seekRel, () -> "seekRel is null for query " + query);
// An EXIST sub-query whose inner child has at least 1 tuple
// (e.g. an Aggregate with no grouping columns or non-empty Values
// node) should be simplified to a Boolean constant expression.
final RelMetadataQuery mq = seekRel.getCluster().getMetadataQuery();
final Double minRowCount = mq.getMinRowCount(seekRel);
if (minRowCount != null && minRowCount >= 1D) {
subQuery.expr = rexBuilder.makeLiteral(true);
return;
}
converted = RelOptUtil.createExistsPlan(seekRel, RelOptUtil.SubQueryType.EXISTS, subQuery.logic, true, relBuilder);
assert !converted.indicator;
if (convertNonCorrelatedSubQuery(subQuery, bb, converted.r, true)) {
return;
}
subQuery.expr = bb.register(converted.r, JoinRelType.LEFT);
return;
case UNIQUE:
return;
case SCALAR_QUERY:
// to a constant expression.
if (!config.isExpand()) {
return;
}
call = (SqlBasicCall) subQuery.node;
query = call.operand(0);
converted = convertExists(query, RelOptUtil.SubQueryType.SCALAR, subQuery.logic, true, null);
assert !converted.indicator;
if (convertNonCorrelatedSubQuery(subQuery, bb, converted.r, false)) {
return;
}
rel = convertToSingleValueSubq(query, converted.r);
subQuery.expr = bb.register(rel, JoinRelType.LEFT);
return;
case SELECT:
// This is used when converting multiset queries:
//
// select * from unnest(select multiset[deptno] from emps);
//
converted = convertExists(subQuery.node, RelOptUtil.SubQueryType.SCALAR, subQuery.logic, true, null);
assert !converted.indicator;
subQuery.expr = bb.register(converted.r, JoinRelType.LEFT);
// This is used when converting window table functions:
//
// select * from table(tumble(table emps, descriptor(deptno), interval '3' DAY))
//
bb.cursors.add(converted.r);
return;
default:
throw new AssertionError("unexpected kind of sub-query: " + subQuery.node);
}
}
use of org.apache.calcite.sql.SqlBasicCall in project calcite by apache.
the class SqlToRelConverter method convertInToOr.
/**
* Converts "x IN (1, 2, ...)" to "x=1 OR x=2 OR ...".
*
* @param leftKeys LHS
* @param valuesList RHS
* @param op The operator (IN, NOT IN, > SOME, ...)
* @return converted expression
*/
@Nullable
private RexNode convertInToOr(final Blackboard bb, final List<RexNode> leftKeys, SqlNodeList valuesList, SqlInOperator op) {
final List<RexNode> comparisons = new ArrayList<>();
for (SqlNode rightVals : valuesList) {
RexNode rexComparison;
final SqlOperator comparisonOp;
if (op instanceof SqlQuantifyOperator) {
comparisonOp = RelOptUtil.op(((SqlQuantifyOperator) op).comparisonKind, SqlStdOperatorTable.EQUALS);
} else {
comparisonOp = SqlStdOperatorTable.EQUALS;
}
if (leftKeys.size() == 1) {
rexComparison = rexBuilder.makeCall(comparisonOp, leftKeys.get(0), ensureSqlType(leftKeys.get(0).getType(), bb.convertExpression(rightVals)));
} else {
assert rightVals instanceof SqlCall;
final SqlBasicCall call = (SqlBasicCall) rightVals;
assert (call.getOperator() instanceof SqlRowOperator) && call.operandCount() == leftKeys.size();
rexComparison = RexUtil.composeConjunction(rexBuilder, Util.transform(Pair.zip(leftKeys, call.getOperandList()), pair -> rexBuilder.makeCall(comparisonOp, pair.left, // TODO: remove requireNonNull when checkerframework issue resolved
ensureSqlType(requireNonNull(pair.left, "pair.left").getType(), bb.convertExpression(pair.right)))));
}
comparisons.add(rexComparison);
}
switch(op.kind) {
case ALL:
return RexUtil.composeConjunction(rexBuilder, comparisons, true);
case NOT_IN:
return rexBuilder.makeCall(SqlStdOperatorTable.NOT, RexUtil.composeDisjunction(rexBuilder, comparisons));
case IN:
case SOME:
return RexUtil.composeDisjunction(rexBuilder, comparisons, true);
default:
throw new AssertionError();
}
}
use of org.apache.calcite.sql.SqlBasicCall in project flink-mirror by flink-ci.
the class SqlValidatorImpl method performUnconditionalRewrites.
/**
* Performs expression rewrites which are always used unconditionally. These rewrites massage
* the expression tree into a standard form so that the rest of the validation logic can be
* simpler.
*
* @param node expression to be rewritten
* @param underFrom whether node appears directly under a FROM clause
* @return rewritten expression
*/
protected SqlNode performUnconditionalRewrites(SqlNode node, boolean underFrom) {
if (node == null) {
return null;
}
SqlNode newOperand;
// first transform operands and invoke generic call rewrite
if (node instanceof SqlCall) {
if (node instanceof SqlMerge) {
validatingSqlMerge = true;
}
SqlCall call = (SqlCall) node;
final SqlKind kind = call.getKind();
final List<SqlNode> operands = call.getOperandList();
for (int i = 0; i < operands.size(); i++) {
SqlNode operand = operands.get(i);
boolean childUnderFrom;
if (kind == SqlKind.SELECT) {
childUnderFrom = i == SqlSelect.FROM_OPERAND;
} else if (kind == SqlKind.AS && (i == 0)) {
// for an aliased expression, it is under FROM if
// the AS expression is under FROM
childUnderFrom = underFrom;
} else {
childUnderFrom = false;
}
newOperand = performUnconditionalRewrites(operand, childUnderFrom);
if (newOperand != null && newOperand != operand) {
call.setOperand(i, newOperand);
}
}
if (call.getOperator() instanceof SqlUnresolvedFunction) {
assert call instanceof SqlBasicCall;
final SqlUnresolvedFunction function = (SqlUnresolvedFunction) call.getOperator();
// This function hasn't been resolved yet. Perform
// a half-hearted resolution now in case it's a
// builtin function requiring special casing. If it's
// not, we'll handle it later during overload resolution.
final List<SqlOperator> overloads = new ArrayList<>();
opTab.lookupOperatorOverloads(function.getNameAsId(), function.getFunctionType(), SqlSyntax.FUNCTION, overloads, catalogReader.nameMatcher());
if (overloads.size() == 1) {
((SqlBasicCall) call).setOperator(overloads.get(0));
}
}
if (config.callRewrite()) {
node = call.getOperator().rewriteCall(this, call);
}
} else if (node instanceof SqlNodeList) {
SqlNodeList list = (SqlNodeList) node;
for (int i = 0, count = list.size(); i < count; i++) {
SqlNode operand = list.get(i);
newOperand = performUnconditionalRewrites(operand, false);
if (newOperand != null) {
list.getList().set(i, newOperand);
}
}
}
// now transform node itself
final SqlKind kind = node.getKind();
switch(kind) {
case VALUES:
// CHECKSTYLE: IGNORE 1
if (underFrom || true) {
// over and over
return node;
} else {
final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
return new SqlSelect(node.getParserPosition(), null, selectList, node, null, null, null, null, null, null, null, null);
}
case ORDER_BY:
{
SqlOrderBy orderBy = (SqlOrderBy) node;
handleOffsetFetch(orderBy.offset, orderBy.fetch);
if (orderBy.query instanceof SqlSelect) {
SqlSelect select = (SqlSelect) orderBy.query;
// an order-sensitive function like RANK.
if (select.getOrderList() == null) {
// push ORDER BY into existing select
select.setOrderBy(orderBy.orderList);
select.setOffset(orderBy.offset);
select.setFetch(orderBy.fetch);
return select;
}
}
if (orderBy.query instanceof SqlWith && ((SqlWith) orderBy.query).body instanceof SqlSelect) {
SqlWith with = (SqlWith) orderBy.query;
SqlSelect select = (SqlSelect) with.body;
// an order-sensitive function like RANK.
if (select.getOrderList() == null) {
// push ORDER BY into existing select
select.setOrderBy(orderBy.orderList);
select.setOffset(orderBy.offset);
select.setFetch(orderBy.fetch);
return with;
}
}
final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
final SqlNodeList orderList;
if (getInnerSelect(node) != null && isAggregate(getInnerSelect(node))) {
orderList = SqlNode.clone(orderBy.orderList);
// We assume that ORDER BY item is present in SELECT list.
for (int i = 0; i < orderList.size(); i++) {
SqlNode sqlNode = orderList.get(i);
SqlNodeList selectList2 = getInnerSelect(node).getSelectList();
for (Ord<SqlNode> sel : Ord.zip(selectList2)) {
if (stripAs(sel.e).equalsDeep(sqlNode, Litmus.IGNORE)) {
orderList.set(i, SqlLiteral.createExactNumeric(Integer.toString(sel.i + 1), SqlParserPos.ZERO));
}
}
}
} else {
orderList = orderBy.orderList;
}
return new SqlSelect(SqlParserPos.ZERO, null, selectList, orderBy.query, null, null, null, null, orderList, orderBy.offset, orderBy.fetch, null);
}
case EXPLICIT_TABLE:
{
// (TABLE t) is equivalent to (SELECT * FROM t)
SqlCall call = (SqlCall) node;
final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
return new SqlSelect(SqlParserPos.ZERO, null, selectList, call.operand(0), null, null, null, null, null, null, null, null);
}
case DELETE:
{
SqlDelete call = (SqlDelete) node;
SqlSelect select = createSourceSelectForDelete(call);
call.setSourceSelect(select);
break;
}
case UPDATE:
{
SqlUpdate call = (SqlUpdate) node;
SqlSelect select = createSourceSelectForUpdate(call);
call.setSourceSelect(select);
// in which case leave it alone).
if (!validatingSqlMerge) {
SqlNode selfJoinSrcExpr = getSelfJoinExprForUpdate(call.getTargetTable(), UPDATE_SRC_ALIAS);
if (selfJoinSrcExpr != null) {
node = rewriteUpdateToMerge(call, selfJoinSrcExpr);
}
}
break;
}
case MERGE:
{
SqlMerge call = (SqlMerge) node;
rewriteMerge(call);
break;
}
}
return node;
}
use of org.apache.calcite.sql.SqlBasicCall in project flink-mirror by flink-ci.
the class SqlValidatorImpl method deriveConstructorType.
public RelDataType deriveConstructorType(SqlValidatorScope scope, SqlCall call, SqlFunction unresolvedConstructor, SqlFunction resolvedConstructor, List<RelDataType> argTypes) {
SqlIdentifier sqlIdentifier = unresolvedConstructor.getSqlIdentifier();
assert sqlIdentifier != null;
RelDataType type = catalogReader.getNamedType(sqlIdentifier);
if (type == null) {
// TODO jvs 12-Feb-2005: proper type name formatting
throw newValidationError(sqlIdentifier, RESOURCE.unknownDatatypeName(sqlIdentifier.toString()));
}
if (resolvedConstructor == null) {
if (call.operandCount() > 0) {
// no user-defined constructor could be found
throw handleUnresolvedFunction(call, unresolvedConstructor, argTypes, null);
}
} else {
SqlCall testCall = resolvedConstructor.createCall(call.getParserPosition(), call.getOperandList());
RelDataType returnType = resolvedConstructor.validateOperands(this, scope, testCall);
assert type == returnType;
}
if (config.identifierExpansion()) {
if (resolvedConstructor != null) {
((SqlBasicCall) call).setOperator(resolvedConstructor);
} else {
// fake a fully-qualified call to the default constructor
((SqlBasicCall) call).setOperator(new SqlFunction(type.getSqlIdentifier(), ReturnTypes.explicit(type), null, null, null, SqlFunctionCategory.USER_DEFINED_CONSTRUCTOR));
}
}
return type;
}
use of org.apache.calcite.sql.SqlBasicCall in project flink-mirror by flink-ci.
the class FlinkCalciteSqlValidator method validateJoin.
@Override
protected void validateJoin(SqlJoin join, SqlValidatorScope scope) {
// temporarily forbid the common predicates until the problem is fixed (see FLINK-7865).
if (join.getJoinType() == JoinType.LEFT && SqlUtil.stripAs(join.getRight()).getKind() == SqlKind.COLLECTION_TABLE) {
SqlNode right = SqlUtil.stripAs(join.getRight());
if (right instanceof SqlBasicCall) {
SqlBasicCall call = (SqlBasicCall) right;
SqlNode operand0 = call.operand(0);
if (operand0 instanceof SqlBasicCall && ((SqlBasicCall) operand0).getOperator() instanceof SqlWindowTableFunction) {
return;
}
}
final SqlNode condition = join.getCondition();
if (condition != null && (!SqlUtil.isLiteral(condition) || ((SqlLiteral) condition).getValueAs(Boolean.class) != Boolean.TRUE)) {
throw new ValidationException(String.format("Left outer joins with a table function do not accept a predicate such as %s. " + "Only literal TRUE is accepted.", condition));
}
}
super.validateJoin(join, scope);
}
Aggregations