Search in sources :

Example 1 with SqlFunction

use of org.apache.calcite.sql.SqlFunction in project hive by apache.

the class HiveSubQueryRemoveRule method apply.

protected RexNode apply(RexSubQuery e, Set<CorrelationId> variablesSet, RelOptUtil.Logic logic, HiveSubQRemoveRelBuilder builder, int inputCount, int offset, boolean isCorrScalarAgg, boolean hasNoWindowingAndNoGby) {
    switch(e.getKind()) {
        case SCALAR_QUERY:
            // since it is guaranteed to produce at most one row
            if (!hasNoWindowingAndNoGby) {
                final List<RexNode> parentQueryFields = new ArrayList<>();
                if (conf.getBoolVar(ConfVars.HIVE_REMOVE_SQ_COUNT_CHECK)) {
                    // we want to have project after join since sq_count_check's count() expression wouldn't
                    // be needed further up
                    parentQueryFields.addAll(builder.fields());
                }
                builder.push(e.rel);
                // returns single row/column
                builder.aggregate(builder.groupKey(), builder.count(false, "cnt"));
                SqlFunction countCheck = new SqlFunction("sq_count_check", SqlKind.OTHER_FUNCTION, ReturnTypes.BIGINT, InferTypes.RETURN_TYPE, OperandTypes.NUMERIC, SqlFunctionCategory.USER_DEFINED_FUNCTION);
                // we create FILTER (sq_count_check(count()) <= 1) instead of PROJECT because RelFieldTrimmer
                // ends up getting rid of Project since it is not used further up the tree
                builder.filter(builder.call(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, builder.call(countCheck, builder.field("cnt")), builder.literal(1)));
                if (!variablesSet.isEmpty()) {
                    builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet);
                } else {
                    builder.join(JoinRelType.INNER, builder.literal(true), variablesSet);
                }
                if (conf.getBoolVar(ConfVars.HIVE_REMOVE_SQ_COUNT_CHECK)) {
                    builder.project(parentQueryFields);
                } else {
                    offset++;
                }
            }
            if (isCorrScalarAgg) {
                // Transformation :
                // Outer Query Left Join (inner query) on correlated predicate
                // and preserve rows only from left side.
                builder.push(e.rel);
                final List<RexNode> parentQueryFields = new ArrayList<>();
                parentQueryFields.addAll(builder.fields());
                // id is appended since there could be multiple scalar subqueries and FILTER
                // is created using field name
                String indicator = "alwaysTrue" + e.rel.getId();
                parentQueryFields.add(builder.alias(builder.literal(true), indicator));
                builder.project(parentQueryFields);
                builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet);
                final ImmutableList.Builder<RexNode> operands = ImmutableList.builder();
                RexNode literal;
                if (isAggZeroOnEmpty(e)) {
                    // since count has a return type of BIG INT we need to make a literal of type big int
                    // relbuilder's literal doesn't allow this
                    literal = e.rel.getCluster().getRexBuilder().makeBigintLiteral(new BigDecimal(0));
                } else {
                    literal = e.rel.getCluster().getRexBuilder().makeNullLiteral(getAggTypeForScalarSub(e));
                }
                operands.add((builder.isNull(builder.field(indicator))), literal);
                operands.add(field(builder, 1, builder.fields().size() - 2));
                return builder.call(SqlStdOperatorTable.CASE, operands.build());
            }
            // Transformation is to left join for correlated predicates and inner join otherwise,
            // but do a count on inner side before that to make sure it generates atmost 1 row.
            builder.push(e.rel);
            builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet);
            return field(builder, inputCount, offset);
        case IN:
        case EXISTS:
            // Most general case, where the left and right keys might have nulls, and
            // caller requires 3-valued logic return.
            // 
            // select e.deptno, e.deptno in (select deptno from emp)
            // 
            // becomes
            // 
            // select e.deptno,
            // case
            // when ct.c = 0 then false
            // when dt.i is not null then true
            // when e.deptno is null then null
            // when ct.ck < ct.c then null
            // else false
            // end
            // from e
            // left join (
            // (select count(*) as c, count(deptno) as ck from emp) as ct
            // cross join (select distinct deptno, true as i from emp)) as dt
            // on e.deptno = dt.deptno
            // 
            // If keys are not null we can remove "ct" and simplify to
            // 
            // select e.deptno,
            // case
            // when dt.i is not null then true
            // else false
            // end
            // from e
            // left join (select distinct deptno, true as i from emp) as dt
            // on e.deptno = dt.deptno
            // 
            // We could further simplify to
            // 
            // select e.deptno,
            // dt.i is not null
            // from e
            // left join (select distinct deptno, true as i from emp) as dt
            // on e.deptno = dt.deptno
            // 
            // but have not yet.
            // 
            // If the logic is TRUE we can just kill the record if the condition
            // evaluates to FALSE or UNKNOWN. Thus the query simplifies to an inner
            // join:
            // 
            // select e.deptno,
            // true
            // from e
            // inner join (select distinct deptno from emp) as dt
            // on e.deptno = dt.deptno
            // 
            builder.push(e.rel);
            final List<RexNode> fields = new ArrayList<>();
            switch(e.getKind()) {
                case IN:
                    fields.addAll(builder.fields());
                    // will produce wrong results (because we further rewrite such queries into JOIN)
                    if (isCorrScalarAgg) {
                        // returns single row/column
                        builder.aggregate(builder.groupKey(), builder.count(false, "cnt_in"));
                        if (!variablesSet.isEmpty()) {
                            builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet);
                        } else {
                            builder.join(JoinRelType.INNER, builder.literal(true), variablesSet);
                        }
                        SqlFunction inCountCheck = new SqlFunction("sq_count_check", SqlKind.OTHER_FUNCTION, ReturnTypes.BIGINT, InferTypes.RETURN_TYPE, OperandTypes.NUMERIC, SqlFunctionCategory.USER_DEFINED_FUNCTION);
                        // we create FILTER (sq_count_check(count()) > 0) instead of PROJECT
                        // because RelFieldTrimmer ends up getting rid of Project
                        // since it is not used further up the tree
                        builder.filter(builder.call(SqlStdOperatorTable.GREATER_THAN, // true here indicates that sq_count_check is for IN/NOT IN subqueries
                        builder.call(inCountCheck, builder.field("cnt_in"), builder.literal(true)), builder.literal(0)));
                        offset = offset + 1;
                        builder.push(e.rel);
                    }
            }
            // First, the cross join
            switch(logic) {
                case TRUE_FALSE_UNKNOWN:
                case UNKNOWN_AS_TRUE:
                    // null keys we do not need to generate count(*), count(c)
                    if (e.getKind() == SqlKind.EXISTS) {
                        logic = RelOptUtil.Logic.TRUE_FALSE;
                        break;
                    }
                    builder.aggregate(builder.groupKey(), builder.count(false, "c"), builder.aggregateCall(SqlStdOperatorTable.COUNT, false, null, "ck", builder.fields()));
                    builder.as("ct");
                    if (!variablesSet.isEmpty()) {
                        // builder.join(JoinRelType.INNER, builder.literal(true), variablesSet);
                        builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet);
                    } else {
                        builder.join(JoinRelType.INNER, builder.literal(true), variablesSet);
                    }
                    offset += 2;
                    builder.push(e.rel);
                    break;
            }
            // Now the left join
            switch(logic) {
                case TRUE:
                    if (fields.isEmpty()) {
                        builder.project(builder.alias(builder.literal(true), "i" + e.rel.getId()));
                        if (!variablesSet.isEmpty() && (e.getKind() == SqlKind.EXISTS || e.getKind() == SqlKind.IN)) {
                            // since this is rewritting into semijoin
                            break;
                        } else {
                            builder.aggregate(builder.groupKey(0));
                        }
                    } else {
                        if (!variablesSet.isEmpty() && (e.getKind() == SqlKind.EXISTS || e.getKind() == SqlKind.IN)) {
                            // since this is rewritting into semijoin
                            break;
                        } else {
                            builder.aggregate(builder.groupKey(fields));
                        }
                    }
                    break;
                default:
                    fields.add(builder.alias(builder.literal(true), "i" + e.rel.getId()));
                    builder.project(fields);
                    builder.distinct();
            }
            builder.as("dt");
            final List<RexNode> conditions = new ArrayList<>();
            for (Pair<RexNode, RexNode> pair : Pair.zip(e.getOperands(), builder.fields())) {
                conditions.add(builder.equals(pair.left, RexUtil.shift(pair.right, offset)));
            }
            switch(logic) {
                case TRUE:
                    builder.join(JoinRelType.INNER, builder.and(conditions), variablesSet, true);
                    return builder.literal(true);
            }
            builder.join(JoinRelType.LEFT, builder.and(conditions), variablesSet);
            final List<RexNode> keyIsNulls = new ArrayList<>();
            for (RexNode operand : e.getOperands()) {
                if (operand.getType().isNullable()) {
                    keyIsNulls.add(builder.isNull(operand));
                }
            }
            final ImmutableList.Builder<RexNode> operands = ImmutableList.builder();
            switch(logic) {
                case TRUE_FALSE_UNKNOWN:
                case UNKNOWN_AS_TRUE:
                    operands.add(builder.equals(builder.field("ct", "c"), builder.literal(0)), builder.literal(false));
                    // now that we are using LEFT OUTER JOIN to join inner count, count(*)
                    // with outer table, we wouldn't be able to tell if count is zero
                    // for inner table since inner join with correlated values will get rid
                    // of all values where join cond is not true (i.e where actual inner table
                    // will produce zero result). To  handle this case we need to check both
                    // count is zero or count is null
                    operands.add((builder.isNull(builder.field("ct", "c"))), builder.literal(false));
                    break;
            }
            operands.add(builder.isNotNull(builder.field("dt", "i" + e.rel.getId())), builder.literal(true));
            if (!keyIsNulls.isEmpty()) {
                // Calcite creates null literal with Null type here but
                // because HIVE doesn't support null type it is appropriately typed boolean
                operands.add(builder.or(keyIsNulls), e.rel.getCluster().getRexBuilder().makeNullLiteral(SqlTypeName.BOOLEAN));
            // we are creating filter here so should not be returning NULL.
            // Not sure why Calcite return NULL
            }
            RexNode b = builder.literal(true);
            switch(logic) {
                case TRUE_FALSE_UNKNOWN:
                    b = e.rel.getCluster().getRexBuilder().makeNullLiteral(SqlTypeName.BOOLEAN);
                // fall through
                case UNKNOWN_AS_TRUE:
                    operands.add(builder.call(SqlStdOperatorTable.LESS_THAN, builder.field("ct", "ck"), builder.field("ct", "c")), b);
                    break;
            }
            operands.add(builder.literal(false));
            return builder.call(SqlStdOperatorTable.CASE, operands.build());
        default:
            throw new AssertionError(e.getKind());
    }
}
Also used : ImmutableList(com.google.common.collect.ImmutableList) ArrayList(java.util.ArrayList) BigDecimal(java.math.BigDecimal) RexNode(org.apache.calcite.rex.RexNode) SqlFunction(org.apache.calcite.sql.SqlFunction)

