@Override public String visitLiteral(RexLiteral rexLiteral) { Object v = rexLiteral.getValue(); RelDataType ty = rexLiteral.getType(); switch (rexLiteral.getTypeName()) { case BOOLEAN: return v.toString(); case CHAR: return CompilerUtil.escapeJavaString(((NlsString) v).getValue(), true); case NULL: return "((" + ((Class<?>) typeFactory.getJavaClass(ty)).getCanonicalName() + ")null)"; case DOUBLE: case BIGINT: case DECIMAL: switch (ty.getSqlTypeName()) { case TINYINT: case SMALLINT: case INTEGER: return Long.toString(((BigDecimal) v).longValueExact()); case BIGINT: return Long.toString(((BigDecimal) v).longValueExact()) + 'L'; case DECIMAL: case FLOAT: case REAL: case DOUBLE: return Util.toScientificNotation((BigDecimal) v); } break; default: throw new UnsupportedOperationException(); } return null; }
/** * Creates a call to the CAST operator, expanding if possible. * * @param type Type to cast to * @param exp Expression being cast * @return Call to CAST operator */ public RexNode makeCast(RelDataType type, RexNode exp) { final SqlTypeName sqlType = type.getSqlTypeName(); if (exp instanceof RexLiteral) { RexLiteral literal = (RexLiteral) exp; Comparable value = literal.getValue(); if (RexLiteral.valueMatchesType(value, sqlType, false) && (!(value instanceof NlsString) || (type.getPrecision() >= ((NlsString) value).getValue().length())) && (!(value instanceof ByteString) || (type.getPrecision() >= ((ByteString) value).length()))) { switch (literal.getTypeName()) { case CHAR: if (value instanceof NlsString) { value = ((NlsString) value).rtrim(); } break; case TIMESTAMP: case TIME: final Calendar calendar = (Calendar) value; int scale = type.getScale(); if (scale == RelDataType.SCALE_NOT_SPECIFIED) { scale = 0; } calendar.setTimeInMillis( SqlFunctions.round( calendar.getTimeInMillis(), DateTimeUtils.powerX(10, 3 - scale))); break; case INTERVAL_DAY_TIME: BigDecimal value2 = (BigDecimal) value; final long multiplier = literal.getType().getIntervalQualifier().getStartUnit().multiplier; SqlTypeName typeName = type.getSqlTypeName(); // Not all types are allowed for literals switch (typeName) { case INTEGER: typeName = SqlTypeName.BIGINT; } return makeLiteral( value2.divide(BigDecimal.valueOf(multiplier), 0, BigDecimal.ROUND_HALF_DOWN), type, typeName); } return makeLiteral(value, type, literal.getTypeName()); } } else if (SqlTypeUtil.isInterval(type) && SqlTypeUtil.isExactNumeric(exp.getType())) { return makeCastExactToInterval(type, exp); } else if (SqlTypeUtil.isExactNumeric(type) && SqlTypeUtil.isInterval(exp.getType())) { return makeCastIntervalToExact(type, exp); } else if (sqlType == SqlTypeName.BOOLEAN && SqlTypeUtil.isExactNumeric(exp.getType())) { return makeCastExactToBoolean(type, exp); } else if (exp.getType().getSqlTypeName() == SqlTypeName.BOOLEAN && SqlTypeUtil.isExactNumeric(type)) { return makeCastBooleanToExact(type, exp); } return makeAbstractCast(type, exp); }
/** @return true if all tuples match rowType; otherwise, assert on mismatch */ private boolean assertRowType() { for (List<RexLiteral> tuple : tuples) { assert tuple.size() == rowType.getFieldCount(); for (Pair<RexLiteral, RelDataTypeField> pair : Pair.zip(tuple, rowType.getFieldList())) { RexLiteral literal = pair.left; RelDataType fieldType = pair.right.getType(); // TODO jvs 19-Feb-2006: strengthen this a bit. For example, // overflow, rounding, and padding/truncation must already have // been dealt with. if (!RexLiteral.isNullLiteral(literal)) { assert (SqlTypeUtil.canAssignFrom(fieldType, literal.getType())); } } } return true; }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link ProjectRel}. */ public TrimResult trimFields( ProjectRel project, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = project.getRowType(); final int fieldCount = rowType.getFieldCount(); final RelNode input = project.getChild(); final RelDataType inputRowType = input.getRowType(); // Which fields are required from the input? BitSet inputFieldsUsed = new BitSet(inputRowType.getFieldCount()); final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<RelDataTypeField>(extraFields); RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputFieldsUsed, inputExtraFields); for (Ord<RexNode> ord : Ord.zip(project.getProjects())) { if (fieldsUsed.get(ord.i)) { ord.e.accept(inputFinder); } } // Create input with trimmed columns. TrimResult trimResult = trimChild(project, input, inputFieldsUsed, inputExtraFields); RelNode newInput = trimResult.left; final Mapping inputMapping = trimResult.right; // If the input is unchanged, and we need to project all columns, // there's nothing we can do. if (newInput == input && fieldsUsed.cardinality() == fieldCount) { return new TrimResult(project, Mappings.createIdentity(fieldCount)); } // Some parts of the system can't handle rows with zero fields, so // pretend that one field is used. if (fieldsUsed.cardinality() == 0) { final Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, 1); final RexLiteral expr = project.getCluster().getRexBuilder().makeExactLiteral(BigDecimal.ZERO); RelDataType newRowType = project .getCluster() .getTypeFactory() .createStructType( Collections.singletonList(expr.getType()), Collections.singletonList("DUMMY")); ProjectRel newProject = new ProjectRel( project.getCluster(), project.getCluster().traitSetOf(RelCollationImpl.EMPTY), newInput, Collections.<RexNode>singletonList(expr), newRowType, project.getFlags()); return new TrimResult(newProject, mapping); } // Build new project expressions, and populate the mapping. List<RexNode> newProjectExprList = new ArrayList<RexNode>(); final RexVisitor<RexNode> shuttle = new RexPermuteInputsShuttle(inputMapping, newInput); final Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, fieldsUsed.cardinality()); for (Ord<RexNode> ord : Ord.zip(project.getProjects())) { if (fieldsUsed.get(ord.i)) { mapping.set(ord.i, newProjectExprList.size()); RexNode newProjectExpr = ord.e.accept(shuttle); newProjectExprList.add(newProjectExpr); } } final RelDataType newRowType = project .getCluster() .getTypeFactory() .createStructType(Mappings.apply3(mapping, rowType.getFieldList())); final List<RelCollation> newCollations = RexUtil.apply(inputMapping, project.getCollationList()); final RelNode newProject; if (RemoveTrivialProjectRule.isIdentity( newProjectExprList, newRowType, newInput.getRowType())) { // The new project would be the identity. It is equivalent to return // its child. newProject = newInput; } else { newProject = new ProjectRel( project.getCluster(), project .getCluster() .traitSetOf( newCollations.isEmpty() ? RelCollationImpl.EMPTY : newCollations.get(0)), newInput, newProjectExprList, newRowType, project.getFlags()); assert newProject.getClass() == project.getClass(); } return new TrimResult(newProject, mapping); }
/** Converts an expression from {@link RexNode} to {@link SqlNode} format. */ SqlNode toSql(RexProgram program, RexNode rex) { if (rex instanceof RexLocalRef) { final int index = ((RexLocalRef) rex).getIndex(); return toSql(program, program.getExprList().get(index)); } else if (rex instanceof RexInputRef) { return field(((RexInputRef) rex).getIndex()); } else if (rex instanceof RexLiteral) { final RexLiteral literal = (RexLiteral) rex; switch (literal.getTypeName().getFamily()) { case CHARACTER: return SqlLiteral.createCharString((String) literal.getValue2(), POS); case NUMERIC: case EXACT_NUMERIC: return SqlLiteral.createExactNumeric(literal.getValue().toString(), POS); case APPROXIMATE_NUMERIC: return SqlLiteral.createApproxNumeric(literal.getValue().toString(), POS); case BOOLEAN: return SqlLiteral.createBoolean((Boolean) literal.getValue(), POS); case DATE: return SqlLiteral.createDate((Calendar) literal.getValue(), POS); case TIME: return SqlLiteral.createTime( (Calendar) literal.getValue(), literal.getType().getPrecision(), POS); case TIMESTAMP: return SqlLiteral.createTimestamp( (Calendar) literal.getValue(), literal.getType().getPrecision(), POS); case ANY: switch (literal.getTypeName()) { case NULL: return SqlLiteral.createNull(POS); // fall through } default: throw new AssertionError(literal + ": " + literal.getTypeName()); } } else if (rex instanceof RexCall) { final RexCall call = (RexCall) rex; final SqlOperator op = call.getOperator(); final List<SqlNode> nodeList = toSql(program, call.getOperands()); if (op == SqlStdOperatorTable.CAST) { RelDataType type = call.getType(); if (type.getSqlTypeName() == SqlTypeName.VARCHAR && dialect.getDatabaseProduct() == SqlDialect.DatabaseProduct.MYSQL) { // MySQL doesn't have a VARCHAR type, only CHAR. nodeList.add( new SqlDataTypeSpec( new SqlIdentifier("CHAR", POS), type.getPrecision(), -1, null, null, POS)); } else { nodeList.add(toSql(type)); } } if (op == SqlStdOperatorTable.CASE) { final SqlNode valueNode; final List<SqlNode> whenList = Expressions.list(); final List<SqlNode> thenList = Expressions.list(); final SqlNode elseNode; if (nodeList.size() % 2 == 0) { // switched: // "case x when v1 then t1 when v2 then t2 ... else e end" valueNode = nodeList.get(0); for (int i = 1; i < nodeList.size() - 1; i += 2) { whenList.add(nodeList.get(i)); thenList.add(nodeList.get(i + 1)); } } else { // other: "case when w1 then t1 when w2 then t2 ... else e end" valueNode = null; for (int i = 0; i < nodeList.size() - 1; i += 2) { whenList.add(nodeList.get(i)); thenList.add(nodeList.get(i + 1)); } } elseNode = nodeList.get(nodeList.size() - 1); return op.createCall( POS, valueNode, new SqlNodeList(whenList, POS), new SqlNodeList(thenList, POS), elseNode); } if (op instanceof SqlBinaryOperator && nodeList.size() > 2) { // In RexNode trees, OR and AND have any number of children; // SqlCall requires exactly 2. So, convert to a left-deep binary tree. return createLeftCall(op, nodeList); } return op.createCall(new SqlNodeList(nodeList, POS)); } else { throw new AssertionError(rex); } }