/** * Casts the rhs to an {@link AssignableValue} using that interface's standard assignment * method. i.e. * * <pre> * [AssignableValueType] lhs; * lhs.[assignMethod](rhs); * </pre> * * or perhaps a type-specific cast: * * <pre> * [AssignableValueType] lhs; * lhs.[castMethod](rhs, lhs.getPrecision()); * </pre> * * <p>Code is also generated to pad and truncate values which need special handling, such as * date and time types. Plus good old null handling. */ private Expression castToAssignableValue() { ensureLhs(); if (requiresSpecializedCast() && rhsType.isNullable()) { assert (lhsType.isNullable()); // propagate null value; normally, we can rely on // assignFrom to do it for us, but for specialized casts, // we can't Expression nullTest = new MethodCall(rhsExp, NullableValue.NULL_IND_ACCESSOR_NAME, new ExpressionList()); addStatement( new ExpressionStatement( new MethodCall( lhsExp, NullableValue.NULL_IND_MUTATOR_NAME, new ExpressionList(nullTest)))); StatementList ifStmtList = new StatementList(); addStatement(new IfStatement(not(nullTest), ifStmtList)); borrowStmtList(ifStmtList); try { return castToAssignableValueImpl(); } finally { returnStmtList(ifStmtList); } } else { return castToAssignableValueImpl(); } }
public int getFieldJdbcType(int fieldOrdinal) { RelDataType type = getFieldNamedType(fieldOrdinal); SqlTypeName typeName = type.getSqlTypeName(); if (typeName == null) { return Types.OTHER; } return typeName.getJdbcOrdinal(); }
public RelDataType getFieldType(int fieldOrdinal) { RelDataType namedType = getFieldNamedType(fieldOrdinal); if (namedType.getSqlTypeName() == SqlTypeName.DISTINCT) { // for most metadata calls, report information about the // predefined type on which the distinct type is based return namedType.getFields()[0].getType(); } else { return namedType; } }
/** * Gets the right hand expression as a valid value to be assigned to the left hand side. Usually * returns the original rhs. However, if the lhs is of a primitive type, and the rhs is an * explicit null, returns a primitive value instead. */ private Expression rhsAsValue() { if (SqlTypeUtil.isJavaPrimitive(lhsType) && (rhsType.getSqlTypeName() == SqlTypeName.NULL)) { if (lhsType.getSqlTypeName() == SqlTypeName.BOOLEAN) { return Literal.constantFalse(); } else { return Literal.constantZero(); } } return rhsExp; }
/** Generates code to throw an exception when a NULL value is casted to a NOT NULL type */ private void checkNotNull() { if (!lhsType.isNullable() && rhsType.isNullable()) { rhsExp = rhsAsJava(); addStatement( new ExpressionStatement( new MethodCall( translator.getRelImplementor().getConnectionVariable(), "checkNotNull", new ExpressionList(Literal.makeLiteral(targetName), rhsExp)))); } }
/** * Generates a cast from one row type to another * * @param rexBuilder RexBuilder to use for constructing casts * @param lhsRowType target row type * @param rhsRowType source row type; fields must be 1-to-1 with lhsRowType, in same order * @return cast expressions */ public static RexNode[] generateCastExpressions( RexBuilder rexBuilder, RelDataType lhsRowType, RelDataType rhsRowType) { int n = rhsRowType.getFieldCount(); assert n == lhsRowType.getFieldCount() : "field count: lhs [" + lhsRowType + "] rhs [" + rhsRowType + "]"; RexNode[] rhsExps = new RexNode[n]; for (int i = 0; i < n; ++i) { rhsExps[i] = rexBuilder.makeInputRef(rhsRowType.getFields()[i].getType(), i); } return generateCastExpressions(rexBuilder, lhsRowType, rhsExps); }
public int getFieldScale(int fieldOrdinal) { RelDataType type = getFieldType(fieldOrdinal); SqlTypeName typeName = type.getSqlTypeName(); if (typeName == null) { return 0; } if (typeName.allowsPrecScale(true, true)) { return type.getScale(); } else { return 0; } }
/** * Derives the list of column names suitable for NATURAL JOIN. These are the columns that occur * exactly once on each side of the join. * * @param leftRowType Row type of left input to the join * @param rightRowType Row type of right input to the join * @return List of columns that occur once on each side */ public static List<String> deriveNaturalJoinColumnList( RelDataType leftRowType, RelDataType rightRowType) { List<String> naturalColumnNames = new ArrayList<String>(); final List<String> leftNames = leftRowType.getFieldNames(); final List<String> rightNames = rightRowType.getFieldNames(); for (String name : leftNames) { if ((Collections.frequency(leftNames, name) == 1) && (Collections.frequency(rightNames, name) == 1)) { naturalColumnNames.add(name); } } return naturalColumnNames; }
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<RelDataTypeField>(columnNameList.size()); for (String name : columnNameList) { RelDataTypeField field = type.getField(name, caseSensitive); fields.add(type.getFieldList().get(field.getIndex())); } return typeFactory.createStructType(fields); }
public RelDataType getNamedType(SqlIdentifier typeName) { if (typeName.equalsDeep(addressType.getSqlIdentifier(), false)) { return addressType; } else { return null; } }
/** * Implements a cast from any Java primitive to a nullable Java primitive as a simple * assignment. i.e. * * <pre> * [NullablePrimitiveType] lhs; * lhs.[nullIndicator] = ...; * if (! lhs.[nullIndicator]) { * // check overflow ... * // round ... * lhs.[value] = ...; * } * </pre> */ private Expression castPrimitiveToNullablePrimitive() { ensureLhs(); boolean nullableSource = rhsType.isNullable(); Expression rhsIsNull; if (nullableSource) { rhsIsNull = getNullIndicator(rhsExp); rhsExp = getValue(rhsType, rhsExp); } else { rhsIsNull = Literal.constantFalse(); } addStatement(assign(getNullIndicator(lhsExp), rhsIsNull)); StatementList setValueBlock = new StatementList(); StatementList oldList = borrowStmtList(setValueBlock); try { checkOverflow(); roundAsNeeded(); addStatement(assign(getValue(lhsType, lhsExp), new CastExpression(getLhsClass(), rhsExp))); } finally { returnStmtList(oldList); } if (nullableSource) { addStatement(new IfStatement(not(getNullIndicator(lhsExp)), setValueBlock)); } else { addStatementList(setValueBlock); } return lhsExp; }
public Void visitInputRef(RexInputRef inputRef) { super.visitInputRef(inputRef); if (inputRef.getIndex() >= inputRowType.getFieldCount()) { throw new IllegalForwardRefException(); } return null; }
public String getFieldTypeName(int fieldOrdinal) { RelDataType type = getFieldNamedType(fieldOrdinal); SqlTypeName typeName = type.getSqlTypeName(); if (typeName == null) { return type.toString(); } switch (typeName) { case STRUCTURED: case DISTINCT: return type.getSqlIdentifier().toString(); case INTERVAL_DAY_TIME: case INTERVAL_YEAR_MONTH: return type.toString(); } return typeName.name(); }
/** Creates an array of {@link RexLocalRef} objects, one for each field of a given rowtype. */ public static RexLocalRef[] toLocalRefs(RelDataType rowType) { final RelDataTypeField[] fields = rowType.getFields(); final RexLocalRef[] refs = new RexLocalRef[fields.length]; for (int i = 0; i < refs.length; i++) { refs[i] = new RexLocalRef(i, fields[i].getType()); } return refs; }
/** * Checks for overflow when assigning one primitive type to another. Non-primitive types check * for overflow during assignment. */ private void checkOverflow() { String maxLiteral = null; String minLiteral = null; if (lhsType == null) { return; } // Assume that equivalent types can be assigned without overflow if (lhsType.getSqlTypeName() == rhsType.getSqlTypeName()) { return; } // Approximate numerics have a wider range than exact numerics if (SqlTypeUtil.isApproximateNumeric(lhsType) && SqlTypeUtil.isExactNumeric(rhsType)) { return; } // We can skip an error check if the left type is "larger" if (SqlTypeUtil.isIntType(lhsType) && SqlTypeUtil.isIntType(rhsType) && (SqlTypeUtil.maxValue(lhsType) >= SqlTypeUtil.maxValue(rhsType))) { return; } if (SqlTypeUtil.isExactNumeric(lhsType)) { String numClassName = SqlTypeUtil.getNumericJavaClassName(lhsType); minLiteral = numClassName + ".MIN_VALUE"; maxLiteral = numClassName + ".MAX_VALUE"; } else if (SqlTypeUtil.isApproximateNumeric(lhsType)) { String numClassName = SqlTypeUtil.getNumericJavaClassName(lhsType); maxLiteral = numClassName + ".MAX_VALUE"; minLiteral = "-" + maxLiteral; } if (maxLiteral == null) { return; } Statement ifstmt = new IfStatement( new BinaryExpression( new BinaryExpression( rhsExp, BinaryExpression.LESS, new Literal(Literal.STRING, minLiteral)), BinaryExpression.LOGICAL_OR, new BinaryExpression( rhsExp, BinaryExpression.GREATER, new Literal(Literal.STRING, maxLiteral))), getThrowStmtList()); addStatement(ifstmt); }
/** * Creates a FarragoJdbcMetaDataImpl. * * @param rowType Type info to return * @param fieldOrigins Origin of each field in column of catalog object */ protected FarragoJdbcMetaDataImpl(RelDataType rowType, List<List<String>> fieldOrigins) { this.rowType = rowType; this.fieldOrigins = fieldOrigins; assert rowType != null; assert fieldOrigins != null; assert fieldOrigins.size() == rowType.getFieldCount() : "field origins " + fieldOrigins + " have different count than row type " + rowType; }
/** * Creates an array of {@link RexInputRef} objects, one for each field of a given rowtype, * according to a permutation. * * @param args Permutation * @param rowType Input row type * @return Array of input refs */ public static RexInputRef[] toInputRefs(int[] args, RelDataType rowType) { final RelDataTypeField[] fields = rowType.getFields(); final RexInputRef[] rexNodes = new RexInputRef[args.length]; for (int i = 0; i < args.length; i++) { int fieldOrdinal = args[i]; rexNodes[i] = new RexInputRef(fieldOrdinal, fields[fieldOrdinal].getType()); } return rexNodes; }
/** * Generates a cast for a row type. * * @param rexBuilder RexBuilder to use for constructing casts * @param lhsRowType target row type * @param rhsExps expressions to be cast * @return cast expressions */ public static RexNode[] generateCastExpressions( RexBuilder rexBuilder, RelDataType lhsRowType, RexNode[] rhsExps) { RelDataTypeField[] lhsFields = lhsRowType.getFields(); final int fieldCount = lhsFields.length; RexNode[] castExps = new RexNode[fieldCount]; assert fieldCount == rhsExps.length; for (int i = 0; i < fieldCount; ++i) { RelDataTypeField lhsField = lhsFields[i]; RelDataType lhsType = lhsField.getType(); RelDataType rhsType = rhsExps[i].getType(); if (lhsType.equals(rhsType)) { castExps[i] = rhsExps[i]; } else { castExps[i] = rexBuilder.makeCast(lhsType, rhsExps[i]); } } return castExps; }
private SqlOperator toOp(SqlIdentifier name, Function function) { List<RelDataType> argTypes = new ArrayList<RelDataType>(); List<SqlTypeFamily> typeFamilies = new ArrayList<SqlTypeFamily>(); for (FunctionParameter o : function.getParameters()) { final RelDataType type = o.getType(typeFactory); argTypes.add(type); typeFamilies.add(Util.first(type.getSqlTypeName().getFamily(), SqlTypeFamily.ANY)); } final RelDataType returnType; if (function instanceof ScalarFunction) { return new SqlUserDefinedFunction( name, ReturnTypes.explicit(Schemas.proto((ScalarFunction) function)), InferTypes.explicit(argTypes), OperandTypes.family(typeFamilies), toSql(argTypes), function); } else if (function instanceof AggregateFunction) { returnType = ((AggregateFunction) function).getReturnType(typeFactory); return new SqlUserDefinedAggFunction( name, ReturnTypes.explicit(returnType), InferTypes.explicit(argTypes), OperandTypes.family(typeFamilies), (AggregateFunction) function); } else if (function instanceof TableMacro) { return new SqlUserDefinedTableMacro( name, ReturnTypes.CURSOR, InferTypes.explicit(argTypes), OperandTypes.family(typeFamilies), (TableMacro) function); } else if (function instanceof TableFunction) { return new SqlUserDefinedTableFunction( name, ReturnTypes.CURSOR, InferTypes.explicit(argTypes), OperandTypes.family(typeFamilies), toSql(argTypes), (TableFunction) function); } else { throw new AssertionError("unknown function type " + function); } }
/** * Determines whether a {@link RexCall} requires decimal expansion. It usually requires expansion * if it has decimal operands. * * <p>Exceptions to this rule are: * * <ul> * <li>isNull doesn't require expansion * <li>It's okay to cast decimals to and from char types * <li>It's okay to cast nulls as decimals * <li>Casts require expansion if their return type is decimal * <li>Reinterpret casts can handle a decimal operand * </ul> * * @param expr expression possibly in need of expansion * @param recurse whether to check nested calls * @return whether the expression requires expansion */ public static boolean requiresDecimalExpansion(RexNode expr, boolean recurse) { if (!(expr instanceof RexCall)) { return false; } RexCall call = (RexCall) expr; boolean localCheck = true; switch (call.getKind()) { case Reinterpret: case IsNull: localCheck = false; break; case Cast: RelDataType lhsType = call.getType(); RelDataType rhsType = call.operands[0].getType(); if (rhsType.getSqlTypeName() == SqlTypeName.NULL) { return false; } if (SqlTypeUtil.inCharFamily(lhsType) || SqlTypeUtil.inCharFamily(rhsType)) { localCheck = false; } else if (SqlTypeUtil.isDecimal(lhsType) && (lhsType != rhsType)) { return true; } break; default: localCheck = call.getOperator().requiresDecimalExpansion(); } if (localCheck) { if (SqlTypeUtil.isDecimal(call.getType())) { // NOTE jvs 27-Mar-2007: Depending on the type factory, the // result of a division may be decimal, even though both inputs // are integer. return true; } for (int i = 0; i < call.operands.length; i++) { if (SqlTypeUtil.isDecimal(call.operands[i].getType())) { return true; } } } return (recurse && requiresDecimalExpansion(call.operands, recurse)); }
private boolean requiresSpecializedCast() { if ((rhsType != null) && (SqlTypeUtil.isNumeric(rhsType) || (rhsType.getSqlTypeName() == SqlTypeName.BOOLEAN)) && SqlTypeUtil.inCharOrBinaryFamilies(lhsType) && !SqlTypeUtil.isLob(lhsType)) { // Boolean or Numeric to String. // sometimes the Integer got slipped by. return true; } else { return false; } }
private List<RelCollation> deduceMonotonicity(SqlValidatorTable table) { final RelDataType rowType = table.getRowType(); final List<RelCollation> collationList = new ArrayList<RelCollation>(); // Deduce which fields the table is sorted on. int i = -1; for (RelDataTypeField field : rowType.getFieldList()) { ++i; final SqlMonotonicity monotonicity = table.getMonotonicity(field.getName()); if (monotonicity != SqlMonotonicity.NOT_MONOTONIC) { final RelFieldCollation.Direction direction = monotonicity.isDecreasing() ? RelFieldCollation.Direction.DESCENDING : RelFieldCollation.Direction.ASCENDING; collationList.add( RelCollationImpl.of( new RelFieldCollation( i, direction, RelFieldCollation.NullDirection.UNSPECIFIED))); } } return collationList; }
public static void checkCharsetAndCollateConsistentIfCharType(RelDataType type) { // (every charset must have a default collation) if (SqlTypeUtil.inCharFamily(type)) { Charset strCharset = type.getCharset(); Charset colCharset = type.getCollation().getCharset(); assert null != strCharset; assert null != colCharset; if (!strCharset.equals(colCharset)) { if (false) { // todo: enable this checking when we have a charset to // collation mapping throw new Error( type.toString() + " was found to have charset '" + strCharset.name() + "' and a mismatched collation charset '" + colCharset.name() + "'"); } } } }
/** * Returns whether the type of an array of expressions is compatible with a struct type. * * @param exprs Array of expressions * @param type Type * @param fail Whether to fail if there is a mismatch * @return Whether every expression has the same type as the corresponding member of the struct * type * @see RelOptUtil#eq(String, RelDataType, String, RelDataType, boolean) */ public static boolean compatibleTypes(RexNode[] exprs, RelDataType type, boolean fail) { final RelDataTypeField[] fields = type.getFields(); if (exprs.length != fields.length) { assert !fail : "rowtype mismatches expressions"; return false; } for (int i = 0; i < fields.length; i++) { final RelDataType exprType = exprs[i].getType(); final RelDataType fieldType = fields[i].getType(); if (!RelOptUtil.eq("type1", exprType, "type2", fieldType, fail)) { return false; } } return true; }
// implement FarragoOJRexImplementor public Expression implementFarrago( FarragoRexToOJTranslator translator, RexCall call, Expression[] operands) { RelDataType lhsType = call.getType(); RelDataType rhsType = call.operands[0].getType(); Expression rhsExp = operands[0]; SqlTypeName lhsTypeName = lhsType.getSqlTypeName(); if ((lhsTypeName == SqlTypeName.CURSOR) || (lhsTypeName == SqlTypeName.COLUMN_LIST)) { // Conversion should already have been taken care of outside. return rhsExp; } // NOTE jvs 19-Nov-2008: In some cases (e.g. FRG-273) a cast // may be illegal at the SQL level, but allowable as part of // implementation, so don't try to enforce // SqlTypeUtil.canCastFrom here. Anything which was supposed // to have been prevented should already have been caught // by the validator. CastHelper helper = new CastHelper(translator, null, call.toString(), lhsType, rhsType, null, rhsExp); return helper.implement(); }
/** * Looks up a field with a given name, returning null if not found. * * @param rowType Row type * @param columnName Field name * @return Field, or null if not found */ public static RelDataTypeField lookupField( boolean caseSensitive, final RelDataType rowType, String columnName) { RelDataTypeField field = rowType.getField(columnName, caseSensitive); if (field != null) { return field; } // If record type is flagged as having "any field you ask for", // return a type. (TODO: Better way to mark accommodating types.) RelDataTypeField extra = RelDataTypeImpl.extra(rowType); if (extra != null) { return new RelDataTypeFieldImpl(columnName, -1, extra.getType()); } return null; }
/** * Replaces the operands of a call. The new operands' types must match the old operands' types. */ public static RexCall replaceOperands(RexCall call, RexNode[] operands) { if (call.operands == operands) { return call; } for (int i = 0; i < operands.length; i++) { RelDataType oldType = call.operands[i].getType(); RelDataType newType = operands[i].getType(); if (!oldType.isNullable() && newType.isNullable()) { throw Util.newInternal("invalid nullability"); } assert (oldType.toString().equals(newType.toString())); } return new RexCall(call.getType(), call.getOperator(), operands); }
/** * Returns whether the leading edge of a given array of expressions is wholly {@link RexInputRef} * objects with types corresponding to the underlying datatype. */ public static boolean containIdentity(RexNode[] exprs, RelDataType rowType, boolean fail) { final RelDataTypeField[] fields = rowType.getFields(); if (exprs.length < fields.length) { assert !fail : "exprs/rowType length mismatch"; return false; } for (int i = 0; i < fields.length; i++) { if (!(exprs[i] instanceof RexInputRef)) { assert !fail : "expr[" + i + "] is not a RexInputRef"; return false; } RexInputRef inputRef = (RexInputRef) exprs[i]; if (inputRef.getIndex() != i) { assert !fail : "expr[" + i + "] has ordinal " + inputRef.getIndex(); return false; } if (!RelOptUtil.eq("type1", exprs[i].getType(), "type2", fields[i].getType(), fail)) { return false; } } return true; }
/** * Implement the cast expression. * * <p>TODO: check for overflow * * @return the rhs expression casted as the lhs type */ public Expression implement() { // Check for invalid null assignment. Code generated afterwards // can assume null will never be assigned to a not null value. checkNotNull(); // Check for an explicit rhs null value. Code generated // afterwards need never check for an explicit null. if (rhsType.getSqlTypeName() == SqlTypeName.NULL) { if (lhsType.isNullable()) { return castFromNull(); } else { // NOTE jvs 27-Jan-2005: this code will never actually // be executed do to previous checkNotNull test, but // it still has to compile! return rhsAsValue(); } } // Case when left hand side is a nullable primitive if (translator.isNullablePrimitive(lhsType)) { if (SqlTypeUtil.isJavaPrimitive(rhsType) && (!rhsType.isNullable() || translator.isNullablePrimitive(rhsType))) { return castPrimitiveToNullablePrimitive(); } return castToAssignableValue(); } // Case when left hand side is a not nullable primitive if (SqlTypeUtil.isJavaPrimitive(lhsType)) { return castToNotNullPrimitive(); } // Case when left hand side is a structure if (lhsType.isStruct()) { assert (rhsType.isStruct()); // TODO jvs 27-May-2004: relax this assert and deal with // conversions, null checks, etc. assert (lhsType.equals(rhsType)); return getDirectAssignment(); } // Default is to treat non-primitives as AssignableValue return castToAssignableValue(); }
/** * Returns the "extra" field in a row type whose presence signals that fields will come into * existence just by asking for them. * * @param rowType Row type * @return The "extra" field, or null */ public static RelDataTypeField extra(RelDataType rowType) { // Even in a case-insensitive connection, the name must be precisely // "_extra". return rowType.getField("_extra", true); }