Example 2 with SqlFunction

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

the class SqlValidatorImpl method handleUnresolvedFunction.

public CalciteException handleUnresolvedFunction(SqlCall call, SqlFunction unresolvedFunction, List<RelDataType> argTypes, List<String> argNames) {
    // For builtins, we can give a better error message
    final List<SqlOperator> overloads = new ArrayList<>();
    opTab.lookupOperatorOverloads(unresolvedFunction.getNameAsId(), null, SqlSyntax.FUNCTION, overloads);
    if (overloads.size() == 1) {
        SqlFunction fun = (SqlFunction) overloads.get(0);
        if ((fun.getSqlIdentifier() == null) && (fun.getSyntax() != SqlSyntax.FUNCTION_ID)) {
            final int expectedArgCount = fun.getOperandCountRange().getMin();
            throw newValidationError(call, RESOURCE.invalidArgCount(call.getOperator().getName(), expectedArgCount));
        }
    }
    AssignableOperandTypeChecker typeChecking = new AssignableOperandTypeChecker(argTypes, argNames);
    String signature = typeChecking.getAllowedSignatures(unresolvedFunction, unresolvedFunction.getName());
    throw newValidationError(call, RESOURCE.validatorUnknownFunction(signature));
}
Also used : AssignableOperandTypeChecker(org.apache.calcite.sql.type.AssignableOperandTypeChecker) SqlOperator(org.apache.calcite.sql.SqlOperator) ArrayList(java.util.ArrayList) BitString(org.apache.calcite.util.BitString) SqlFunction(org.apache.calcite.sql.SqlFunction)

