/** * Returns true if a type is a simple cast of another type. It is if the cast type is nullable and * the cast is one of the following: * <li>x TO x * <li>char(n) TO varchar(m) * <li>varchar(n) TO varchar(m) * <li>x not null TO x nullable * * @param origType original type passed into the cast operand * @param castType type the operand will be casted to * @return true if the cast is simple */ private boolean isCastSimple(RelDataType origType, RelDataType castType) { SqlTypeName origTypeName = origType.getSqlTypeName(); SqlTypeName castTypeName = castType.getSqlTypeName(); if (!(castType.isNullable())) { return false; } Charset origCharset = origType.getCharset(); Charset castCharset = castType.getCharset(); if ((origCharset != null) || (castCharset != null)) { if ((origCharset == null) || (castCharset == null)) { return false; } if (!origCharset.equals(castCharset)) { return false; } } return ((origType == castType) || ((origTypeName == SqlTypeName.CHAR) && (castTypeName == SqlTypeName.VARCHAR)) || ((origTypeName == SqlTypeName.VARCHAR) && (castTypeName == SqlTypeName.VARCHAR)) || ((origTypeName == castTypeName) && (origType.getPrecision() == castType.getPrecision()) && ((origTypeName != SqlTypeName.DECIMAL) || (origType.getScale() == castType.getScale())) && (!origType.isNullable() && castType.isNullable()))); }
/** * 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(); } }
/** 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)))); } }
/** * 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); }
/** * 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; }
/** * 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(); }
public RelDataType inferReturnType(SqlOperatorBinding opBinding) { RelDataType type1 = opBinding.getOperandType(0); if (SqlTypeUtil.isDecimal(type1)) { if (type1.getScale() == 0) { return type1; } else { int p = type1.getPrecision(); RelDataType ret; ret = opBinding.getTypeFactory().createSqlType(SqlTypeName.DECIMAL, p, 0); if (type1.isNullable()) { ret = opBinding.getTypeFactory().createTypeWithNullability(ret, true); } return ret; } } return null; }
private void reduceCasts(RexCall outerCast) { RexNode[] operands = outerCast.getOperands(); if (operands.length != 1) { return; } RelDataType outerCastType = outerCast.getType(); RelDataType operandType = operands[0].getType(); if (operandType.equals(outerCastType)) { removableCasts.add(outerCast); return; } // See if the reduction // CAST((CAST x AS type) AS type NOT NULL) // -> CAST(x AS type NOT NULL) // applies. TODO jvs 15-Dec-2008: consider // similar cases for precision changes. if (!(operands[0] instanceof RexCall)) { return; } RexCall innerCast = (RexCall) operands[0]; if (innerCast.getOperator() != SqlStdOperatorTable.castFunc) { return; } if (innerCast.getOperands().length != 1) { return; } RelDataTypeFactory typeFactory = preparingStmt.getFarragoTypeFactory(); RelDataType outerTypeNullable = typeFactory.createTypeWithNullability(outerCastType, true); RelDataType innerTypeNullable = typeFactory.createTypeWithNullability(operandType, true); if (outerTypeNullable != innerTypeNullable) { return; } if (operandType.isNullable()) { removableCasts.add(innerCast); } }
public int isFieldNullable(int fieldOrdinal) { RelDataType type = getFieldType(fieldOrdinal); return type.isNullable() ? ResultSetMetaData.columnNullable : ResultSetMetaData.columnNoNulls; }
// implement FarragoMedDataServer public FarragoMedColumnSet newColumnSet( String[] localName, Properties tableProps, FarragoTypeFactory typeFactory, RelDataType rowType, Map<String, Properties> columnPropMap) throws SQLException { if (rowType == null) { rowType = createMockRowType(typeFactory); } assert (rowType.getFieldList().size() == 1); RelDataType type = rowType.getFields()[0].getType(); assert (!type.isNullable()); assert (typeFactory.getClassForPrimitive(type) != null); // TODO jvs 5-Aug-2005: clean up usage of server properties // as defaults long nRows = -1; String rowCountSql = tableProps.getProperty(PROP_ROW_COUNT_SQL); if (rowCountSql != null) { // Attempt to issue a loopback query into Farrago to // get the number of rows to produce. DataSource loopbackDataSource = getLoopbackDataSource(); Connection connection = null; if (loopbackDataSource != null) { try { connection = loopbackDataSource.getConnection(); Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(rowCountSql); if (resultSet.next()) { nRows = resultSet.getLong(1); } } finally { // It's OK not to clean up stmt and resultSet; // connection.close() will do that for us. if (connection != null) { connection.close(); } } } } if (nRows == -1) { nRows = getLongProperty( tableProps, PROP_ROW_COUNT, getLongProperty(getProperties(), PROP_ROW_COUNT, 10)); } String executorImpl = tableProps.getProperty( PROP_EXECUTOR_IMPL, getProperties().getProperty(PROP_EXECUTOR_IMPL, PROPVAL_JAVA)); assert (executorImpl.equals(PROPVAL_JAVA) || executorImpl.equals(PROPVAL_FENNEL)); String udxSpecificName = tableProps.getProperty(PROP_UDX_SPECIFIC_NAME); if (udxSpecificName != null) { assert (executorImpl.equals(PROPVAL_JAVA)); } checkNameMatch(getForeignSchemaName(), tableProps.getProperty(PROP_SCHEMA_NAME)); checkNameMatch(getForeignTableName(), tableProps.getProperty(PROP_TABLE_NAME)); return new MedMockColumnSet(this, localName, rowType, nRows, executorImpl, udxSpecificName); }
private Expression castToAssignableValueImpl() { if (requiresSpecializedCast()) { if (rhsType.isNullable() && (!SqlTypeUtil.isDecimal(rhsType))) { rhsExp = getValue(rhsType, rhsExp); } addStatement( new ExpressionStatement( new MethodCall( lhsExp, "cast", new ExpressionList(rhsExp, Literal.makeLiteral(lhsType.getPrecision()))))); } else { // Set current_date for casting time to timestamp. If // rhsType is null then we may have to be ready for anything. // But it will be null even for current_timestamp, so the // condition below seems a bit excessive. if ((lhsType.getSqlTypeName() == SqlTypeName.TIMESTAMP) && ((rhsType == null) || (rhsType.getSqlTypeName() == SqlTypeName.TIME))) { addStatement( new ExpressionStatement( new MethodCall(lhsExp, "setCurrentDate", new ExpressionList(getCurrentDate())))); } addStatement( new ExpressionStatement( new MethodCall( lhsExp, AssignableValue.ASSIGNMENT_METHOD_NAME, new ExpressionList(rhsExp)))); } // Trim precision of datetime values. // if (((lhsType.getSqlTypeName() == SqlTypeName.TIMESTAMP) || (lhsType.getSqlTypeName() == SqlTypeName.TIME))) { if ((rhsType != null) // FIXME: JavaType(java.sql.Time) and // JavaType(java.sql.Timestamp) say they support precision // but do not. && !rhsType.toString().startsWith("JavaType(") && rhsType.getSqlTypeName().allowsPrec() && (lhsType.getPrecision() < rhsType.getPrecision())) { int lhsPrecision = lhsType.getPrecision(); if (lhsPrecision == -1) { lhsPrecision = 0; } addStatement( new ExpressionStatement( new MethodCall( lhsExp, SqlDateTimeWithoutTZ.ADJUST_PRECISION_METHOD_NAME, new ExpressionList(Literal.makeLiteral(lhsPrecision))))); } } boolean mayNeedPadOrTruncate = false; if (SqlTypeUtil.inCharOrBinaryFamilies(lhsType) && !SqlTypeUtil.isLob(lhsType)) { mayNeedPadOrTruncate = true; } if (mayNeedPadOrTruncate) { // check overflow if it is datetime. // TODO: should check it at the run time. // so, it should be in the // cast(SqlDateTimeWithTZ, int precision); if ((rhsType != null) && (rhsType.getSqlTypeName() != null)) { SqlTypeName typeName = rhsType.getSqlTypeName(); int precision = 0; switch (typeName) { case DATE: precision = 10; break; case TIME: precision = 8; break; case TIMESTAMP: precision = 19; break; } if ((precision != 0) && (precision > lhsType.getPrecision())) { addStatement( new IfStatement( new BinaryExpression( Literal.makeLiteral(precision), BinaryExpression.GREATER, Literal.makeLiteral(lhsType.getPrecision())), getThrowStmtList())); } } if ((rhsType != null) && (rhsType.getFamily() == lhsType.getFamily()) && !SqlTypeUtil.isLob(rhsType)) { // we may be able to skip pad/truncate based on // known facts about source and target precisions if (SqlTypeUtil.isBoundedVariableWidth(lhsType)) { if (lhsType.getPrecision() >= rhsType.getPrecision()) { // target precision is greater than source // precision, so truncation is impossible // and we can skip adjustment return lhsExp; } } else { if ((lhsType.getPrecision() == rhsType.getPrecision()) && !SqlTypeUtil.isBoundedVariableWidth(rhsType)) { // source and target are both fixed-width, and // precisions are the same, so there's no adjustment // needed return lhsExp; } } } // determine target precision Expression precisionExp = Literal.makeLiteral(lhsType.getPrecision()); // need to pad only for fixed width Expression needPadExp = Literal.makeLiteral(!SqlTypeUtil.isBoundedVariableWidth(lhsType)); // pad character is 0 for binary, space for character Expression padByteExp; if (!SqlTypeUtil.inCharFamily(lhsType)) { padByteExp = new CastExpression(OJSystem.BYTE, Literal.makeLiteral(0)); } else { padByteExp = new CastExpression(OJSystem.BYTE, Literal.makeLiteral(' ')); } // generate the call to do the job addStatement( new ExpressionStatement( new MethodCall( lhsExp, BytePointer.ENFORCE_PRECISION_METHOD_NAME, new ExpressionList(precisionExp, needPadExp, padByteExp)))); } return lhsExp; }