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());
}
}
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));
}
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;
}
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);
}
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;
}
}
Aggregations