Example 3 with SqlFunction

use of org.apache.calcite.sql.SqlFunction 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 4 with SqlFunction

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

the class SqlValidatorImpl method validateCall.

public void validateCall(SqlCall call, SqlValidatorScope scope) {
    final SqlOperator operator = call.getOperator();
    if ((call.operandCount() == 0) && (operator.getSyntax() == SqlSyntax.FUNCTION_ID) && !call.isExpanded() && !conformance.allowNiladicParentheses()) {
        // SqlIdentifier.)
        throw handleUnresolvedFunction(call, (SqlFunction) operator, ImmutableList.<RelDataType>of(), null);
    }
    SqlValidatorScope operandScope = scope.getOperandScope(call);
    if (operator instanceof SqlFunction && ((SqlFunction) operator).getFunctionType() == SqlFunctionCategory.MATCH_RECOGNIZE && !(operandScope instanceof MatchRecognizeScope)) {
        throw newValidationError(call, Static.RESOURCE.functionMatchRecognizeOnly(call.toString()));
    }
    // Delegate validation to the operator.
    operator.validateCall(call, this, scope, operandScope);
}
Also used : SqlOperator(org.apache.calcite.sql.SqlOperator) SqlFunction(org.apache.calcite.sql.SqlFunction)

Example 5 with SqlFunction

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

