/** * Returns the type the row which results when two relations are joined. * * <p>The resulting row type consists of the system fields (if any), followed by the fields of the * left type, followed by the fields of the right type. The field name list, if present, overrides * the original names of the fields. * * @param typeFactory Type factory * @param leftType Type of left input to join * @param rightType Type of right input to join * @param fieldNameList If not null, overrides the original names of the fields * @param systemFieldList List of system fields that will be prefixed to output row type; * typically empty but must not be null * @return type of row which results when two relations are joined */ public static RelDataType createJoinType( RelDataTypeFactory typeFactory, RelDataType leftType, RelDataType rightType, List<String> fieldNameList, List<RelDataTypeField> systemFieldList) { assert (fieldNameList == null) || (fieldNameList.size() == (systemFieldList.size() + leftType.getFieldCount() + rightType.getFieldCount())); List<String> nameList = new ArrayList<>(); final List<RelDataType> typeList = new ArrayList<>(); // Use a set to keep track of the field names; this is needed // to ensure that the contains() call to check for name uniqueness // runs in constant time; otherwise, if the number of fields is large, // doing a contains() on a list can be expensive. final Set<String> uniqueNameList = typeFactory.getTypeSystem().isSchemaCaseSensitive() ? new HashSet<String>() : new TreeSet<>(String.CASE_INSENSITIVE_ORDER); addFields(systemFieldList, typeList, nameList, uniqueNameList); addFields(leftType.getFieldList(), typeList, nameList, uniqueNameList); if (rightType != null) { addFields(rightType.getFieldList(), typeList, nameList, uniqueNameList); } if (fieldNameList != null) { assert fieldNameList.size() == nameList.size(); nameList = fieldNameList; } return typeFactory.createStructType(typeList, nameList); }
@Override public AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall e) { return AggregateCall.create( new HiveSqlCountAggFunction( isDistinct, returnTypeInference, operandTypeInference, operandTypeChecker), false, ImmutableIntList.of(), -1, typeFactory.createTypeWithNullability( typeFactory.createSqlType(SqlTypeName.BIGINT), true), "count"); }
private RexNode makeCastExactToInterval(RelDataType toType, RexNode exp) { IntervalSqlType intervalType = (IntervalSqlType) toType; TimeUnit endUnit = intervalType.getIntervalQualifier().getEndUnit(); if (endUnit == null) { endUnit = intervalType.getIntervalQualifier().getStartUnit(); } int scale = 0; if (endUnit == TimeUnit.SECOND) { scale = Math.min( intervalType .getIntervalQualifier() .getFractionalSecondPrecision(typeFactory.getTypeSystem()), 3); } BigDecimal multiplier = BigDecimal.valueOf(endUnit.multiplier).divide(BigDecimal.TEN.pow(scale)); RelDataType decimalType = getTypeFactory() .createSqlType(SqlTypeName.DECIMAL, scale + intervalType.getPrecision(), scale); RexNode value = decodeIntervalOrDecimal(ensureType(decimalType, exp, true)); if (multiplier.longValue() != 1) { value = makeCall(SqlStdOperatorTable.MULTIPLY, value, makeExactLiteral(multiplier)); } return encodeIntervalOrDecimal(value, toType, false); }
/** * Creates a RexBuilder. * * @param typeFactory Type factory */ public RexBuilder(RelDataTypeFactory typeFactory) { this.typeFactory = typeFactory; this.booleanTrue = makeLiteral( Boolean.TRUE, typeFactory.createSqlType(SqlTypeName.BOOLEAN), SqlTypeName.BOOLEAN); this.booleanFalse = makeLiteral( Boolean.FALSE, typeFactory.createSqlType(SqlTypeName.BOOLEAN), SqlTypeName.BOOLEAN); this.charEmpty = makeLiteral( new NlsString("", null, null), typeFactory.createSqlType(SqlTypeName.CHAR, 0), SqlTypeName.CHAR); this.constantNull = makeLiteral(null, typeFactory.createSqlType(SqlTypeName.NULL), SqlTypeName.NULL); }
/** Creates a Timestamp literal. */ public RexLiteral makeTimestampLiteral(Calendar timestamp, int precision) { assert timestamp != null; return makeLiteral( timestamp, typeFactory.createSqlType(SqlTypeName.TIMESTAMP, precision), SqlTypeName.TIMESTAMP); }
/** Creates an interval literal. */ public RexLiteral makeIntervalLiteral(BigDecimal v, SqlIntervalQualifier intervalQualifier) { return makeLiteral( v, typeFactory.createSqlIntervalType(intervalQualifier), intervalQualifier.isYearMonth() ? SqlTypeName.INTERVAL_YEAR_MONTH : SqlTypeName.INTERVAL_DAY_TIME); }
/** Creates a double-precision literal. */ public RexLiteral makeApproxLiteral(BigDecimal bd) { // Validator should catch if underflow is allowed // If underflow is allowed, let underflow become zero if (bd.doubleValue() == 0) { bd = BigDecimal.ZERO; } return makeApproxLiteral(bd, typeFactory.createSqlType(SqlTypeName.DOUBLE)); }
/** Creates a numeric literal. */ public RexLiteral makeExactLiteral(BigDecimal bd) { RelDataType relType; int scale = bd.scale(); long l = bd.unscaledValue().longValue(); assert scale >= 0; assert scale <= typeFactory.getTypeSystem().getMaxNumericScale() : scale; assert BigDecimal.valueOf(l, scale).equals(bd); if (scale == 0) { if ((l >= Integer.MIN_VALUE) && (l <= Integer.MAX_VALUE)) { relType = typeFactory.createSqlType(SqlTypeName.INTEGER); } else { relType = typeFactory.createSqlType(SqlTypeName.BIGINT); } } else { int precision = bd.unscaledValue().toString().length(); relType = typeFactory.createSqlType(SqlTypeName.DECIMAL, scale, precision); } return makeExactLiteral(bd, relType); }
/** Creates a call to a windowed agg. */ public RexNode makeOver( RelDataType type, SqlAggFunction operator, List<RexNode> exprs, List<RexNode> partitionKeys, ImmutableList<RexFieldCollation> orderKeys, RexWindowBound lowerBound, RexWindowBound upperBound, boolean physical, boolean allowPartial, boolean nullWhenCountZero) { assert operator != null; assert exprs != null; assert partitionKeys != null; assert orderKeys != null; final RexWindow window = makeWindow(partitionKeys, orderKeys, lowerBound, upperBound, physical); final RexOver over = new RexOver(type, operator, exprs, window); RexNode result = over; // This should be correct but need time to go over test results. // Also want to look at combing with section below. if (nullWhenCountZero) { final RelDataType bigintType = getTypeFactory().createSqlType(SqlTypeName.BIGINT); result = makeCall( SqlStdOperatorTable.CASE, makeCall( SqlStdOperatorTable.GREATER_THAN, new RexOver(bigintType, SqlStdOperatorTable.COUNT, exprs, window), makeLiteral(BigDecimal.ZERO, bigintType, SqlTypeName.DECIMAL)), ensureType( type, // SUM0 is non-nullable, thus need a cast new RexOver( typeFactory.createTypeWithNullability(type, false), operator, exprs, window), false), makeCast(type, constantNull())); } if (!allowPartial) { Util.permAssert(physical, "DISALLOW PARTIAL over RANGE"); final RelDataType bigintType = getTypeFactory().createSqlType(SqlTypeName.BIGINT); // todo: read bound result = makeCall( SqlStdOperatorTable.CASE, makeCall( SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, new RexOver( bigintType, SqlStdOperatorTable.COUNT, ImmutableList.<RexNode>of(), window), makeLiteral(BigDecimal.valueOf(2), bigintType, SqlTypeName.DECIMAL)), result, constantNull); } return result; }
/** * Creates a character string literal with type CHAR and default charset and collation. * * @param s String value * @return Character string literal */ protected RexLiteral makePreciseStringLiteral(String s) { assert s != null; if (s.equals("")) { return charEmpty; } else { return makeLiteral( new NlsString(s, null, null), typeFactory.createSqlType(SqlTypeName.CHAR, s.length()), SqlTypeName.CHAR); } }
public static RelDataType createTypeFromProjection( RelDataType type, List<String> columnNameList, RelDataTypeFactory typeFactory, boolean caseSensitive) { // If the names in columnNameList and type have case-sensitive differences, // the resulting type will use those from type. These are presumably more // canonical. final List<RelDataTypeField> fields = new ArrayList<>(columnNameList.size()); for (String name : columnNameList) { RelDataTypeField field = type.getField(name, caseSensitive, false); fields.add(type.getFieldList().get(field.getIndex())); } return typeFactory.createStructType(fields); }
/** * Derives the type of a join relational expression. * * @param leftType Row type of left input to join * @param rightType Row type of right input to join * @param joinType Type of join * @param typeFactory Type factory * @param fieldNameList List of names of fields; if null, field names are inherited and made * unique * @param systemFieldList List of system fields that will be prefixed to output row type; * typically empty but must not be null * @return join type */ public static RelDataType deriveJoinRowType( RelDataType leftType, RelDataType rightType, JoinRelType joinType, RelDataTypeFactory typeFactory, List<String> fieldNameList, List<RelDataTypeField> systemFieldList) { assert systemFieldList != null; switch (joinType) { case LEFT: rightType = typeFactory.createTypeWithNullability(rightType, true); break; case RIGHT: leftType = typeFactory.createTypeWithNullability(leftType, true); break; case FULL: leftType = typeFactory.createTypeWithNullability(leftType, true); rightType = typeFactory.createTypeWithNullability(rightType, true); break; default: break; } return createJoinType(typeFactory, leftType, rightType, fieldNameList, systemFieldList); }
/** * 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 createSqlType(RelDataTypeFactory typeFactory) { BitString bitString; switch (typeName) { case NULL: case BOOLEAN: RelDataType ret = typeFactory.createSqlType(typeName); ret = typeFactory.createTypeWithNullability(ret, null == value); return ret; case BINARY: bitString = (BitString) value; int bitCount = bitString.getBitCount(); return typeFactory.createSqlType(SqlTypeName.BINARY, bitCount / 8); case CHAR: NlsString string = (NlsString) value; Charset charset = string.getCharset(); if (null == charset) { charset = typeFactory.getDefaultCharset(); } SqlCollation collation = string.getCollation(); if (null == collation) { collation = SqlCollation.COERCIBLE; } RelDataType type = typeFactory.createSqlType(SqlTypeName.CHAR, string.getValue().length()); type = typeFactory.createTypeWithCharsetAndCollation(type, charset, collation); return type; case INTERVAL_YEAR_MONTH: case INTERVAL_DAY_TIME: SqlIntervalLiteral.IntervalValue intervalValue = (SqlIntervalLiteral.IntervalValue) value; return typeFactory.createSqlIntervalType(intervalValue.getIntervalQualifier()); case SYMBOL: return typeFactory.createSqlType(SqlTypeName.SYMBOL); case INTEGER: // handled in derived class case TIME: // handled in derived class case VARCHAR: // should never happen case VARBINARY: // should never happen default: throw Util.needToImplement(toString() + ", operand=" + value); } }
private RelDataType guessType(Object value) { if (value == null) { return typeFactory.createSqlType(SqlTypeName.NULL); } if (value instanceof Float || value instanceof Double) { return typeFactory.createSqlType(SqlTypeName.DOUBLE); } if (value instanceof Number) { return typeFactory.createSqlType(SqlTypeName.BIGINT); } if (value instanceof Boolean) { return typeFactory.createSqlType(SqlTypeName.BOOLEAN); } if (value instanceof String) { return typeFactory.createSqlType(SqlTypeName.CHAR, ((String) value).length()); } if (value instanceof ByteString) { return typeFactory.createSqlType(SqlTypeName.BINARY, ((ByteString) value).length()); } throw new AssertionError("unknown type " + value.getClass()); }
/** * Creates a reference to all the fields in the row. * * <p>For example, if the input row has type <code>T{f0,f1,f2,f3,f4}</code> then <code> * makeRangeReference(T{f0,f1,f2,f3,f4}, S{f3,f4}, 3)</code> is an expression which yields the * last 2 fields. * * @param type Type of the resulting range record. * @param offset Index of first field. * @param nullable Whether the record is nullable. */ public RexRangeRef makeRangeReference(RelDataType type, int offset, boolean nullable) { if (nullable && !type.isNullable()) { type = typeFactory.createTypeWithNullability(type, nullable); } return new RexRangeRef(type, offset); }
/** Creates a Time literal. */ public RexLiteral makeTimeLiteral(Calendar time, int precision) { assert time != null; return makeLiteral( time, typeFactory.createSqlType(SqlTypeName.TIME, precision), SqlTypeName.TIME); }
/** Creates a Date literal. */ public RexLiteral makeDateLiteral(Calendar date) { assert date != null; return makeLiteral(date, typeFactory.createSqlType(SqlTypeName.DATE), SqlTypeName.DATE); }
/** Creates a byte array literal. */ public RexLiteral makeBinaryLiteral(ByteString byteString) { return makeLiteral( byteString, typeFactory.createSqlType(SqlTypeName.BINARY, byteString.length()), SqlTypeName.BINARY); }
/** Creates a BIGINT literal. */ public RexLiteral makeBigintLiteral(BigDecimal bd) { RelDataType bigintType = typeFactory.createSqlType(SqlTypeName.BIGINT); return makeLiteral(bd, bigintType, SqlTypeName.DECIMAL); }
/** * Casts a decimal's integer representation to a decimal node. If the expression is not the * expected integer type, then it is casted first. * * <p>An overflow check may be requested to ensure the internal value does not exceed the maximum * value of the decimal type. * * @param value integer representation of decimal * @param type type integer will be reinterpreted as * @param checkOverflow indicates whether an overflow check is required when reinterpreting this * particular value as the decimal type. A check usually not required for arithmetic, but is * often required for rounding and explicit casts. * @return the integer reinterpreted as an opaque decimal type */ public RexNode encodeIntervalOrDecimal(RexNode value, RelDataType type, boolean checkOverflow) { RelDataType bigintType = typeFactory.createSqlType(SqlTypeName.BIGINT); RexNode cast = ensureType(bigintType, value, true); return makeReinterpretCast(type, cast, makeLiteral(checkOverflow)); }
/** * Retrieves an interval or decimal node's integer representation * * @param node the interval or decimal value as an opaque type * @return an integer representation of the decimal value */ public RexNode decodeIntervalOrDecimal(RexNode node) { assert SqlTypeUtil.isDecimal(node.getType()) || SqlTypeUtil.isInterval(node.getType()); RelDataType bigintType = typeFactory.createSqlType(SqlTypeName.BIGINT); return makeReinterpretCast(matchNullability(bigintType, node), node, makeLiteral(false)); }
/** * Creates a literal whose value is NULL, with a particular type. * * <p>The typing is necessary because RexNodes are strictly typed. For example, in the Rex world * the <code>NULL</code> parameter to <code> * SUBSTRING(NULL FROM 2 FOR 4)</code> must have a valid VARCHAR type so that the result type can * be determined. * * @param typeName Type to cast NULL to * @return NULL literal of given type */ public RexNode makeNullLiteral(SqlTypeName typeName) { RelDataType type = typeFactory.createTypeWithNullability(typeFactory.createSqlType(typeName), true); return makeCast(type, constantNull()); }
/** * Creates a literal representing a flag. * * @param flag Flag value */ public RexLiteral makeFlag(Enum flag) { assert flag != null; return makeLiteral(flag, typeFactory.createSqlType(SqlTypeName.SYMBOL), SqlTypeName.SYMBOL); }
/** * 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()); } }
@Override public Prel visitProject(ProjectPrel project, Object unused) throws RelConversionException { // Apply the rule to the child RelNode originalInput = ((Prel) project.getInput(0)).accept(this, null); project = (ProjectPrel) project.copy(project.getTraitSet(), Lists.newArrayList(originalInput)); List<RexNode> exprList = new ArrayList<>(); List<RelDataTypeField> relDataTypes = new ArrayList(); List<RelDataTypeField> origRelDataTypes = new ArrayList(); int i = 0; final int lastColumnReferenced = PrelUtil.getLastUsedColumnReference(project.getProjects()); if (lastColumnReferenced == -1) { return project; } final int lastRexInput = lastColumnReferenced + 1; RexVisitorComplexExprSplitter exprSplitter = new RexVisitorComplexExprSplitter(factory, funcReg, lastRexInput); for (RexNode rex : project.getChildExps()) { origRelDataTypes.add(project.getRowType().getFieldList().get(i)); i++; exprList.add(rex.accept(exprSplitter)); } List<RexNode> complexExprs = exprSplitter.getComplexExprs(); if (complexExprs.size() == 1 && findTopComplexFunc(project.getChildExps()).size() == 1) { return project; } ProjectPrel childProject; List<RexNode> allExprs = new ArrayList(); int exprIndex = 0; List<String> fieldNames = originalInput.getRowType().getFieldNames(); for (int index = 0; index < lastRexInput; index++) { RexBuilder builder = new RexBuilder(factory); allExprs.add( builder.makeInputRef(new RelDataTypeDrillImpl(new RelDataTypeHolder(), factory), index)); if (fieldNames.get(index).contains(StarColumnHelper.STAR_COLUMN)) { relDataTypes.add( new RelDataTypeFieldImpl( fieldNames.get(index), allExprs.size(), factory.createSqlType(SqlTypeName.ANY))); } else { relDataTypes.add( new RelDataTypeFieldImpl( "EXPR$" + exprIndex, allExprs.size(), factory.createSqlType(SqlTypeName.ANY))); exprIndex++; } } RexNode currRexNode; int index = lastRexInput - 1; // if the projection expressions contained complex outputs, split them into their own individual // projects if (complexExprs.size() > 0) { while (complexExprs.size() > 0) { if (index >= lastRexInput) { allExprs.remove(allExprs.size() - 1); RexBuilder builder = new RexBuilder(factory); allExprs.add( builder.makeInputRef( new RelDataTypeDrillImpl(new RelDataTypeHolder(), factory), index)); } index++; exprIndex++; currRexNode = complexExprs.remove(0); allExprs.add(currRexNode); relDataTypes.add( new RelDataTypeFieldImpl( "EXPR$" + exprIndex, allExprs.size(), factory.createSqlType(SqlTypeName.ANY))); childProject = new ProjectPrel( project.getCluster(), project.getTraitSet(), originalInput, ImmutableList.copyOf(allExprs), new RelRecordType(relDataTypes)); originalInput = childProject; } // copied from above, find a better way to do this allExprs.remove(allExprs.size() - 1); RexBuilder builder = new RexBuilder(factory); allExprs.add( builder.makeInputRef(new RelDataTypeDrillImpl(new RelDataTypeHolder(), factory), index)); relDataTypes.add( new RelDataTypeFieldImpl( "EXPR$" + index, allExprs.size(), factory.createSqlType(SqlTypeName.ANY))); } return (Prel) project.copy( project.getTraitSet(), originalInput, exprList, new RelRecordType(origRelDataTypes)); }