@Override public Boolean visitCall(RexCall call) { // Constant if operator is deterministic and all operands are // constant. return call.getOperator().isDeterministic() && RexVisitorImpl.visitArrayAnd(this, call.getOperands()); }
private boolean isAlwaysTrue(RexNode predicate) { if (predicate instanceof RexCall) { RexCall c = (RexCall) predicate; if (c.getOperator().getKind() == SqlKind.EQUALS) { int lPos = pos(c.getOperands().get(0)); int rPos = pos(c.getOperands().get(1)); return lPos != -1 && lPos == rPos; } } return predicate.isAlwaysTrue(); }
@Override public Void visitCall(RexCall call) { if (call.getOperator().getKind() == SqlKind.EQUALS) { int lPos = pos(call.getOperands().get(0)); int rPos = pos(call.getOperands().get(1)); if (lPos != -1 && rPos != -1) { JoinConditionBasedPredicateInference.this.equivalent(lPos, rPos); JoinConditionBasedPredicateInference.this.equalityPredicates.add(call.toString()); } } return null; }
/** Find the list of expressions where Complex type function is at top level. */ private List<RexNode> findTopComplexFunc(List<RexNode> exprs) { final List<RexNode> topComplexFuncs = new ArrayList<>(); for (RexNode exp : exprs) { if (exp instanceof RexCall) { RexCall call = (RexCall) exp; String functionName = call.getOperator().getName(); if (funcReg.isFunctionComplexOutput(functionName)) { topComplexFuncs.add(exp); } } } return topComplexFuncs; }
@Override public String translate(ExprCompiler compiler, RexCall call) { String val = compiler.reserveName(); PrintWriter pw = compiler.pw; RexNode op = call.getOperands().get(0); String lhs = op.accept(compiler); boolean nullable = call.getType().isNullable(); pw.print(String.format("final %s %s;\n", compiler.javaTypeName(call), val)); if (!nullable) { pw.print(String.format("%1$s = !(%2$s);\n", val, lhs)); } else { String s = foldNullExpr(String.format("%1$s == null ? null : !(%1$s)", lhs), "null", op); pw.print(String.format("%1$s = %2$s;\n", val, s)); } return val; }
private String compile(ExprCompiler compiler, RexCall call) { SqlOperator op = call.getOperator(); CallExprPrinter printer = getCallExprPrinter(op); if (printer == null) { throw new UnsupportedOperationException(); } else { return printer.translate(compiler, call); } }
@Override public String translate(ExprCompiler compiler, RexCall call) { String val = compiler.reserveName(); PrintWriter pw = compiler.pw; RexNode op = call.getOperands().get(0); String lhs = op.accept(compiler); pw.print( String.format( "final %1$s %2$s = (%1$s) %3$s;\n", compiler.javaTypeName(call), val, lhs)); return val; }
public Double averageRexSize(RexNode node, List<Double> inputColumnSizes) { switch (node.getKind()) { case INPUT_REF: return inputColumnSizes.get(((RexInputRef) node).getIndex()); case LITERAL: return typeValueSize(node.getType(), ((RexLiteral) node).getValue()); default: if (node instanceof RexCall) { RexCall call = (RexCall) node; for (RexNode operand : call.getOperands()) { // It's a reasonable assumption that a function's result will have // similar size to its argument of a similar type. For example, // UPPER(c) has the same average size as c. if (operand.getType().getSqlTypeName() == node.getType().getSqlTypeName()) { return averageRexSize(operand, inputColumnSizes); } } } return averageTypeValueSize(node.getType()); } }
@Override public String translate(ExprCompiler compiler, RexCall call) { String val = compiler.reserveName(); PrintWriter pw = compiler.pw; pw.print(String.format("final %s %s;\n", compiler.javaTypeName(call), val)); RexNode op0 = call.getOperands().get(0); RexNode op1 = call.getOperands().get(1); boolean lhsNullable = op0.getType().isNullable(); boolean rhsNullable = op1.getType().isNullable(); String lhs = op0.accept(compiler); if (!lhsNullable) { pw.print(String.format("if (%2$s) { %1$s = true; }\n", val, lhs)); pw.print("else {\n"); String rhs = op1.accept(compiler); pw.print(String.format(" %1$s = %2$s;\n}\n", val, rhs)); } else { String foldedLHS = foldNullExpr(String.format("%1$s == null || !(%1$s)", lhs), "true", op0); pw.print(String.format("if (%s) {\n", foldedLHS)); String rhs = op1.accept(compiler); String s; if (rhsNullable) { s = foldNullExpr( String.format( "(%2$s != null && %2$s) ? Boolean.TRUE : ((%1$s == null || %2$s == null) ? null : Boolean.FALSE)", lhs, rhs), "null", op1); } else { s = String.format("%2$s ? Boolean.valueOf(%2$s) : %1$s", lhs, rhs); } pw.print(String.format(" %1$s = %2$s;\n", val, s)); pw.print(String.format("} else { %1$s = true; }\n", val)); } return val; }
private static void splitJoinCondition( List<RelDataTypeField> sysFieldList, List<RelNode> inputs, RexNode condition, List<List<RexNode>> joinKeys, List<Integer> filterNulls, List<SqlOperator> rangeOp, List<RexNode> nonEquiList) throws CalciteSemanticException { final int sysFieldCount = sysFieldList.size(); final RelOptCluster cluster = inputs.get(0).getCluster(); final RexBuilder rexBuilder = cluster.getRexBuilder(); if (condition instanceof RexCall) { RexCall call = (RexCall) condition; if (call.getOperator() == SqlStdOperatorTable.AND) { for (RexNode operand : call.getOperands()) { splitJoinCondition( sysFieldList, inputs, operand, joinKeys, filterNulls, rangeOp, nonEquiList); } return; } RexNode leftKey = null; RexNode rightKey = null; int leftInput = 0; int rightInput = 0; List<RelDataTypeField> leftFields = null; List<RelDataTypeField> rightFields = null; boolean reverse = false; SqlKind kind = call.getKind(); // Only consider range operators if we haven't already seen one if ((kind == SqlKind.EQUALS) || (filterNulls != null && kind == SqlKind.IS_NOT_DISTINCT_FROM) || (rangeOp != null && rangeOp.isEmpty() && (kind == SqlKind.GREATER_THAN || kind == SqlKind.GREATER_THAN_OR_EQUAL || kind == SqlKind.LESS_THAN || kind == SqlKind.LESS_THAN_OR_EQUAL))) { final List<RexNode> operands = call.getOperands(); RexNode op0 = operands.get(0); RexNode op1 = operands.get(1); final ImmutableBitSet projRefs0 = InputFinder.bits(op0); final ImmutableBitSet projRefs1 = InputFinder.bits(op1); final ImmutableBitSet[] inputsRange = new ImmutableBitSet[inputs.size()]; int totalFieldCount = 0; for (int i = 0; i < inputs.size(); i++) { final int firstField = totalFieldCount + sysFieldCount; totalFieldCount = firstField + inputs.get(i).getRowType().getFieldCount(); inputsRange[i] = ImmutableBitSet.range(firstField, totalFieldCount); } boolean foundBothInputs = false; for (int i = 0; i < inputs.size() && !foundBothInputs; i++) { if (projRefs0.intersects(inputsRange[i]) && projRefs0.union(inputsRange[i]).equals(inputsRange[i])) { if (leftKey == null) { leftKey = op0; leftInput = i; leftFields = inputs.get(leftInput).getRowType().getFieldList(); } else { rightKey = op0; rightInput = i; rightFields = inputs.get(rightInput).getRowType().getFieldList(); reverse = true; foundBothInputs = true; } } else if (projRefs1.intersects(inputsRange[i]) && projRefs1.union(inputsRange[i]).equals(inputsRange[i])) { if (leftKey == null) { leftKey = op1; leftInput = i; leftFields = inputs.get(leftInput).getRowType().getFieldList(); } else { rightKey = op1; rightInput = i; rightFields = inputs.get(rightInput).getRowType().getFieldList(); foundBothInputs = true; } } } if ((leftKey != null) && (rightKey != null)) { // adjustment array int[] adjustments = new int[totalFieldCount]; for (int i = 0; i < inputs.size(); i++) { final int adjustment = inputsRange[i].nextSetBit(0); for (int j = adjustment; j < inputsRange[i].length(); j++) { adjustments[j] = -adjustment; } } // replace right Key input ref rightKey = rightKey.accept( new RelOptUtil.RexInputConverter( rexBuilder, rightFields, rightFields, adjustments)); // left key only needs to be adjusted if there are system // fields, but do it for uniformity leftKey = leftKey.accept( new RelOptUtil.RexInputConverter( rexBuilder, leftFields, leftFields, adjustments)); RelDataType leftKeyType = leftKey.getType(); RelDataType rightKeyType = rightKey.getType(); if (leftKeyType != rightKeyType) { // perform casting using Hive rules TypeInfo rType = TypeConverter.convert(rightKeyType); TypeInfo lType = TypeConverter.convert(leftKeyType); TypeInfo tgtType = FunctionRegistry.getCommonClassForComparison(lType, rType); if (tgtType == null) { throw new CalciteSemanticException( "Cannot find common type for join keys " + leftKey + " (type " + leftKeyType + ") and " + rightKey + " (type " + rightKeyType + ")"); } RelDataType targetKeyType = TypeConverter.convert(tgtType, rexBuilder.getTypeFactory()); if (leftKeyType != targetKeyType && TypeInfoUtils.isConversionRequiredForComparison(tgtType, lType)) { leftKey = rexBuilder.makeCast(targetKeyType, leftKey); } if (rightKeyType != targetKeyType && TypeInfoUtils.isConversionRequiredForComparison(tgtType, rType)) { rightKey = rexBuilder.makeCast(targetKeyType, rightKey); } } } } if ((leftKey != null) && (rightKey != null)) { // found suitable join keys // add them to key list, ensuring that if there is a // non-equi join predicate, it appears at the end of the // key list; also mark the null filtering property addJoinKey(joinKeys.get(leftInput), leftKey, (rangeOp != null) && !rangeOp.isEmpty()); addJoinKey(joinKeys.get(rightInput), rightKey, (rangeOp != null) && !rangeOp.isEmpty()); if (filterNulls != null && kind == SqlKind.EQUALS) { // nulls are considered not matching for equality comparison // add the position of the most recently inserted key filterNulls.add(joinKeys.get(leftInput).size() - 1); } if (rangeOp != null && kind != SqlKind.EQUALS && kind != SqlKind.IS_DISTINCT_FROM) { if (reverse) { kind = reverse(kind); } rangeOp.add(op(kind, call.getOperator())); } return; } // else fall through and add this condition as nonEqui condition } // The operator is not of RexCall type // So we fail. Fall through. // Add this condition to the list of non-equi-join conditions. nonEquiList.add(condition); }
public static boolean isComparisonOp(RexCall call) { return call.getKind().belongsTo(SqlKind.COMPARISON); }
private RexNode convert(final ExprNodeGenericFuncDesc func) throws SemanticException { ExprNodeDesc tmpExprNode; RexNode tmpRN; List<RexNode> childRexNodeLst = new LinkedList<RexNode>(); Builder<RelDataType> argTypeBldr = ImmutableList.<RelDataType>builder(); // TODO: 1) Expand to other functions as needed 2) What about types other than primitive. TypeInfo tgtDT = null; GenericUDF tgtUdf = func.getGenericUDF(); boolean isNumeric = (tgtUdf instanceof GenericUDFBaseBinary && func.getTypeInfo().getCategory() == Category.PRIMITIVE && (PrimitiveGrouping.NUMERIC_GROUP == PrimitiveObjectInspectorUtils.getPrimitiveGrouping( ((PrimitiveTypeInfo) func.getTypeInfo()).getPrimitiveCategory()))); boolean isCompare = !isNumeric && tgtUdf instanceof GenericUDFBaseCompare; if (isNumeric) { tgtDT = func.getTypeInfo(); assert func.getChildren().size() == 2; // TODO: checking 2 children is useless, compare already does that. } else if (isCompare && (func.getChildren().size() == 2)) { tgtDT = FunctionRegistry.getCommonClassForComparison( func.getChildren().get(0).getTypeInfo(), func.getChildren().get(1).getTypeInfo()); } for (ExprNodeDesc childExpr : func.getChildren()) { tmpExprNode = childExpr; if (tgtDT != null && TypeInfoUtils.isConversionRequiredForComparison(tgtDT, childExpr.getTypeInfo())) { if (isCompare) { // For compare, we will convert requisite children tmpExprNode = ParseUtils.createConversionCast(childExpr, (PrimitiveTypeInfo) tgtDT); } else if (isNumeric) { // For numeric, we'll do minimum necessary cast - if we cast to the type // of expression, bad things will happen. PrimitiveTypeInfo minArgType = ExprNodeDescUtils.deriveMinArgumentCast(childExpr, tgtDT); tmpExprNode = ParseUtils.createConversionCast(childExpr, minArgType); } else { throw new AssertionError("Unexpected " + tgtDT + " - not a numeric op or compare"); } } argTypeBldr.add(TypeConverter.convert(tmpExprNode.getTypeInfo(), cluster.getTypeFactory())); tmpRN = convert(tmpExprNode); childRexNodeLst.add(tmpRN); } // See if this is an explicit cast. RexNode expr = null; RelDataType retType = null; expr = handleExplicitCast(func, childRexNodeLst); if (expr == null) { // This is not a cast; process the function. retType = TypeConverter.convert(func.getTypeInfo(), cluster.getTypeFactory()); SqlOperator calciteOp = SqlFunctionConverter.getCalciteOperator( func.getFuncText(), func.getGenericUDF(), argTypeBldr.build(), retType); expr = cluster.getRexBuilder().makeCall(calciteOp, childRexNodeLst); } else { retType = expr.getType(); } // TODO: Cast Function in Calcite have a bug where it infer type on cast throws // an exception if (flattenExpr && (expr instanceof RexCall) && !(((RexCall) expr).getOperator() instanceof SqlCastFunction)) { RexCall call = (RexCall) expr; expr = cluster .getRexBuilder() .makeCall( retType, call.getOperator(), RexUtil.flatten(call.getOperands(), call.getOperator())); } return expr; }