the class ReflectiveSqlOperatorTable method lookupOperatorOverloads.

// implement SqlOperatorTable
public void lookupOperatorOverloads(SqlIdentifier opName, SqlFunctionCategory category, SqlSyntax syntax, List<SqlOperator> operatorList) {
    // NOTE jvs 3-Mar-2005:  ignore category until someone cares
    String simpleName;
    if (opName.names.size() > 1) {
        if (opName.names.get(opName.names.size() - 2).equals(IS_NAME)) {
            // per SQL99 Part 2 Section 10.4 Syntax Rule 7.b.ii.1
            simpleName = Util.last(opName.names);
        } else {
            return;
        }
    } else {
        simpleName = opName.getSimple();
    }
    // Always look up built-in operators case-insensitively. Even in sessions
    // with unquotedCasing=UNCHANGED and caseSensitive=true.
    final Collection<SqlOperator> list = operators.get(new Key(simpleName, syntax));
    if (list.isEmpty()) {
        return;
    }
    for (SqlOperator op : list) {
        if (op.getSyntax() == syntax) {
            operatorList.add(op);
        } else if (syntax == SqlSyntax.FUNCTION && op instanceof SqlFunction) {
            // this special case is needed for operators like CAST,
            // which are treated as functions but have special syntax
            operatorList.add(op);
        }
    }
    // Shouldn't it be covered by search above?
    switch(syntax) {
        case BINARY:
        case PREFIX:
        case POSTFIX:
            for (SqlOperator extra : operators.get(new Key(simpleName, syntax))) {
                // REVIEW: should only search operators added during this method?
                if (extra != null && !operatorList.contains(extra)) {
                    operatorList.add(extra);
                }
            }
            break;
    }
}
Also used : SqlOperator(org.apache.calcite.sql.SqlOperator) SqlFunction(org.apache.calcite.sql.SqlFunction)

Aggregations

SqlFunction (org.apache.calcite.sql.SqlFunction)30 SqlOperator (org.apache.calcite.sql.SqlOperator)13 ArrayList (java.util.ArrayList)7 RexNode (org.apache.calcite.rex.RexNode)6 RoundOperatorConversion (org.apache.druid.sql.calcite.expression.builtin.RoundOperatorConversion)6 Test (org.junit.Test)6 SqlCall (org.apache.calcite.sql.SqlCall)5 ImmutableList (com.google.common.collect.ImmutableList)4 SqlIdentifier (org.apache.calcite.sql.SqlIdentifier)4 BigDecimal (java.math.BigDecimal)3 RexCall (org.apache.calcite.rex.RexCall)3 RexLiteral (org.apache.calcite.rex.RexLiteral)3 SqlNode (org.apache.calcite.sql.SqlNode)3 List (java.util.List)2 TimeUnitRange (org.apache.calcite.avatica.util.TimeUnitRange)2 LogicalProject (org.apache.calcite.rel.logical.LogicalProject)2 RelDataType (org.apache.calcite.rel.type.RelDataType)2 RexBuilder (org.apache.calcite.rex.RexBuilder)2 SqlAggFunction (org.apache.calcite.sql.SqlAggFunction)2 SqlBasicCall (org.apache.calcite.sql.SqlBasicCall)2