Search in sources :

Example 1 with SqlBasicCall

use of org.apache.calcite.sql.SqlBasicCall in project calcite by apache.

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 node;
    }
    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);
            if (overloads.size() == 1) {
                ((SqlBasicCall) call).setOperator(overloads.get(0));
            }
        }
        if (rewriteCalls) {
            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);
            }
        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);
            }
        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);
            }
        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;
}
Also used : Ord(org.apache.calcite.linq4j.Ord) SqlCall(org.apache.calcite.sql.SqlCall) SqlOperator(org.apache.calcite.sql.SqlOperator) SqlWith(org.apache.calcite.sql.SqlWith) ArrayList(java.util.ArrayList) SqlKind(org.apache.calcite.sql.SqlKind) SqlUpdate(org.apache.calcite.sql.SqlUpdate) SqlMerge(org.apache.calcite.sql.SqlMerge) SqlSelect(org.apache.calcite.sql.SqlSelect) SqlBasicCall(org.apache.calcite.sql.SqlBasicCall) SqlDelete(org.apache.calcite.sql.SqlDelete) SqlNodeList(org.apache.calcite.sql.SqlNodeList) SqlOrderBy(org.apache.calcite.sql.SqlOrderBy) SqlNode(org.apache.calcite.sql.SqlNode) SqlUnresolvedFunction(org.apache.calcite.sql.SqlUnresolvedFunction)

Example 2 with SqlBasicCall

use of org.apache.calcite.sql.SqlBasicCall in project calcite by apache.

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 (shouldExpandIdentifiers()) {
        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;
}
Also used : SqlBasicCall(org.apache.calcite.sql.SqlBasicCall) SqlCall(org.apache.calcite.sql.SqlCall) RelDataType(org.apache.calcite.rel.type.RelDataType) SqlIdentifier(org.apache.calcite.sql.SqlIdentifier) SqlFunction(org.apache.calcite.sql.SqlFunction)

Example 3 with SqlBasicCall

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 MULTISET_QUERY_CONSTRUCTOR:
        case MULTISET_VALUE_CONSTRUCTOR:
        case ARRAY_QUERY_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 = Lists.newArrayList();
                    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;
                if (!containsNullLiteral(valueList) && valueList.size() < config.getInSubQueryThreshold()) {
                    // We're under the threshold, so convert to OR.
                    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.
            }
            // Project out the search columns from the left side
            // Q1:
            // "select from emp where emp.deptno in (select col1 from T)"
            // 
            // is converted to
            // 
            // "select from
            // emp inner join (select distinct col1 from T)) q
            // on emp.deptno = q.col1
            // 
            // Q2:
            // "select from emp where emp.deptno not in (Q)"
            // 
            // is converted to
            // 
            // "select from
            // emp left outer join (select distinct col1, TRUE from T) q
            // on emp.deptno = q.col1
            // where emp.deptno <> null
            // and q.indicator <> TRUE"
            // 
            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, ImmutableBitSet.of(), null, ImmutableList.of(AggregateCall.create(SqlStdOperatorTable.COUNT, false, false, ImmutableList.<Integer>of(), -1, longType, null), AggregateCall.create(SqlStdOperatorTable.COUNT, false, false, args, -1, longType, null)));
                LogicalJoin join = LogicalJoin.create(bb.root, aggregate, rexBuilder.makeLiteral(true), ImmutableSet.<CorrelationId>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;
                    }
            }
            subQuery.expr = translateIn(logic, bb.root, rex);
            if (notIn) {
                subQuery.expr = rexBuilder.makeCall(SqlStdOperatorTable.NOT, subQuery.expr);
            }
            return;
        case EXISTS:
            // "select from emp where exists (select a from T)"
            // 
            // is converted to the following if the sub-query is correlated:
            // 
            // "select from emp left outer join (select AGG_TRUE() as indicator
            // from T group by corr_var) q where q.indicator is true"
            // 
            // If there is no correlation, the expression is replaced with a
            // boolean indicating whether the sub-query returned 0 or >= 1 row.
            call = (SqlBasicCall) subQuery.node;
            query = call.operand(0);
            if (!config.isExpand()) {
                return;
            }
            converted = convertExists(query, RelOptUtil.SubQueryType.EXISTS, subQuery.logic, true, null);
            assert !converted.indicator;
            if (convertNonCorrelatedSubQuery(subQuery, bb, converted.r, true)) {
                return;
            }
            subQuery.expr = bb.register(converted.r, JoinRelType.LEFT);
            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);
            return;
        default:
            throw new AssertionError("unexpected kind of sub-query: " + subQuery.node);
    }
}
Also used : RelOptUtil(org.apache.calcite.plan.RelOptUtil) SqlInOperator(org.apache.calcite.sql.fun.SqlInOperator) RelDataType(org.apache.calcite.rel.type.RelDataType) LogicalAggregate(org.apache.calcite.rel.logical.LogicalAggregate) SqlBasicCall(org.apache.calcite.sql.SqlBasicCall) RelNode(org.apache.calcite.rel.RelNode) LogicalJoin(org.apache.calcite.rel.logical.LogicalJoin) SqlNodeList(org.apache.calcite.sql.SqlNodeList) RexNode(org.apache.calcite.rex.RexNode) SqlNode(org.apache.calcite.sql.SqlNode)

