/* * Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software;Designed and Developed mainly by many Chinese * opensource volunteers. you can redistribute it and/or modify it under the * terms of the GNU General Public License version 2 only, as published by the * Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Any questions about this component can be directed to it's project Web address * https://code.google.com/p/opencloudb/. * */ package com.akiban.sql.types; import com.akiban.sql.StandardException; import java.sql.Types; /** * DataTypeDescriptor describes a runtime SQL type. * It consists of a catalog type (TypeDescriptor) * and runtime attributes. The list of runtime * attributes is: * <UL> * <LI> Collation Derivation * </UL> * <P> * A DataTypeDescriptor is immutable. */ // NOTE: The Derby original had two levels of type descriptor, this // one for in memory and a simpler TypeDescriptor (the "catalog type") // that was actually stored in the data dictionary. For now, they have // been combined into this one. public final class DataTypeDescriptor { public static final int MAXIMUM_WIDTH_UNKNOWN = -1; public static final DataTypeDescriptor MEDIUMINT = new DataTypeDescriptor(TypeId.MEDIUMINT_ID, true); public static final DataTypeDescriptor MEDIUMINT_NOT_NULL = MEDIUMINT.getNullabilityType(true); /** * Runtime INTEGER type that is nullable. */ public static final DataTypeDescriptor INTEGER = new DataTypeDescriptor(TypeId.INTEGER_ID, true); /** * Runtime INTEGER type that is not nullable. */ public static final DataTypeDescriptor INTEGER_NOT_NULL = INTEGER.getNullabilityType(false); /** * Runtime SMALLINT type that is nullable. */ public static final DataTypeDescriptor SMALLINT = new DataTypeDescriptor(TypeId.SMALLINT_ID, true); /** * Runtime INTEGER type that is not nullable. */ public static final DataTypeDescriptor SMALLINT_NOT_NULL = SMALLINT.getNullabilityType(false); /* *** Static creators */ /** * Get a descriptor that corresponds to a nullable builtin JDBC type. * If a variable length type then the size information will be set * to the maximum possible. * * Collation type will be UCS_BASIC and derivation IMPLICIT. * * For well known types code may also use the pre-defined * runtime types that are fields of this class, such as INTEGER. * * @param jdbcType The int type of the JDBC type for which to get * a corresponding SQL DataTypeDescriptor * * @return A new DataTypeDescriptor that corresponds to the Java type. * A null return value means there is no corresponding SQL type */ public static DataTypeDescriptor getBuiltInDataTypeDescriptor(int jdbcType) { return getBuiltInDataTypeDescriptor(jdbcType, true); } /** * Get a descriptor that corresponds to a nullable builtin variable * length JDBC type. * * Collation type will be UCS_BASIC and derivation IMPLICIT. * * @param jdbcType The int type of the JDBC type for which to get * a corresponding SQL DataTypeDescriptor * * @return A new DataTypeDescriptor that corresponds to the Java type. * A null return value means there is no corresponding SQL type */ public static DataTypeDescriptor getBuiltInDataTypeDescriptor(int jdbcType, int length) { return getBuiltInDataTypeDescriptor(jdbcType, true, length); } /** * Get a descriptor that corresponds to a builtin JDBC type. * * For well known types code may also use the pre-defined * runtime types that are fields of this class, such as INTEGER. * E.g. using DataTypeDescriptor.INTEGER is preferred to * DataTypeDescriptor.getBuiltInDataTypeDescriptor(Types.INTEGER, true) * (both will return the same immutable object). * * @param jdbcType The int type of the JDBC type for which to get * a corresponding SQL DataTypeDescriptor * @param isNullable TRUE means it could contain NULL, FALSE means * it definitely cannot contain NULL. * * @return A new DataTypeDescriptor that corresponds to the Java type. * A null return value means there is no corresponding SQL type */ public static DataTypeDescriptor getBuiltInDataTypeDescriptor(int jdbcType, boolean isNullable) { // Re-use pre-defined types wherever possible. switch (jdbcType) { case Types.INTEGER: return isNullable ? INTEGER : INTEGER_NOT_NULL; case Types.SMALLINT: return isNullable ? SMALLINT : SMALLINT_NOT_NULL; default: break; } TypeId typeId = TypeId.getBuiltInTypeId(jdbcType); if (typeId == null) { return null; } return new DataTypeDescriptor(typeId, isNullable); } /** * Get a descriptor that corresponds to a builtin JDBC type. * * Collation type will be UCS_BASIC and derivation IMPLICIT. * * @param jdbcType The int type of the JDBC type for which to get * a corresponding SQL DataTypeDescriptor * @param isNullable TRUE means it could contain NULL, FALSE means * it definitely cannot contain NULL. * * @return A new DataTypeDescriptor that corresponds to the Java type. * A null return value means there is no corresponding SQL type */ public static DataTypeDescriptor getBuiltInDataTypeDescriptor(int jdbcType, boolean isNullable, int maxLength) { TypeId typeId = TypeId.getBuiltInTypeId(jdbcType); if (typeId == null) { return null; } return new DataTypeDescriptor(typeId, isNullable, maxLength); } /** * Get a DataTypeDescriptor that corresponds to a nullable builtin SQL type. * * Collation type will be UCS_BASIC and derivation IMPLICIT. * * @param sqlTypeName The name of the type for which to get * a corresponding SQL DataTypeDescriptor * * @return A new DataTypeDescriptor that corresponds to the Java type. * A null return value means there is no corresponding SQL type (only for 'char') */ public static DataTypeDescriptor getBuiltInDataTypeDescriptor(String sqlTypeName) { return new DataTypeDescriptor(TypeId.getBuiltInTypeId(sqlTypeName), true); } /** * Get a DataTypeDescriptor that corresponds to a builtin SQL type * * Collation type will be UCS_BASIC and derivation IMPLICIT. * * @param sqlTypeName The name of the type for which to get * a corresponding SQL DataTypeDescriptor * * @return A new DataTypeDescriptor that corresponds to the Java type. * A null return value means there is no corresponding SQL type (only for 'char') */ public static DataTypeDescriptor getBuiltInDataTypeDescriptor(String sqlTypeName, int length) { return new DataTypeDescriptor(TypeId.getBuiltInTypeId(sqlTypeName), true, length); } /** * Get a DataTypeDescriptor that corresponds to a Java type * * @param javaTypeName The name of the Java type for which to get * a corresponding SQL DataTypeDescriptor * * @return A new DataTypeDescriptor that corresponds to the Java type. * A null return value means there is no corresponding SQL type (only for 'char') */ public static DataTypeDescriptor getSQLDataTypeDescriptor(String javaTypeName) throws StandardException { return getSQLDataTypeDescriptor(javaTypeName, true); } /** * Get a DataTypeDescriptor that corresponds to a Java type * * @param javaTypeName The name of the Java type for which to get * a corresponding SQL DataTypeDescriptor * @param isNullable TRUE means it could contain NULL, FALSE means * it definitely cannot contain NULL. * * @return A new DataTypeDescriptor that corresponds to the Java type. * A null return value means there is no corresponding SQL type (only for 'char') */ public static DataTypeDescriptor getSQLDataTypeDescriptor(String javaTypeName, boolean isNullable) throws StandardException { TypeId typeId = TypeId.getSQLTypeForJavaType(javaTypeName); if (typeId == null) { return null; } return new DataTypeDescriptor(typeId, isNullable); } /** * Get a DataTypeDescriptor that corresponds to a Java type * * @param javaTypeName The name of the Java type for which to get * a corresponding SQL DataTypeDescriptor * @param precision The number of decimal digits * @param scale The number of digits after the decimal point * @param isNullable TRUE means it could contain NULL, FALSE means * it definitely cannot contain NULL. * @param maximumWidth The maximum width of a data value * represented by this type. * * @return A new DataTypeDescriptor that corresponds to the Java type. * A null return value means there is no corresponding SQL type. */ public static DataTypeDescriptor getSQLDataTypeDescriptor(String javaTypeName, int precision, int scale, boolean isNullable, int maximumWidth) throws StandardException { TypeId typeId = TypeId.getSQLTypeForJavaType(javaTypeName); if (typeId == null) { return null; } return new DataTypeDescriptor(typeId, precision, scale, isNullable, maximumWidth); } /** * Get a catalog type that corresponds to a SQL Row Multiset * * @param columnNames Names of the columns in the Row Muliset * @param catalogTypes Types of the columns in the Row Muliset * * @return A new DataTypeDescriptor describing the SQL Row Multiset */ public static DataTypeDescriptor getRowMultiSet(String[] columnNames, DataTypeDescriptor[] columnTypes) { return new DataTypeDescriptor(TypeId.getRowMultiSet(columnNames, columnTypes), true); } /* ** Instance fields & methods */ private TypeId typeId; private int precision; private int scale; private boolean isNullable; private int maximumWidth; private CharacterTypeAttributes characterAttributes; /** * Constructor for use with numeric types * * @param typeId The typeId of the type being described * @param precision The number of decimal digits. * @param scale The number of digits after the decimal point. * @param isNullable TRUE means it could contain NULL, FALSE means * it definitely cannot contain NULL. * @param maximumWidth The maximum number of bytes for this datatype */ public DataTypeDescriptor(TypeId typeId, int precision, int scale, boolean isNullable, int maximumWidth) { this.typeId = typeId; this.precision = precision; this.scale = scale; this.isNullable = isNullable; this.maximumWidth = maximumWidth; } /** * Constructor for use with non-numeric types * * @param typeId The typeId of the type being described * @param isNullable TRUE means it could contain NULL, FALSE means * it definitely cannot contain NULL. * @param maximumWidth The maximum number of bytes for this datatype */ public DataTypeDescriptor(TypeId typeId, boolean isNullable, int maximumWidth) { this.typeId = typeId; this.isNullable = isNullable; this.maximumWidth = maximumWidth; } public DataTypeDescriptor(TypeId typeId, boolean isNullable) { this.typeId = typeId; this.precision = typeId.getMaximumPrecision(); this.scale = typeId.getMaximumScale(); this.isNullable = isNullable; this.maximumWidth = typeId.getMaximumMaximumWidth(); } private DataTypeDescriptor(DataTypeDescriptor source, boolean isNullable) { this.typeId = source.typeId; this.precision = source.precision; this.scale = source.scale; this.isNullable = isNullable; this.maximumWidth = source.maximumWidth; this.characterAttributes = source.characterAttributes; } private DataTypeDescriptor(DataTypeDescriptor source, int precision, int scale, boolean isNullable, int maximumWidth) { this.typeId = source.typeId; this.precision = precision; this.scale = scale; this.isNullable = isNullable; this.maximumWidth = maximumWidth; } public DataTypeDescriptor(TypeId typeId, boolean isNullable, int maximumWidth, CharacterTypeAttributes characterAttributes) { this.typeId = typeId; this.isNullable = isNullable; this.maximumWidth = maximumWidth; this.characterAttributes = characterAttributes; } public DataTypeDescriptor(DataTypeDescriptor source, CharacterTypeAttributes characterAttributes) { this.typeId = source.typeId; this.precision = source.precision; this.scale = source.scale; this.isNullable = source.isNullable; this.maximumWidth = source.maximumWidth; this.characterAttributes = characterAttributes; } /** * Get the dominant type (DataTypeDescriptor) of the 2. * For variable length types, the resulting type will have the * biggest max length of the 2. * If either side is nullable, then the result will also be nullable. * * @param otherDTS DataTypeDescriptor to compare with. * * @return DataTypeDescriptor DTS for dominant type * * @exception StandardException Thrown on error */ public DataTypeDescriptor getDominantType(DataTypeDescriptor otherDTS) throws StandardException { boolean nullable; TypeId thisType; TypeId otherType; DataTypeDescriptor higherType; DataTypeDescriptor lowerType = null; int maximumWidth; int precision = getPrecision(); int scale = getScale(); thisType = getTypeId(); otherType = otherDTS.getTypeId(); /* The result is nullable if either side is nullable */ nullable = isNullable() || otherDTS.isNullable(); /* ** The result will have the maximum width of both sides */ maximumWidth = (getMaximumWidth() > otherDTS.getMaximumWidth()) ? getMaximumWidth() : otherDTS.getMaximumWidth(); /* We need 2 separate methods of determining type dominance - 1 if both * types are system built-in types and the other if at least 1 is * a user type. (typePrecedence is meaningless for user types.) */ if (!thisType.userType() && !otherType.userType()) { TypeId higherTypeId; TypeId lowerTypeId; if (thisType.typePrecedence() > otherType.typePrecedence()) { higherType = this; lowerType = otherDTS; higherTypeId = thisType; lowerTypeId = otherType; } else { higherType = otherDTS; lowerType = this; higherTypeId = otherType; lowerTypeId = thisType; } //Following is checking if higher type argument is real and other argument is decimal/bigint/integer/smallint, //then result type should be double if (higherTypeId.isRealTypeId() && !lowerTypeId.isRealTypeId() && lowerTypeId.isNumericTypeId()) { higherType = DataTypeDescriptor.getBuiltInDataTypeDescriptor(Types.DOUBLE); higherTypeId = TypeId.getBuiltInTypeId(Types.DOUBLE); } /* ** If we have a DECIMAL/NUMERIC we have to do some ** extra work to make sure the resultant type can ** handle the maximum values for the two input ** types. We cannot just take the maximum for ** precision. E.g. we want something like: ** ** DEC(10,10) and DEC(3,0) => DEC(13,10) ** ** (var)char type needs some conversion handled later. */ if (higherTypeId.isDecimalTypeId() && !lowerTypeId.isStringTypeId()) { precision = higherTypeId.getPrecision(this, otherDTS); if (precision > 31) precision = 31; //db2 silently does this and so do we scale = higherTypeId.getScale(this, otherDTS); /* maximumWidth needs to count possible leading '-' and * decimal point and leading '0' if scale > 0. See also * sqlgrammar.jj(exactNumericType). Beetle 3875 */ maximumWidth = (scale > 0) ? precision + 3 : precision + 1; } else if (thisType.typePrecedence() != otherType.typePrecedence()) { precision = higherType.getPrecision(); scale = higherType.getScale(); /* GROSS HACKS: * If we are doing an implicit (var)char->(var)bit conversion * then the maximum width for the (var)char as a (var)bit * is really 16 * its width as a (var)char. Adjust * maximumWidth accordingly. * If we are doing an implicit (var)char->decimal conversion * then we need to increment the decimal's precision by * 2 * the maximum width for the (var)char and the scale * by the maximum width for the (var)char. The maximumWidth * becomes the new precision + 3. This is because * the (var)char could contain any decimal value from XXXXXX * to 0.XXXXX. (In other words, we don't know which side of the * decimal point the characters will be on.) */ if (lowerTypeId.isStringTypeId()) { if (higherTypeId.isBitTypeId() && !higherTypeId.isLongConcatableTypeId()) { if (lowerTypeId.isLongConcatableTypeId()) { if (maximumWidth > (Integer.MAX_VALUE / 16)) maximumWidth = Integer.MAX_VALUE; else maximumWidth *= 16; } else { int charMaxWidth; int fromWidth = lowerType.getMaximumWidth(); if (fromWidth > (Integer.MAX_VALUE / 16)) charMaxWidth = Integer.MAX_VALUE; else charMaxWidth = 16 * fromWidth; maximumWidth = (maximumWidth >= charMaxWidth) ? maximumWidth : charMaxWidth; } } } /* * If we are doing an implicit (var)char->decimal conversion * then the resulting decimal's precision could be as high as * 2 * the maximum width (precisely 2mw-1) for the (var)char * and the scale could be as high as the maximum width * (precisely mw-1) for the (var)char. * The maximumWidth becomes the new precision + 3. This is * because the (var)char could contain any decimal value from * XXXXXX to 0.XXXXX. (In other words, we don't know which * side of the decimal point the characters will be on.) * * We don't follow this algorithm for long varchar because the * maximum length of a long varchar is maxint, and we don't * want to allocate a huge decimal value. So in this case, * the precision, scale, and maximum width all come from * the decimal type. */ if (lowerTypeId.isStringTypeId() && !lowerTypeId.isLongConcatableTypeId() && higherTypeId.isDecimalTypeId()) { int charMaxWidth = lowerType.getMaximumWidth(); int charPrecision; /* ** Be careful not to overflow when calculating the ** precision. Remember that we will be adding ** three to the precision to get the maximum width. */ if (charMaxWidth > (Integer.MAX_VALUE - 3) / 2) charPrecision = Integer.MAX_VALUE - 3; else charPrecision = charMaxWidth * 2; if (precision < charPrecision) precision = charPrecision; if (scale < charMaxWidth) scale = charMaxWidth; maximumWidth = precision + 3; } } } else { /* At least 1 type is not a system built-in type */ if (!thisType.equals(otherType)) { throw new StandardException("Two different user-defined types"); } higherType = this; precision = higherType.getPrecision(); scale = higherType.getScale(); } higherType = new DataTypeDescriptor(higherType, precision, scale, nullable, maximumWidth); higherType.characterAttributes = CharacterTypeAttributes.mergeCollations(characterAttributes, otherDTS.characterAttributes); return higherType; } /** * Get maximum width. */ public int getMaximumWidth() { return maximumWidth; } /** * Gets the TypeId for the datatype. * * @return The TypeId for the datatype. */ public TypeId getTypeId() { return typeId; } /** * Gets the name of this datatype. * * * @return the name of this datatype */ public String getTypeName() { return typeId.getSQLTypeName(); } /** * Get the jdbc type id for this type. JDBC type can be * found in java.sql.Types. * * @return a jdbc type, e.g. java.sql.Types.DECIMAL * * @see Types */ public int getJDBCTypeId() { return typeId.getJDBCTypeId(); } /** * Returns the number of decimal digits for the datatype, if applicable. * * @return The number of decimal digits for the datatype. Returns * zero for non-numeric datatypes. * @see TypeDescriptor#getPrecision() */ public int getPrecision() { return precision; } /** * Returns the number of digits to the right of the decimal for * the datatype, if applicable. * * @return The number of digits to the right of the decimal for * the datatype. Returns zero for non-numeric datatypes. * @see TypeDescriptor#getScale() */ public int getScale() { return scale; } /** * Returns TRUE if the datatype can contain NULL, FALSE if not. * JDBC supports a return value meaning "nullability unknown" - * I assume we will never have columns where the nullability is unknown. * * @return TRUE if the datatype can contain NULL, FALSE if not. */ public boolean isNullable() { return isNullable; } public boolean isRowMultiSet() { return typeId.isRowMultiSet(); } /** * Return a type descriptor identical to the this type * with the exception of its nullability. If the nullablity * required matches the nullability of this then this is returned. * * @param isNullable True to return a nullable type, false otherwise. */ public DataTypeDescriptor getNullabilityType(boolean isNullable) { if (isNullable() == isNullable) return this; return new DataTypeDescriptor(this, isNullable); } public CharacterTypeAttributes getCharacterAttributes() { return characterAttributes; } /** * Compare if two DataTypeDescriptors are exactly the same * @param other the type to compare to. */ public boolean equals(Object other) { if (!(other instanceof DataTypeDescriptor)) return false; DataTypeDescriptor odtd = (DataTypeDescriptor)other; if (!this.getTypeName().equals(odtd.getTypeName()) || this.precision != odtd.getPrecision() || this.scale != odtd.getScale() || this.isNullable != odtd.isNullable() || this.maximumWidth != odtd.getMaximumWidth() || ((this.characterAttributes == null) ? (odtd.characterAttributes != null) : !this.characterAttributes.equals(odtd.characterAttributes))) return false; else return true; } /** * Check if this type is comparable with the passed type. * * @param compareWithDTD the type of the instance to compare with this type. * @param forEquals True if this is an = or <> comparison, false * otherwise. * @return true if compareWithDTD is comparable to this type, else false. */ public boolean comparable(DataTypeDescriptor compareWithDTD, boolean forEquals) { TypeId compareWithTypeID = compareWithDTD.getTypeId(); int compareWithJDBCTypeId = compareWithTypeID.getJDBCTypeId(); // Incomparable types include: // XML (SQL/XML[2003] spec, section 4.2.2) // ref types if (!typeId.isComparable() || !compareWithTypeID.isComparable()) return false; // if the two types are equal, they should be comparable if (typeId.equals(compareWithTypeID)) return true; //If this DTD is not user defined type but the DTD to be compared with //is user defined type, then let the other DTD decide what should be the //outcome of the comparable method. if (!(typeId.isUserDefinedTypeId()) && (compareWithTypeID.isUserDefinedTypeId())) return compareWithDTD.comparable(this, forEquals); //Numeric types are comparable to numeric types if (typeId.isNumericTypeId()) return (compareWithTypeID.isNumericTypeId()); //CHAR, VARCHAR and LONGVARCHAR are comparable to strings, boolean, //DATE/TIME/TIMESTAMP and to comparable user types if (typeId.isStringTypeId()) { if((compareWithTypeID.isDateTimeTimeStampTypeID() || compareWithTypeID.isBooleanTypeId())) return true; //If both the types are string types, then we need to make sure //they have the same collation set on them if (compareWithTypeID.isStringTypeId() && typeId.isStringTypeId()) { return true; // TODO: compareCollationInfo(compareWithDTD); } else return false; //can't be compared } //Are comparable to other bit types and comparable user types if (typeId.isBitTypeId()) return (compareWithTypeID.isBitTypeId()); //Booleans are comparable to Boolean, string, and to //comparable user types. As part of the work on DERYB-887, //I removed the comparability of booleans to numerics; I don't //understand the previous statement about comparable user types. //I suspect that is wrong and should be addressed when we //re-enable UDTs (see DERBY-651). if (typeId.isBooleanTypeId()) return (compareWithTypeID.getSQLTypeName().equals(typeId.getSQLTypeName()) || compareWithTypeID.isStringTypeId()); //Dates are comparable to dates, strings and to comparable //user types. if (typeId.getJDBCTypeId() == Types.DATE) if (compareWithJDBCTypeId == Types.DATE || compareWithJDBCTypeId == Types.TIMESTAMP || compareWithTypeID.isStringTypeId()) return true; else return false; //Times are comparable to times, strings and to comparable //user types. if (typeId.getJDBCTypeId() == Types.TIME) if (compareWithJDBCTypeId == Types.TIME || compareWithTypeID.isStringTypeId()) return true; else return false; //Timestamps are comparable to timestamps, strings and to //comparable user types. if (typeId.getJDBCTypeId() == Types.TIMESTAMP) if (compareWithJDBCTypeId == Types.TIMESTAMP || compareWithJDBCTypeId == Types.DATE || compareWithTypeID.isStringTypeId()) return true; else return false; return false; } /** * Converts this data type descriptor (including length/precision) * to a string. E.g. * * VARCHAR(30) * * or * * java.util.Hashtable * * @return String version of datatype, suitable for running through * the Parser. */ public String getSQLstring() { return typeId.toParsableString(this); } /** * Compare JdbcTypeIds to determine if they represent equivalent * SQL types. For example Types.NUMERIC and Types.DECIMAL are * equivalent * * @param existingType JDBC type id of Derby data type * @param jdbcTypeId JDBC type id passed in from application. * * @return boolean true if types are equivalent, false if not */ public static boolean isJDBCTypeEquivalent(int existingType, int jdbcTypeId) { // Any type matches itself. if (existingType == jdbcTypeId) return true; // To a numeric type if (isNumericType(existingType)) { if (isNumericType(jdbcTypeId)) return true; if (isCharacterType(jdbcTypeId)) return true; return false; } // To character type. if (isCharacterType(existingType)) { if (isCharacterType(jdbcTypeId)) return true; if (isNumericType(jdbcTypeId)) return true; switch (jdbcTypeId) { case Types.DATE: case Types.TIME: case Types.TIMESTAMP: return true; default: break; } return false; } // To binary type if (isBinaryType(existingType)) { if (isBinaryType(jdbcTypeId)) return true; return false; } // To DATE, TIME if (existingType == Types.DATE || existingType == Types.TIME) { if (isCharacterType(jdbcTypeId)) return true; if (jdbcTypeId == Types.TIMESTAMP) return true; return false; } // To TIMESTAMP if (existingType == Types.TIMESTAMP) { if (isCharacterType(jdbcTypeId)) return true; if (jdbcTypeId == Types.DATE) return true; return false; } // To CLOB if (existingType == Types.CLOB && isCharacterType(jdbcTypeId)) return true; return false; } public static boolean isNumericType(int jdbcType) { switch (jdbcType) { case Types.BIT: case Types.BOOLEAN: case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: case Types.BIGINT: case Types.REAL: case Types.FLOAT: case Types.DOUBLE: case Types.DECIMAL: case Types.NUMERIC: return true; default: return false; } } /** * Check whether a JDBC type is one of the character types that are * compatible with the Java type <code>String</code>. * * <p><strong>Note:</strong> <code>CLOB</code> is not compatible with * <code>String</code>. See tables B-4, B-5 and B-6 in the JDBC 3.0 * Specification. * * <p> There are some non-character types that are compatible with * <code>String</code> (examples: numeric types, binary types and * time-related types), but they are not covered by this method. * * @param jdbcType a JDBC type * @return <code>true</code> iff <code>jdbcType</code> is a character type * and compatible with <code>String</code> * @see java.sql.Types */ private static boolean isCharacterType(int jdbcType) { switch (jdbcType) { case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: return true; default: return false; } } /** * Check whether a JDBC type is compatible with the Java type * <code>byte[]</code>. * * <p><strong>Note:</strong> <code>BLOB</code> is not compatible with * <code>byte[]</code>. See tables B-4, B-5 and B-6 in the JDBC 3.0 * Specification. * * @param jdbcType a JDBC type * @return <code>true</code> iff <code>jdbcType</code> is compatible with * <code>byte[]</code> * @see java.sql.Types */ private static boolean isBinaryType(int jdbcType) { switch (jdbcType) { case Types.BINARY: case Types.VARBINARY: case Types.LONGVARBINARY: return true; default: return false; } } /** * Determine if an ASCII stream can be inserted into a column or parameter * of type <code>jdbcType</code>. * * @param jdbcType JDBC type of column or parameter * @return <code>true</code> if an ASCII stream can be inserted; * <code>false</code> otherwise */ public static boolean isAsciiStreamAssignable(int jdbcType) { return jdbcType == Types.CLOB || isCharacterType(jdbcType); } /** * Determine if a binary stream can be inserted into a column or parameter * of type <code>jdbcType</code>. * * @param jdbcType JDBC type of column or parameter * @return <code>true</code> if a binary stream can be inserted; * <code>false</code> otherwise */ public static boolean isBinaryStreamAssignable(int jdbcType) { return jdbcType == Types.BLOB || isBinaryType(jdbcType); } /** * Determine if a character stream can be inserted into a column or * parameter of type <code>jdbcType</code>. * * @param jdbcType JDBC type of column or parameter * @return <code>true</code> if a character stream can be inserted; * <code>false</code> otherwise */ public static boolean isCharacterStreamAssignable(int jdbcType) { // currently, we support the same types for ASCII streams and // character streams return isAsciiStreamAssignable(jdbcType); } public String toString() { String s = getSQLstring(); if (characterAttributes != null) s += " " + characterAttributes; if (!isNullable()) s += " NOT NULL"; return s; } /** * Return the SQL type name and, if applicable, scale/precision/length * for this DataTypeDescriptor. Note that we want the values from *this* * object specifically, not the max values defined on this.typeId. */ public String getFullSQLTypeName() { StringBuffer sbuf = new StringBuffer(typeId.getSQLTypeName()); if (typeId.isDecimalTypeId() || typeId.isNumericTypeId()) { sbuf.append("("); sbuf.append(getPrecision()); sbuf.append(", "); sbuf.append(getScale()); sbuf.append(")"); } else if (typeId.isIntervalTypeId()) { if (typeId == TypeId.INTERVAL_SECOND_ID) { if (getPrecision() > 0) { sbuf.append("("); sbuf.append(getPrecision()); if (getScale() > 0) { sbuf.append(", "); sbuf.append(getScale()); } sbuf.append(")"); } } else { if (getPrecision() > 0) { int idx = sbuf.indexOf(" ", 9); if (idx < 0) idx = sbuf.length(); sbuf.insert(idx, ")"); sbuf.insert(idx, getPrecision()); sbuf.insert(idx, "("); } if (getScale() > 0) { sbuf.append("("); sbuf.append(getScale()); sbuf.append(")"); } } } else if (typeId.variableLength()) { sbuf.append("("); sbuf.append(getMaximumWidth()); sbuf.append(")"); } return sbuf.toString(); } /** * Compute the maximum width (column display width) of a decimal or numeric data value, * given its precision and scale. * * @param precision The precision (number of digits) of the data value. * @param scale The number of fractional digits (digits to the right of the decimal point). * * @return The maximum number of characters needed to display the value. */ public static int computeMaxWidth (int precision, int scale) { // There are 3 possible cases with respect to finding the correct max // width for DECIMAL type. // 1. If scale = 0, only sign should be added to precision. // 2. scale = precision, 3 should be added to precision for sign, decimal and an additional char '0'. // 3. precision > scale > 0, 2 should be added to precision for sign and decimal. return (scale == 0) ? (precision + 1) : ((scale == precision) ? (precision + 3) : (precision + 2)); } public DataTypeDescriptor getUnsigned() throws StandardException { TypeId unsignedTypeId; if (typeId == TypeId.SMALLINT_ID) unsignedTypeId = TypeId.SMALLINT_UNSIGNED_ID; else if (typeId == TypeId.MEDIUMINT_ID) unsignedTypeId = TypeId.MEDIUMINT_UNSIGNED_ID; else if (typeId == TypeId.INTEGER_ID) unsignedTypeId = TypeId.INTEGER_UNSIGNED_ID; else if (typeId == TypeId.TINYINT_ID) unsignedTypeId = TypeId.TINYINT_UNSIGNED_ID; else if (typeId == TypeId.BIGINT_ID) unsignedTypeId = TypeId.BIGINT_UNSIGNED_ID; else if (typeId == TypeId.REAL_ID) unsignedTypeId = TypeId.REAL_UNSIGNED_ID; else if (typeId == TypeId.DOUBLE_ID) unsignedTypeId = TypeId.DOUBLE_UNSIGNED_ID; else if (typeId == TypeId.DECIMAL_ID) unsignedTypeId = TypeId.DECIMAL_UNSIGNED_ID; else if (typeId == TypeId.NUMERIC_ID) unsignedTypeId = TypeId.NUMERIC_UNSIGNED_ID; else throw new StandardException("Not a numeric type: " + this); return new DataTypeDescriptor(unsignedTypeId, precision, scale, isNullable, maximumWidth); } public static int intervalMaxWidth(TypeId typeId, int precision, int scale) { int maxMax; if (typeId.getTypeFormatId() == TypeId.FormatIds.INTERVAL_YEAR_MONTH_ID) { if (precision == 0) precision = TypeId.INTERVAL_YEAR_MONTH_PRECISION; maxMax = TypeId.INTERVAL_YEAR_MONTH_MAXWIDTH; } else { if (precision == 0) precision = TypeId.INTERVAL_DAY_SECOND_PRECISION; maxMax = TypeId.INTERVAL_DAY_SECOND_MAXWIDTH; } if ((typeId == TypeId.INTERVAL_YEAR_ID) || (typeId == TypeId.INTERVAL_MONTH_ID) || (typeId == TypeId.INTERVAL_DAY_ID) || (typeId == TypeId.INTERVAL_HOUR_ID) || (typeId == TypeId.INTERVAL_MINUTE_ID)) { return precision; } else if (typeId == TypeId.INTERVAL_SECOND_ID) { if (scale == 0) return precision; else return precision + scale + 1; } else if (typeId == TypeId.INTERVAL_DAY_HOUR_ID) { return precision + 3; } else if (typeId == TypeId.INTERVAL_DAY_MINUTE_ID) { return precision + 6; } else if (typeId == TypeId.INTERVAL_DAY_SECOND_ID) { if (scale == 0) return precision + 9; else return precision + scale + 10; } else if (typeId == TypeId.INTERVAL_HOUR_MINUTE_ID) { return precision + 3; } else if (typeId == TypeId.INTERVAL_HOUR_SECOND_ID) { if (scale == 0) return precision + 6; else return precision + scale + 7; } else if (typeId == TypeId.INTERVAL_MINUTE_SECOND_ID) { if (scale == 0) return precision + 3; else return precision + scale + 4; } else return maxMax; } }