/** Creates a cluster. */ RelOptCluster( RelOptQuery query, RelOptPlanner planner, RelDataTypeFactory typeFactory, RexBuilder rexBuilder) { assert planner != null; assert typeFactory != null; this.query = query; this.planner = planner; this.typeFactory = typeFactory; this.rexBuilder = rexBuilder; this.originalExpression = rexBuilder.makeLiteral("?"); // set up a default rel metadata provider, // giving the planner first crack at everything setMetadataProvider(new DefaultRelMetadataProvider()); this.emptyTraitSet = planner.emptyTraitSet(); }
/** * Unit test for logic functions {@link * org.apache.calcite.plan.SubstitutionVisitor#mayBeSatisfiable} and {@link * org.apache.calcite.plan.SubstitutionVisitor#simplify}. */ @Test public void testSatisfiable() { // TRUE may be satisfiable checkSatisfiable(rexBuilder.makeLiteral(true), "true"); // FALSE is not satisfiable checkNotSatisfiable(rexBuilder.makeLiteral(false)); // The expression "$0 = 1". final RexNode i0_eq_0 = rexBuilder.makeCall( SqlStdOperatorTable.EQUALS, rexBuilder.makeInputRef(typeFactory.createType(int.class), 0), rexBuilder.makeExactLiteral(BigDecimal.ZERO)); // "$0 = 1" may be satisfiable checkSatisfiable(i0_eq_0, "=($0, 0)"); // "$0 = 1 AND TRUE" may be satisfiable final RexNode e0 = rexBuilder.makeCall(SqlStdOperatorTable.AND, i0_eq_0, rexBuilder.makeLiteral(true)); checkSatisfiable(e0, "=($0, 0)"); // "$0 = 1 AND FALSE" is not satisfiable final RexNode e1 = rexBuilder.makeCall(SqlStdOperatorTable.AND, i0_eq_0, rexBuilder.makeLiteral(false)); checkNotSatisfiable(e1); // "$0 = 0 AND NOT $0 = 0" is not satisfiable final RexNode e2 = rexBuilder.makeCall( SqlStdOperatorTable.AND, i0_eq_0, rexBuilder.makeCall(SqlStdOperatorTable.NOT, i0_eq_0)); checkNotSatisfiable(e2); // "TRUE AND NOT $0 = 0" may be satisfiable. Can simplify. final RexNode e3 = rexBuilder.makeCall( SqlStdOperatorTable.AND, rexBuilder.makeLiteral(true), rexBuilder.makeCall(SqlStdOperatorTable.NOT, i0_eq_0)); checkSatisfiable(e3, "NOT(=($0, 0))"); // The expression "$1 = 1". final RexNode i1_eq_1 = rexBuilder.makeCall( SqlStdOperatorTable.EQUALS, rexBuilder.makeInputRef(typeFactory.createType(int.class), 1), rexBuilder.makeExactLiteral(BigDecimal.ONE)); // "$0 = 0 AND $1 = 1 AND NOT $0 = 0" is not satisfiable final RexNode e4 = rexBuilder.makeCall( SqlStdOperatorTable.AND, i0_eq_0, rexBuilder.makeCall( SqlStdOperatorTable.AND, i1_eq_1, rexBuilder.makeCall(SqlStdOperatorTable.NOT, i0_eq_0))); checkNotSatisfiable(e4); // "$0 = 0 AND NOT $1 = 1" may be satisfiable. Can't simplify. final RexNode e5 = rexBuilder.makeCall( SqlStdOperatorTable.AND, i0_eq_0, rexBuilder.makeCall(SqlStdOperatorTable.NOT, i1_eq_1)); checkSatisfiable(e5, "AND(=($0, 0), NOT(=($1, 1)))"); // "$0 = 0 AND NOT ($0 = 0 AND $1 = 1)" may be satisfiable. Can simplify. final RexNode e6 = rexBuilder.makeCall( SqlStdOperatorTable.AND, i0_eq_0, rexBuilder.makeCall( SqlStdOperatorTable.NOT, rexBuilder.makeCall(SqlStdOperatorTable.AND, i0_eq_0, i1_eq_1))); checkSatisfiable(e6, "AND(=($0, 0), NOT(AND(=($0, 0), =($1, 1))))"); // "$0 = 0 AND ($1 = 1 AND NOT ($0 = 0))" is not satisfiable. final RexNode e7 = rexBuilder.makeCall( SqlStdOperatorTable.AND, i0_eq_0, rexBuilder.makeCall( SqlStdOperatorTable.AND, i1_eq_1, rexBuilder.makeCall(SqlStdOperatorTable.NOT, i0_eq_0))); checkNotSatisfiable(e7); // The expression "$2". final RexInputRef i2 = rexBuilder.makeInputRef(typeFactory.createType(boolean.class), 2); // The expression "$3". final RexInputRef i3 = rexBuilder.makeInputRef(typeFactory.createType(boolean.class), 3); // The expression "$4". final RexInputRef i4 = rexBuilder.makeInputRef(typeFactory.createType(boolean.class), 4); // "$0 = 0 AND $2 AND $3 AND NOT ($2 AND $3 AND $4) AND NOT ($2 AND $4)" may // be satisfiable. Can't simplify. final RexNode e8 = rexBuilder.makeCall( SqlStdOperatorTable.AND, i0_eq_0, rexBuilder.makeCall( SqlStdOperatorTable.AND, i2, rexBuilder.makeCall( SqlStdOperatorTable.AND, i3, rexBuilder.makeCall( SqlStdOperatorTable.NOT, rexBuilder.makeCall(SqlStdOperatorTable.AND, i2, i3, i4)), rexBuilder.makeCall(SqlStdOperatorTable.NOT, i4)))); checkSatisfiable(e8, "AND(=($0, 0), $2, $3, NOT(AND($2, $3, $4)), NOT($4))"); }
protected RexNode convert(ExprNodeConstantDesc literal) throws CalciteSemanticException { RexBuilder rexBuilder = cluster.getRexBuilder(); RelDataTypeFactory dtFactory = rexBuilder.getTypeFactory(); PrimitiveTypeInfo hiveType = (PrimitiveTypeInfo) literal.getTypeInfo(); RelDataType calciteDataType = TypeConverter.convert(hiveType, dtFactory); PrimitiveCategory hiveTypeCategory = hiveType.getPrimitiveCategory(); ConstantObjectInspector coi = literal.getWritableObjectInspector(); Object value = ObjectInspectorUtils.copyToStandardJavaObject(coi.getWritableConstantValue(), coi); RexNode calciteLiteral = null; // TODO: Verify if we need to use ConstantObjectInspector to unwrap data switch (hiveTypeCategory) { case BOOLEAN: calciteLiteral = rexBuilder.makeLiteral(((Boolean) value).booleanValue()); break; case BYTE: calciteLiteral = rexBuilder.makeExactLiteral(new BigDecimal((Byte) value), calciteDataType); break; case SHORT: calciteLiteral = rexBuilder.makeExactLiteral(new BigDecimal((Short) value), calciteDataType); break; case INT: calciteLiteral = rexBuilder.makeExactLiteral(new BigDecimal((Integer) value)); break; case LONG: calciteLiteral = rexBuilder.makeBigintLiteral(new BigDecimal((Long) value)); break; // TODO: is Decimal an exact numeric or approximate numeric? case DECIMAL: if (value instanceof HiveDecimal) { value = ((HiveDecimal) value).bigDecimalValue(); } else if (value instanceof Decimal128) { value = ((Decimal128) value).toBigDecimal(); } if (value == null) { // We have found an invalid decimal value while enforcing precision and // scale. Ideally, // we would replace it with null here, which is what Hive does. However, // we need to plumb // this thru up somehow, because otherwise having different expression // type in AST causes // the plan generation to fail after CBO, probably due to some residual // state in SA/QB. // For now, we will not run CBO in the presence of invalid decimal // literals. throw new CalciteSemanticException( "Expression " + literal.getExprString() + " is not a valid decimal", UnsupportedFeature.Invalid_decimal); // TODO: return createNullLiteral(literal); } BigDecimal bd = (BigDecimal) value; BigInteger unscaled = bd.unscaledValue(); if (unscaled.compareTo(MIN_LONG_BI) >= 0 && unscaled.compareTo(MAX_LONG_BI) <= 0) { calciteLiteral = rexBuilder.makeExactLiteral(bd); } else { // CBO doesn't support unlimited precision decimals. In practice, this // will work... // An alternative would be to throw CboSemanticException and fall back // to no CBO. RelDataType relType = cluster .getTypeFactory() .createSqlType(SqlTypeName.DECIMAL, bd.scale(), unscaled.toString().length()); calciteLiteral = rexBuilder.makeExactLiteral(bd, relType); } break; case FLOAT: calciteLiteral = rexBuilder.makeApproxLiteral(new BigDecimal((Float) value), calciteDataType); break; case DOUBLE: calciteLiteral = rexBuilder.makeApproxLiteral(new BigDecimal((Double) value), calciteDataType); break; case CHAR: if (value instanceof HiveChar) { value = ((HiveChar) value).getValue(); } calciteLiteral = rexBuilder.makeCharLiteral(asUnicodeString((String) value)); break; case VARCHAR: if (value instanceof HiveVarchar) { value = ((HiveVarchar) value).getValue(); } calciteLiteral = rexBuilder.makeCharLiteral(asUnicodeString((String) value)); break; case STRING: calciteLiteral = rexBuilder.makeCharLiteral(asUnicodeString((String) value)); break; case DATE: Calendar cal = new GregorianCalendar(); cal.setTime((Date) value); calciteLiteral = rexBuilder.makeDateLiteral(cal); break; case TIMESTAMP: Calendar c = null; if (value instanceof Calendar) { c = (Calendar) value; } else { c = Calendar.getInstance(); c.setTimeInMillis(((Timestamp) value).getTime()); } calciteLiteral = rexBuilder.makeTimestampLiteral(c, RelDataType.PRECISION_NOT_SPECIFIED); break; case INTERVAL_YEAR_MONTH: // Calcite year-month literal value is months as BigDecimal BigDecimal totalMonths = BigDecimal.valueOf(((HiveIntervalYearMonth) value).getTotalMonths()); calciteLiteral = rexBuilder.makeIntervalLiteral( totalMonths, new SqlIntervalQualifier(TimeUnit.YEAR, TimeUnit.MONTH, new SqlParserPos(1, 1))); break; case INTERVAL_DAY_TIME: // Calcite day-time interval is millis value as BigDecimal // Seconds converted to millis BigDecimal secsValueBd = BigDecimal.valueOf(((HiveIntervalDayTime) value).getTotalSeconds() * 1000); // Nanos converted to millis BigDecimal nanosValueBd = BigDecimal.valueOf(((HiveIntervalDayTime) value).getNanos(), 6); calciteLiteral = rexBuilder.makeIntervalLiteral( secsValueBd.add(nanosValueBd), new SqlIntervalQualifier(TimeUnit.DAY, TimeUnit.SECOND, new SqlParserPos(1, 1))); break; case VOID: calciteLiteral = cluster .getRexBuilder() .makeLiteral(null, cluster.getTypeFactory().createSqlType(SqlTypeName.NULL), true); break; case BINARY: case UNKNOWN: default: throw new RuntimeException("UnSupported Literal"); } return calciteLiteral; }