public Type getJavaClass(RelDataType type) { if (type instanceof JavaType) { JavaType javaType = (JavaType) type; return javaType.getJavaClass(); } if (type.isStruct() && type.getFieldCount() == 1) { return getJavaClass(type.getFieldList().get(0).getType()); } if (type instanceof BasicSqlType || type instanceof IntervalSqlType) { switch (type.getSqlTypeName()) { case VARCHAR: case CHAR: return String.class; case DATE: case TIME: case INTEGER: case INTERVAL_YEAR_MONTH: return type.isNullable() ? Integer.class : int.class; case TIMESTAMP: case BIGINT: case INTERVAL_DAY_TIME: return type.isNullable() ? Long.class : long.class; case SMALLINT: return type.isNullable() ? Short.class : short.class; case TINYINT: return type.isNullable() ? Byte.class : byte.class; case DECIMAL: return BigDecimal.class; case BOOLEAN: return type.isNullable() ? Boolean.class : boolean.class; case DOUBLE: case FLOAT: // sic return type.isNullable() ? Double.class : double.class; case REAL: return type.isNullable() ? Float.class : float.class; case BINARY: case VARBINARY: return ByteString.class; case ARRAY: return Array.class; case ANY: return Object.class; } } switch (type.getSqlTypeName()) { case ROW: assert type instanceof RelRecordType; if (type instanceof JavaRecordType) { return ((JavaRecordType) type).clazz; } else { return createSyntheticType((RelRecordType) type); } case MAP: return Map.class; case ARRAY: case MULTISET: return List.class; } return null; }
private static Comparable zeroValue(RelDataType type) { switch (type.getSqlTypeName()) { case CHAR: return new NlsString(Spaces.of(type.getPrecision()), null, null); case VARCHAR: return new NlsString("", null, null); case BINARY: return new ByteString(new byte[type.getPrecision()]); case VARBINARY: return ByteString.EMPTY; case TINYINT: case SMALLINT: case INTEGER: case BIGINT: case DECIMAL: case FLOAT: case REAL: case DOUBLE: return BigDecimal.ZERO; case BOOLEAN: return false; case TIME: case DATE: case TIMESTAMP: return DateTimeUtils.ZERO_CALENDAR; default: throw Util.unexpected(type.getSqlTypeName()); } }
/** * 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); }
/** * Estimates the average size (in bytes) of a value of a type. * * <p>Nulls count as 1 byte. */ public double typeValueSize(RelDataType type, Comparable value) { if (value == null) { return 1d; } switch (type.getSqlTypeName()) { case BOOLEAN: case TINYINT: return 1d; case SMALLINT: return 2d; case INTEGER: case FLOAT: case REAL: case DATE: case TIME: return 4d; case BIGINT: case DOUBLE: case TIMESTAMP: case INTERVAL_DAY_TIME: case INTERVAL_YEAR_MONTH: return 8d; case BINARY: case VARBINARY: return ((ByteString) value).length(); case CHAR: case VARCHAR: return ((NlsString) value).getValue().length() * BYTES_PER_CHARACTER; default: return 32; } }
@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; }
/** * Converts the type of a value to comply with {@link * org.apache.calcite.rex.RexLiteral#valueMatchesType}. */ private static Object clean(Object o, RelDataType type) { if (o == null) { return null; } final Calendar calendar; switch (type.getSqlTypeName()) { case TINYINT: case SMALLINT: case INTEGER: case BIGINT: case DECIMAL: case INTERVAL_YEAR_MONTH: case INTERVAL_DAY_TIME: if (o instanceof BigDecimal) { return o; } return new BigDecimal(((Number) o).longValue()); case FLOAT: case REAL: case DOUBLE: if (o instanceof BigDecimal) { return o; } return new BigDecimal(((Number) o).doubleValue()); case CHAR: case VARCHAR: if (o instanceof NlsString) { return o; } return new NlsString((String) o, type.getCharset().name(), type.getCollation()); case TIME: if (o instanceof Calendar) { return o; } calendar = Calendar.getInstance(DateTimeUtils.GMT_ZONE); calendar.setTimeInMillis((Integer) o); return calendar; case DATE: if (o instanceof Calendar) { return o; } calendar = Calendar.getInstance(DateTimeUtils.GMT_ZONE); calendar.setTimeInMillis(0); calendar.add(Calendar.DAY_OF_YEAR, (Integer) o); return calendar; case TIMESTAMP: if (o instanceof Calendar) { return o; } calendar = Calendar.getInstance(DateTimeUtils.GMT_ZONE); calendar.setTimeInMillis((Long) o); return calendar; default: return o; } }
/** * Internal method to create a call to a literal. Code outside this package should call one of the * type-specific methods such as {@link #makeDateLiteral(Calendar)}, {@link * #makeLiteral(boolean)}, {@link #makeLiteral(String)}. * * @param o Value of literal, must be appropriate for the type * @param type Type of literal * @param typeName SQL type of literal * @return Literal */ protected RexLiteral makeLiteral(Comparable o, RelDataType type, SqlTypeName typeName) { // All literals except NULL have NOT NULL types. type = typeFactory.createTypeWithNullability(type, o == null); if (typeName == SqlTypeName.CHAR) { // Character literals must have a charset and collation. Populate // from the type if necessary. assert o instanceof NlsString; NlsString nlsString = (NlsString) o; if ((nlsString.getCollation() == null) || (nlsString.getCharset() == null)) { assert type.getSqlTypeName() == SqlTypeName.CHAR; assert type.getCharset().name() != null; assert type.getCollation() != null; o = new NlsString(nlsString.getValue(), type.getCharset().name(), type.getCollation()); } } return new RexLiteral(o, type, typeName); }
public RelDataType toSql(RelDataType type) { if (type instanceof RelRecordType) { return createStructType( Lists.transform( type.getFieldList(), new Function<RelDataTypeField, RelDataType>() { public RelDataType apply(RelDataTypeField a0) { return toSql(a0.getType()); } }), type.getFieldNames()); } if (type instanceof JavaType) { return createTypeWithNullability(createSqlType(type.getSqlTypeName()), type.isNullable()); } return type; }
private RexNode makeCastBooleanToExact(RelDataType toType, RexNode exp) { final RexNode casted = makeCall( SqlStdOperatorTable.CASE, exp, makeExactLiteral(BigDecimal.ONE, toType), makeZeroLiteral(toType)); if (!exp.getType().isNullable()) { return casted; } return makeCall( toType, SqlStdOperatorTable.CASE, ImmutableList.<RexNode>of( makeCall(SqlStdOperatorTable.IS_NOT_NULL, exp), casted, makeNullLiteral(toType.getSqlTypeName()))); }
/** * Estimates the average size (in bytes) of a value of a type. * * <p>We assume that the proportion of nulls is negligible, even if the type is nullable. */ public Double averageTypeValueSize(RelDataType type) { switch (type.getSqlTypeName()) { case BOOLEAN: case TINYINT: return 1d; case SMALLINT: return 2d; case INTEGER: case REAL: case DECIMAL: case DATE: case TIME: return 4d; case BIGINT: case DOUBLE: case FLOAT: // sic case TIMESTAMP: case INTERVAL_DAY_TIME: case INTERVAL_YEAR_MONTH: return 8d; case BINARY: return (double) type.getPrecision(); case VARBINARY: return Math.min((double) type.getPrecision(), 100d); case CHAR: return (double) type.getPrecision() * BYTES_PER_CHARACTER; case VARCHAR: // Even in large (say VARCHAR(2000)) columns most strings are small return Math.min((double) type.getPrecision() * BYTES_PER_CHARACTER, 100d); case ROW: Double average = 0.0; for (RelDataTypeField field : type.getFieldList()) { average += averageTypeValueSize(field.getType()); } return average; default: return null; } }
/** * Creates a literal of a given type. The value is assumed to be compatible with the type. * * @param value Value * @param type Type * @param allowCast Whether to allow a cast. If false, value is always a {@link RexLiteral} but * may not be the exact type * @return Simple literal, or cast simple literal */ public RexNode makeLiteral(Object value, RelDataType type, boolean allowCast) { if (value == null) { return makeCast(type, constantNull); } if (type.isNullable()) { final RelDataType typeNotNull = typeFactory.createTypeWithNullability(type, false); RexNode literalNotNull = makeLiteral(value, typeNotNull, allowCast); return makeAbstractCast(type, literalNotNull); } value = clean(value, type); RexLiteral literal; final List<RexNode> operands; switch (type.getSqlTypeName()) { case CHAR: return makeCharLiteral(padRight((NlsString) value, type.getPrecision())); case VARCHAR: literal = makeCharLiteral((NlsString) value); if (allowCast) { return makeCast(type, literal); } else { return literal; } case BINARY: return makeBinaryLiteral(padRight((ByteString) value, type.getPrecision())); case VARBINARY: literal = makeBinaryLiteral((ByteString) value); if (allowCast) { return makeCast(type, literal); } else { return literal; } case TINYINT: case SMALLINT: case INTEGER: case BIGINT: case DECIMAL: return makeExactLiteral((BigDecimal) value, type); case FLOAT: case REAL: case DOUBLE: return makeApproxLiteral((BigDecimal) value, type); case BOOLEAN: return (Boolean) value ? booleanTrue : booleanFalse; case TIME: return makeTimeLiteral((Calendar) value, type.getPrecision()); case DATE: return makeDateLiteral((Calendar) value); case TIMESTAMP: return makeTimestampLiteral((Calendar) value, type.getPrecision()); case INTERVAL_YEAR_MONTH: case INTERVAL_DAY_TIME: return makeIntervalLiteral((BigDecimal) value, type.getIntervalQualifier()); case MAP: final MapSqlType mapType = (MapSqlType) type; @SuppressWarnings("unchecked") final Map<Object, Object> map = (Map) value; operands = new ArrayList<RexNode>(); for (Map.Entry<Object, Object> entry : map.entrySet()) { operands.add(makeLiteral(entry.getKey(), mapType.getKeyType(), allowCast)); operands.add(makeLiteral(entry.getValue(), mapType.getValueType(), allowCast)); } return makeCall(SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR, operands); case ARRAY: final ArraySqlType arrayType = (ArraySqlType) type; @SuppressWarnings("unchecked") final List<Object> listValue = (List) value; operands = new ArrayList<RexNode>(); for (Object entry : listValue) { operands.add(makeLiteral(entry, arrayType.getComponentType(), allowCast)); } return makeCall(SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR, operands); case ANY: return makeLiteral(value, guessType(value), allowCast); default: throw Util.unexpected(type.getSqlTypeName()); } }
/** * Creates an approximate numeric literal (double or float). * * @param bd literal value * @param type approximate numeric type * @return new literal */ public RexLiteral makeApproxLiteral(BigDecimal bd, RelDataType type) { assert SqlTypeFamily.APPROXIMATE_NUMERIC.getTypeNames().contains(type.getSqlTypeName()); return makeLiteral(bd, type, SqlTypeName.DOUBLE); }