Example 4 with SqlBasicCall

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, &gt; SOME, ...)
 * @return converted expression
 */
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, Iterables.transform(Pair.zip(leftKeys, call.getOperandList()), new Function<Pair<RexNode, SqlNode>, RexNode>() {

                public RexNode apply(Pair<RexNode, SqlNode> pair) {
                    return rexBuilder.makeCall(comparisonOp, pair.left, ensureSqlType(pair.left.getType(), bb.convertExpression(pair.right)));
                }
            }), false);
        }
        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, true));
        case IN:
        case SOME:
            return RexUtil.composeDisjunction(rexBuilder, comparisons, true);
        default:
            throw new AssertionError();
    }
}
Also used : SqlOperator(org.apache.calcite.sql.SqlOperator) SqlCall(org.apache.calcite.sql.SqlCall) ArrayList(java.util.ArrayList) SqlRowOperator(org.apache.calcite.sql.fun.SqlRowOperator) SqlBasicCall(org.apache.calcite.sql.SqlBasicCall) SqlQuantifyOperator(org.apache.calcite.sql.fun.SqlQuantifyOperator) RexNode(org.apache.calcite.rex.RexNode) SqlNode(org.apache.calcite.sql.SqlNode) Pair(org.apache.calcite.util.Pair)

Example 5 with SqlBasicCall

use of org.apache.calcite.sql.SqlBasicCall in project calcite by apache.

the class SqlToRelConverter method convertNonCorrelatedSubQuery.

/**
 * Determines if a sub-query is non-correlated and if so, converts it to a
 * constant.
 *
 * @param subQuery  the call that references the sub-query
 * @param bb        blackboard used to convert the sub-query
 * @param converted RelNode tree corresponding to the sub-query
 * @param isExists  true if the sub-query is part of an EXISTS expression
 * @return Whether the sub-query can be converted to a constant
 */
private boolean convertNonCorrelatedSubQuery(SubQuery subQuery, Blackboard bb, RelNode converted, boolean isExists) {
    SqlCall call = (SqlBasicCall) subQuery.node;
    if (subQueryConverter.canConvertSubQuery() && isSubQueryNonCorrelated(converted, bb)) {
        // First check if the sub-query has already been converted
        // because it's a nested sub-query.  If so, don't re-evaluate
        // it again.
        RexNode constExpr = mapConvertedNonCorrSubqs.get(call);
        if (constExpr == null) {
            constExpr = subQueryConverter.convertSubQuery(call, this, isExists, config.isExplain());
        }
        if (constExpr != null) {
            subQuery.expr = constExpr;
            mapConvertedNonCorrSubqs.put(call, constExpr);
            return true;
        }
    }
    return false;
}
Also used : SqlBasicCall(org.apache.calcite.sql.SqlBasicCall) SqlCall(org.apache.calcite.sql.SqlCall) RexNode(org.apache.calcite.rex.RexNode)

Aggregations

SqlBasicCall (org.apache.calcite.sql.SqlBasicCall)25 SqlNode (org.apache.calcite.sql.SqlNode)18 SqlCall (org.apache.calcite.sql.SqlCall)11 SqlOperator (org.apache.calcite.sql.SqlOperator)10 ArrayList (java.util.ArrayList)9 RelDataType (org.apache.calcite.rel.type.RelDataType)9 SqlNodeList (org.apache.calcite.sql.SqlNodeList)7 SqlIdentifier (org.apache.calcite.sql.SqlIdentifier)6 SqlLiteral (org.apache.calcite.sql.SqlLiteral)6 RexNode (org.apache.calcite.rex.RexNode)5 SqlKind (org.apache.calcite.sql.SqlKind)4 SqlSelect (org.apache.calcite.sql.SqlSelect)4 ImmutableList (com.google.common.collect.ImmutableList)3 IdentityHashMap (java.util.IdentityHashMap)3 RelDataTypeFactory (org.apache.calcite.rel.type.RelDataTypeFactory)3 SqlInOperator (org.apache.calcite.sql.fun.SqlInOperator)3 HashMap (java.util.HashMap)2 Map (java.util.Map)2 RelNode (org.apache.calcite.rel.RelNode)2 RelDataTypeField (org.apache.calcite.rel.type.RelDataTypeField)2