--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.iapi.types.DataTypeDescriptor\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.iapi.types;\r
+\r
+import java.io.IOException;\r
+import java.io.ObjectInput;\r
+import java.io.ObjectOutput;\r
+import java.sql.Types;\r
+import java.text.RuleBasedCollator;\r
+\r
+import org.apache.derby.catalog.TypeDescriptor;\r
+import org.apache.derby.catalog.types.TypeDescriptorImpl;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.io.Formatable;\r
+import org.apache.derby.iapi.services.io.StoredFormatIds;\r
+import org.apache.derby.iapi.services.loader.ClassFactory;\r
+import org.apache.derby.iapi.services.loader.ClassInspector;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.conn.ConnectionUtil;\r
+\r
+/** \r
+ * This is an implementation of DataTypeDescriptor from the generic language\r
+ * datatype module interface.\r
+ *\r
+ * @version 1.0\r
+ */\r
+\r
+public final class DataTypeDescriptor implements TypeDescriptor, Formatable\r
+{\r
+ /********************************************************\r
+ **\r
+ ** This class implements Formatable. That means that it\r
+ ** can write itself to and from a formatted stream. If\r
+ ** you add more fields to this class, make sure that you\r
+ ** also write/read them with the writeExternal()/readExternal()\r
+ ** methods.\r
+ **\r
+ ** If, inbetween releases, you add more fields to this class,\r
+ ** then you should bump the version number emitted by the getTypeFormatId()\r
+ ** method.\r
+ **\r
+ ********************************************************/\r
+\r
+ /*\r
+ ** Static creators\r
+ */\r
+ /**\r
+ * Get a descriptor that corresponds to a builtin JDBC type.\r
+ *\r
+ * @param jdbcType The int type of the JDBC type for which to get\r
+ * a corresponding SQL DataTypeDescriptor\r
+ *\r
+ * @return A new DataTypeDescriptor that corresponds to the Java type.\r
+ * A null return value means there is no corresponding SQL type\r
+ */\r
+ public static DataTypeDescriptor getBuiltInDataTypeDescriptor\r
+ (\r
+ int jdbcType\r
+ )\r
+ {\r
+ return DataTypeDescriptor.getBuiltInDataTypeDescriptor(jdbcType, true);\r
+ }\r
+ public static DataTypeDescriptor getBuiltInDataTypeDescriptor\r
+ (\r
+ int jdbcType,\r
+ int length\r
+ )\r
+ {\r
+ return DataTypeDescriptor.getBuiltInDataTypeDescriptor(jdbcType, true, length);\r
+ }\r
+\r
+ /**\r
+ * Get a descriptor that corresponds to a builtin JDBC type.\r
+ *\r
+ * @param jdbcType The int type of the JDBC type for which to get\r
+ * a corresponding SQL DataTypeDescriptor\r
+ * @param isNullable TRUE means it could contain NULL, FALSE means\r
+ * it definitely cannot contain NULL.\r
+ *\r
+ * @return A new DataTypeDescriptor that corresponds to the Java type.\r
+ * A null return value means there is no corresponding SQL type\r
+ */\r
+ public static DataTypeDescriptor getBuiltInDataTypeDescriptor\r
+ (\r
+ int jdbcType, \r
+ boolean isNullable\r
+ )\r
+ {\r
+ TypeId typeId = TypeId.getBuiltInTypeId(jdbcType);\r
+ if (typeId == null)\r
+ {\r
+ return null;\r
+ }\r
+\r
+ return new DataTypeDescriptor(typeId, isNullable);\r
+ }\r
+ /**\r
+ * Get a descriptor that corresponds to a builtin JDBC type.\r
+ *\r
+ * @param jdbcType The int type of the JDBC type for which to get\r
+ * a corresponding SQL DataTypeDescriptor\r
+ * @param isNullable TRUE means it could contain NULL, FALSE means\r
+ * it definitely cannot contain NULL.\r
+ *\r
+ * @return A new DataTypeDescriptor that corresponds to the Java type.\r
+ * A null return value means there is no corresponding SQL type\r
+ */\r
+ public static DataTypeDescriptor getBuiltInDataTypeDescriptor\r
+ (\r
+ int jdbcType, \r
+ boolean isNullable,\r
+ int maxLength\r
+ )\r
+ {\r
+ TypeId typeId = TypeId.getBuiltInTypeId(jdbcType);\r
+ if (typeId == null)\r
+ {\r
+ return null;\r
+ }\r
+\r
+ return new DataTypeDescriptor(typeId, isNullable, maxLength);\r
+ }\r
+ /**\r
+ * Get a DataTypeServices that corresponds to a builtin SQL type\r
+ *\r
+ * @param sqlTypeName The name of the type for which to get\r
+ * a corresponding SQL DataTypeDescriptor\r
+ *\r
+ * @return A new DataTypeDescriptor that corresponds to the Java type.\r
+ * A null return value means there is no corresponding SQL type (only for 'char')\r
+ */\r
+ public static DataTypeDescriptor getBuiltInDataTypeDescriptor\r
+ (\r
+ String sqlTypeName\r
+ )\r
+ {\r
+ return new DataTypeDescriptor(TypeId.getBuiltInTypeId(sqlTypeName), true);\r
+ }\r
+ /**\r
+ * Get a DataTypeServices that corresponds to a builtin SQL type\r
+ *\r
+ * @param sqlTypeName The name of the type for which to get\r
+ * a corresponding SQL DataTypeDescriptor\r
+ *\r
+ * @return A new DataTypeDescriptor that corresponds to the Java type.\r
+ * A null return value means there is no corresponding SQL type (only for 'char')\r
+ */\r
+ public static DataTypeDescriptor getBuiltInDataTypeDescriptor\r
+ (\r
+ String sqlTypeName,\r
+ int length\r
+ )\r
+ {\r
+ return new DataTypeDescriptor(TypeId.getBuiltInTypeId(sqlTypeName), true, length);\r
+ }\r
+ /**\r
+ * Get a DataTypeServices that corresponds to a Java type\r
+ *\r
+ * @param javaTypeName The name of the Java type for which to get\r
+ * a corresponding SQL DataTypeDescriptor\r
+ *\r
+ * @return A new DataTypeDescriptor that corresponds to the Java type.\r
+ * A null return value means there is no corresponding SQL type (only for 'char')\r
+ */\r
+ public static DataTypeDescriptor getSQLDataTypeDescriptor\r
+ (\r
+ String javaTypeName\r
+ )\r
+ {\r
+ return DataTypeDescriptor.getSQLDataTypeDescriptor(javaTypeName, true);\r
+ }\r
+\r
+ /**\r
+ * Get a DataTypeServices that corresponds to a Java type\r
+ *\r
+ * @param javaTypeName The name of the Java type for which to get\r
+ * a corresponding SQL DataTypeDescriptor\r
+ * @param isNullable TRUE means it could contain NULL, FALSE means\r
+ * it definitely cannot contain NULL.\r
+ *\r
+ * @return A new DataTypeDescriptor that corresponds to the Java type.\r
+ * A null return value means there is no corresponding SQL type (only for 'char')\r
+ */\r
+ public static DataTypeDescriptor getSQLDataTypeDescriptor\r
+ (\r
+ String javaTypeName, \r
+ boolean isNullable\r
+ )\r
+ {\r
+ TypeId typeId = TypeId.getSQLTypeForJavaType(javaTypeName);\r
+ if (typeId == null)\r
+ {\r
+ return null;\r
+ }\r
+\r
+ return new DataTypeDescriptor(typeId, isNullable);\r
+ }\r
+\r
+ /**\r
+ * Get a DataTypeDescriptor that corresponds to a Java type\r
+ *\r
+ * @param javaTypeName The name of the Java type for which to get\r
+ * a corresponding SQL DataTypeDescriptor\r
+ * @param precision The number of decimal digits\r
+ * @param scale The number of digits after the decimal point\r
+ * @param isNullable TRUE means it could contain NULL, FALSE means\r
+ * it definitely cannot contain NULL.\r
+ * @param maximumWidth The maximum width of a data value\r
+ * represented by this type.\r
+ *\r
+ * @return A new DataTypeDescriptor that corresponds to the Java type.\r
+ * A null return value means there is no corresponding SQL type.\r
+ */\r
+ public static DataTypeDescriptor getSQLDataTypeDescriptor\r
+ (\r
+ String javaTypeName, \r
+ int precision,\r
+ int scale, \r
+ boolean isNullable, \r
+ int maximumWidth\r
+ )\r
+ {\r
+ TypeId typeId = TypeId.getSQLTypeForJavaType(javaTypeName);\r
+ if (typeId == null)\r
+ {\r
+ return null;\r
+ }\r
+\r
+ return new DataTypeDescriptor(typeId,\r
+ precision,\r
+ scale,\r
+ isNullable,\r
+ maximumWidth);\r
+ }\r
+ \r
+ /*\r
+ ** Instance fields & methods\r
+ */\r
+\r
+ private TypeDescriptorImpl typeDescriptor;\r
+ private TypeId typeId;\r
+\r
+ /**\r
+ * Public niladic constructor. Needed for Formatable interface to work.\r
+ *\r
+ */\r
+ public DataTypeDescriptor() {}\r
+\r
+ /**\r
+ * Constructor for use with numeric types\r
+ *\r
+ * @param typeId The typeId of the type being described\r
+ * @param precision The number of decimal digits.\r
+ * @param scale The number of digits after the decimal point.\r
+ * @param isNullable TRUE means it could contain NULL, FALSE means\r
+ * it definitely cannot contain NULL.\r
+ * @param maximumWidth The maximum number of bytes for this datatype\r
+ */\r
+ public DataTypeDescriptor(TypeId typeId, int precision, int scale,\r
+ boolean isNullable, int maximumWidth)\r
+ {\r
+ this.typeId = typeId;\r
+ typeDescriptor = new TypeDescriptorImpl(typeId.getBaseTypeId(),\r
+ precision,\r
+ scale,\r
+ isNullable,\r
+ maximumWidth);\r
+ }\r
+\r
+ /**\r
+ * Constructor to use when the caller doesn't know if it is requesting\r
+ * numeric or no-numeric DTD. For instance, when dealing with MAX/MIN \r
+ * aggregrate operators, AggregateNode.bindExpression could be dealing\r
+ * with a character string operand or a numeric operand. The result of\r
+ * MAX/MIN will depend on the type of it's operand. And hence when this\r
+ * constructor gets called by AggregateNode.bindExpression, we don't know \r
+ * what type we are constructing and hence this constructor supports \r
+ * arguments for both numeric and non-numeric types.\r
+ *\r
+ * @param typeId The typeId of the type being described\r
+ * @param precision The number of decimal digits.\r
+ * @param scale The number of digits after the decimal point.\r
+ * @param isNullable TRUE means it could contain NULL, FALSE means\r
+ * it definitely cannot contain NULL.\r
+ * @param maximumWidth The maximum number of bytes for this datatype\r
+ * @param collationType The collation type of a string data type\r
+ * @param collationDerivation Collation Derivation of a string data type\r
+ */\r
+ public DataTypeDescriptor(TypeId typeId, int precision, int scale,\r
+ boolean isNullable, int maximumWidth, int collationType,\r
+ int collationDerivation)\r
+ {\r
+ this.typeId = typeId;\r
+ typeDescriptor = new TypeDescriptorImpl(typeId.getBaseTypeId(),\r
+ precision,\r
+ scale,\r
+ isNullable,\r
+ maximumWidth,\r
+ collationType,\r
+ collationDerivation);\r
+ }\r
+\r
+ /**\r
+ * Constructor for use with non-numeric types\r
+ *\r
+ * @param typeId The typeId of the type being described\r
+ * @param isNullable TRUE means it could contain NULL, FALSE means\r
+ * it definitely cannot contain NULL.\r
+ * @param maximumWidth The maximum number of bytes for this datatype\r
+ */\r
+ public DataTypeDescriptor(TypeId typeId, boolean isNullable,\r
+ int maximumWidth)\r
+ {\r
+ this.typeId = typeId;\r
+ typeDescriptor = new TypeDescriptorImpl(typeId.getBaseTypeId(),\r
+ isNullable,\r
+ maximumWidth);\r
+ }\r
+\r
+\r
+ public DataTypeDescriptor(TypeId typeId, boolean isNullable) {\r
+\r
+ this.typeId = typeId;\r
+ typeDescriptor = new TypeDescriptorImpl(typeId.getBaseTypeId(),\r
+ typeId.getMaximumPrecision(),\r
+ typeId.getMaximumScale(),\r
+ isNullable,\r
+ typeId.getMaximumMaximumWidth());\r
+ }\r
+ public DataTypeDescriptor(DataTypeDescriptor source, boolean isNullable)\r
+ {\r
+ //There might be other places, but one place this method gets called\r
+ //from is ResultColumn.init. When the ResultColumn(RC) is for a \r
+ //ColumnDescriptor(CD), the RC's TypeDescriptorImpl(TDI) should get \r
+ //all the attributes of CD's TDI. So, if the CD is for a user table's\r
+ //character type column, then this call by RC.init should have CD's \r
+ //collation attributes copied into RC along with other attributes. \r
+ this.typeId = source.typeId;\r
+ typeDescriptor = new TypeDescriptorImpl(source.typeDescriptor,\r
+ source.getPrecision(),\r
+ source.getScale(),\r
+ isNullable,\r
+ source.getMaximumWidth(),\r
+ source.getCollationType(),\r
+ source.getCollationDerivation());\r
+ }\r
+\r
+ /**\r
+ * Constructor for internal uses only. \r
+ * (This is useful when the precision and scale are potentially wider than\r
+ * those in the source, like when determining the dominant data type.)\r
+ *\r
+ * @param source The DTSI to copy\r
+ * @param precision The number of decimal digits.\r
+ * @param scale The number of digits after the decimal point.\r
+ * @param isNullable TRUE means it could contain NULL, FALSE means\r
+ * it definitely cannot contain NULL.\r
+ * @param maximumWidth The maximum number of bytes for this datatype\r
+ */\r
+ public DataTypeDescriptor(DataTypeDescriptor source, \r
+ int precision,\r
+ int scale,\r
+ boolean isNullable,\r
+ int maximumWidth)\r
+ {\r
+ this.typeId = source.typeId;\r
+ typeDescriptor = new TypeDescriptorImpl(source.typeDescriptor,\r
+ precision,\r
+ scale,\r
+ isNullable,\r
+ maximumWidth,\r
+ source.getCollationType(),\r
+ source.getCollationDerivation());\r
+ }\r
+\r
+ /**\r
+ * Constructor for internal uses only\r
+ *\r
+ * @param source The DTSI to copy\r
+ * @param isNullable TRUE means it could contain NULL, FALSE means\r
+ * it definitely cannot contain NULL.\r
+ * @param maximumWidth The maximum number of bytes for this datatype\r
+ */\r
+ public DataTypeDescriptor(DataTypeDescriptor source, boolean isNullable,\r
+ int maximumWidth)\r
+ {\r
+ this.typeId = source.typeId;\r
+ typeDescriptor = new TypeDescriptorImpl(source.typeDescriptor,\r
+ source.getPrecision(),\r
+ source.getScale(),\r
+ isNullable,\r
+ maximumWidth,\r
+ source.getCollationType(),\r
+ source.getCollationDerivation());\r
+\r
+ }\r
+\r
+ /**\r
+ * Constructor for use in reconstructing a DataTypeDescriptor from a\r
+ * TypeDescriptorImpl and a TypeId\r
+ *\r
+ * @param source The TypeDescriptorImpl to construct this DTSI from\r
+ */\r
+ public DataTypeDescriptor(TypeDescriptorImpl source, TypeId typeId)\r
+ {\r
+ typeDescriptor = source;\r
+ this.typeId = typeId;;\r
+ }\r
+\r
+ /* DataTypeDescriptor Interface */\r
+ public DataValueDescriptor normalize(DataValueDescriptor source,\r
+ DataValueDescriptor cachedDest)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG) {\r
+ if (cachedDest != null) {\r
+ if (!getTypeId().isUserDefinedTypeId()) {\r
+ String t1 = getTypeName();\r
+ String t2 = cachedDest.getTypeName();\r
+ if (!t1.equals(t2)) {\r
+\r
+ if (!(((t1.equals("DECIMAL") || t1.equals("NUMERIC"))\r
+ && (t2.equals("DECIMAL") || t2.equals("NUMERIC"))) ||\r
+ (t1.startsWith("INT") && t2.startsWith("INT")))) //INT/INTEGER\r
+\r
+ SanityManager.THROWASSERT(\r
+ "Normalization of " + t2 + " being asked to convert to " + t1);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ if (source.isNull())\r
+ {\r
+ if (!isNullable())\r
+ throw StandardException.newException(SQLState.LANG_NULL_INTO_NON_NULL,"");\r
+\r
+ if (cachedDest == null)\r
+ cachedDest = getNull();\r
+ else\r
+ cachedDest.setToNull();\r
+ } else {\r
+\r
+ if (cachedDest == null)\r
+ cachedDest = getNull();\r
+\r
+ int jdbcId = getJDBCTypeId();\r
+\r
+ cachedDest.normalize(this, source);\r
+ //doing the following check after normalize so that normalize method would get called on long varchs and long varbinary\r
+ //Need normalize to be called on long varchar for bug 5592 where we need to enforce a lenght limit in db2 mode\r
+ if ((jdbcId == Types.LONGVARCHAR) || (jdbcId == Types.LONGVARBINARY)) {\r
+ // special case for possible streams\r
+ if (source.getClass() == cachedDest.getClass()) \r
+ return source;\r
+ }\r
+\r
+ }\r
+ return cachedDest;\r
+ }\r
+ \r
+ /**\r
+ * Get the dominant type (DataTypeDescriptor) of the 2.\r
+ * For variable length types, the resulting type will have the\r
+ * biggest max length of the 2.\r
+ * If either side is nullable, then the result will also be nullable.\r
+ * \r
+ * If dealing with character string types, then make sure to set the\r
+ * collation info on the dominant type. Following algorithm will be used \r
+ * for dominant DTD's collation determination. Each of the steps of the \r
+ * algorithm have been numbered in the comments below and those same \r
+ * numbers are used in the actual algorithm below so it is easier to \r
+ * understand and maintain.\r
+ * \r
+ * Step 1\r
+ * If the DTD for "this" node has the same collation derivation as the \r
+ * otherDTS, then check if their collation types match too. If the \r
+ * collation types match too, then DTD for dominant type will get the same \r
+ * collation derivation and type.\r
+ * \r
+ * Step 2\r
+ * If the collation derivation for DTD for "this" node and otherDTS do not \r
+ * match, then check if one of them has the collation derivation of NONE. \r
+ * If that is the case, then dominant DTD will get the collation type and \r
+ * derivation of DTD whose collation derivation is not NONE.\r
+ * \r
+ * Step 3\r
+ * If the collation derivation for DTD for "this" node and otherDTS do not \r
+ * match, and none of them have the derivation of NONE then it means that \r
+ * we are dealing with collation derivation of IMPLICIT and EXPLICIT and \r
+ * hence the dominant DTD should get collation derivation of NONE. This is \r
+ * not a possibility in Derby 10.3 because the only 2 possible collation \r
+ * derivation supported are IMPLICIT and NONE.\r
+ * \r
+ * Step 4\r
+ * If the collation derivation for DTD for "this" node and otherDTS match, \r
+ * then check if the collation types match too. If not, then the dominant \r
+ * DTD should get collation derivation of NONE. \r
+ *\r
+ * @param otherDTS DataTypeDescriptor to compare with.\r
+ * @param cf A ClassFactory\r
+ *\r
+ * @return DataTypeDescriptor DTS for dominant type\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public DataTypeDescriptor getDominantType(DataTypeDescriptor otherDTS, ClassFactory cf)\r
+ throws StandardException\r
+ {\r
+ boolean nullable;\r
+ TypeId thisType;\r
+ TypeId otherType;\r
+ DataTypeDescriptor higherType = null;\r
+ DataTypeDescriptor lowerType = null;\r
+ int maximumWidth;\r
+ int precision = getPrecision();\r
+ int scale = getScale();\r
+\r
+ thisType = getTypeId();\r
+ otherType = otherDTS.getTypeId();\r
+\r
+ /* The result is nullable if either side is nullable */\r
+ nullable = isNullable() || otherDTS.isNullable();\r
+\r
+ /*\r
+ ** The result will have the maximum width of both sides\r
+ */\r
+ maximumWidth = (getMaximumWidth() > otherDTS.getMaximumWidth())\r
+ ? getMaximumWidth() : otherDTS.getMaximumWidth();\r
+\r
+ /* We need 2 separate methods of determining type dominance - 1 if both\r
+ * types are system built-in types and the other if at least 1 is\r
+ * a user type. (typePrecedence is meaningless for user types.)\r
+ */\r
+ if (!thisType.userType() && !otherType.userType())\r
+ {\r
+ TypeId higherTypeId;\r
+ TypeId lowerTypeId;\r
+ if (thisType.typePrecedence() > otherType.typePrecedence())\r
+ {\r
+ higherType = this;\r
+ lowerType = otherDTS;\r
+ higherTypeId = thisType;\r
+ lowerTypeId = otherType;\r
+ }\r
+ else\r
+ {\r
+ higherType = otherDTS;\r
+ lowerType = this;\r
+ higherTypeId = otherType;\r
+ lowerTypeId = thisType;\r
+ }\r
+\r
+ //Following is checking if higher type argument is real and other argument is decimal/bigint/integer/smallint,\r
+ //then result type should be double\r
+ if (higherTypeId.isRealTypeId() && (!lowerTypeId.isRealTypeId()) && lowerTypeId.isNumericTypeId())\r
+ {\r
+ higherType = DataTypeDescriptor.getBuiltInDataTypeDescriptor(Types.DOUBLE);\r
+ higherTypeId = TypeId.getBuiltInTypeId(Types.DOUBLE);\r
+ }\r
+ /*\r
+ ** If we have a DECIMAL/NUMERIC we have to do some\r
+ ** extra work to make sure the resultant type can\r
+ ** handle the maximum values for the two input\r
+ ** types. We cannot just take the maximum for\r
+ ** precision. E.g. we want something like:\r
+ **\r
+ ** DEC(10,10) and DEC(3,0) => DEC(13,10)\r
+ **\r
+ ** (var)char type needs some conversion handled later.\r
+ */\r
+ if (higherTypeId.isDecimalTypeId() && (!lowerTypeId.isStringTypeId()))\r
+ {\r
+ precision = higherTypeId.getPrecision(this, otherDTS);\r
+ if (precision > 31) precision = 31; //db2 silently does this and so do we\r
+ scale = higherTypeId.getScale(this, otherDTS);\r
+\r
+ /* maximumWidth needs to count possible leading '-' and\r
+ * decimal point and leading '0' if scale > 0. See also\r
+ * sqlgrammar.jj(exactNumericType). Beetle 3875\r
+ */\r
+ maximumWidth = (scale > 0) ? precision + 3 : precision + 1;\r
+ }\r
+ else if (thisType.typePrecedence() != otherType.typePrecedence())\r
+ {\r
+ precision = higherType.getPrecision();\r
+ scale = higherType.getScale();\r
+\r
+ /* GROSS HACKS:\r
+ * If we are doing an implicit (var)char->(var)bit conversion\r
+ * then the maximum width for the (var)char as a (var)bit\r
+ * is really 16 * its width as a (var)char. Adjust\r
+ * maximumWidth accordingly.\r
+ * If we are doing an implicit (var)char->decimal conversion\r
+ * then we need to increment the decimal's precision by\r
+ * 2 * the maximum width for the (var)char and the scale\r
+ * by the maximum width for the (var)char. The maximumWidth\r
+ * becomes the new precision + 3. This is because\r
+ * the (var)char could contain any decimal value from XXXXXX\r
+ * to 0.XXXXX. (In other words, we don't know which side of the\r
+ * decimal point the characters will be on.)\r
+ */\r
+ if (lowerTypeId.isStringTypeId())\r
+ {\r
+ if (higherTypeId.isBitTypeId() &&\r
+ ! (higherTypeId.isLongConcatableTypeId()))\r
+ {\r
+ if (lowerTypeId.isLongConcatableTypeId())\r
+ {\r
+ if (maximumWidth > (Integer.MAX_VALUE / 16))\r
+ maximumWidth = Integer.MAX_VALUE;\r
+ else\r
+ maximumWidth *= 16;\r
+ }\r
+ else\r
+ {\r
+ int charMaxWidth;\r
+\r
+ int fromWidth = lowerType.getMaximumWidth();\r
+ if (fromWidth > (Integer.MAX_VALUE / 16))\r
+ charMaxWidth = Integer.MAX_VALUE;\r
+ else\r
+ charMaxWidth = 16 * fromWidth;\r
+\r
+ maximumWidth = (maximumWidth >= charMaxWidth) ?\r
+ maximumWidth : charMaxWidth;\r
+ }\r
+ }\r
+ }\r
+\r
+ /*\r
+ * If we are doing an implicit (var)char->decimal conversion\r
+ * then the resulting decimal's precision could be as high as \r
+ * 2 * the maximum width (precisely 2mw-1) for the (var)char\r
+ * and the scale could be as high as the maximum width\r
+ * (precisely mw-1) for the (var)char.\r
+ * The maximumWidth becomes the new precision + 3. This is\r
+ * because the (var)char could contain any decimal value from\r
+ * XXXXXX to 0.XXXXX. (In other words, we don't know which\r
+ * side of the decimal point the characters will be on.)\r
+ *\r
+ * We don't follow this algorithm for long varchar because the\r
+ * maximum length of a long varchar is maxint, and we don't\r
+ * want to allocate a huge decimal value. So in this case,\r
+ * the precision, scale, and maximum width all come from\r
+ * the decimal type.\r
+ */\r
+ if (lowerTypeId.isStringTypeId() &&\r
+ ! (lowerTypeId.isLongConcatableTypeId()) &&\r
+ higherTypeId.isDecimalTypeId() )\r
+ {\r
+ int charMaxWidth = lowerType.getMaximumWidth();\r
+ int charPrecision;\r
+\r
+ /*\r
+ ** Be careful not to overflow when calculating the\r
+ ** precision. Remember that we will be adding\r
+ ** three to the precision to get the maximum width.\r
+ */\r
+ if (charMaxWidth > (Integer.MAX_VALUE - 3) / 2)\r
+ charPrecision = Integer.MAX_VALUE - 3;\r
+ else\r
+ charPrecision = charMaxWidth * 2;\r
+\r
+ if (precision < charPrecision)\r
+ precision = charPrecision;\r
+\r
+ if (scale < charMaxWidth)\r
+ scale = charMaxWidth;\r
+\r
+ maximumWidth = precision + 3;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* At least 1 type is not a system built-in type */\r
+ ClassInspector cu = cf.getClassInspector();\r
+\r
+ TypeId thisCompType = (TypeId) thisType;\r
+ TypeId otherCompType = (TypeId) otherType;\r
+\r
+ if (cu.assignableTo(thisCompType.getCorrespondingJavaTypeName(),\r
+ otherCompType.getCorrespondingJavaTypeName()))\r
+ {\r
+ higherType = otherDTS;\r
+ }\r
+ else\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(\r
+ cu.assignableTo(otherCompType.getCorrespondingJavaTypeName(),\r
+ thisCompType.getCorrespondingJavaTypeName()),\r
+ otherCompType.getCorrespondingJavaTypeName() +\r
+ " expected to be assignable to " +\r
+ thisCompType.getCorrespondingJavaTypeName());\r
+\r
+ higherType = this;\r
+ }\r
+ precision = higherType.getPrecision();\r
+ scale = higherType.getScale();\r
+ }\r
+\r
+\r
+ higherType = new DataTypeDescriptor(higherType, \r
+ precision, scale, nullable, maximumWidth);\r
+\r
+ //Set collation info on the DTD for dominant type if it is string type\r
+ //The algorithm used is explained in this method's javadoc\r
+ if (higherType.getTypeId().isStringTypeId()) {\r
+ if (getCollationDerivation() != otherDTS.getCollationDerivation()) {\r
+ if (getCollationDerivation() == StringDataValue.COLLATION_DERIVATION_NONE) {\r
+ //Step 2\r
+ higherType.setCollationDerivation(otherDTS.getCollationDerivation()); \r
+ higherType.setCollationType(otherDTS.getCollationType()); \r
+ } else if (otherDTS.getCollationDerivation() == StringDataValue.COLLATION_DERIVATION_NONE) {\r
+ //Step 2\r
+ higherType.setCollationDerivation(getCollationDerivation()); \r
+ higherType.setCollationType(getCollationType()); \r
+ } else {\r
+ //Step 3\r
+ higherType.setCollationDerivation(StringDataValue.COLLATION_DERIVATION_NONE); \r
+ }\r
+ } else if (getCollationType() != otherDTS.getCollationType())\r
+ //Step 4\r
+ higherType.setCollationDerivation(StringDataValue.COLLATION_DERIVATION_NONE); \r
+ else {\r
+ //Step 1\r
+ higherType.setCollationDerivation(getCollationDerivation()); \r
+ higherType.setCollationType(getCollationType()); \r
+ }\r
+ }\r
+\r
+ return higherType;\r
+ }\r
+\r
+ /**\r
+ * Check whether or not the 2 types (DataTypeDescriptor) have the same type\r
+ * and length.\r
+ * This is useful for UNION when trying to decide whether a NormalizeResultSet\r
+ * is required.\r
+ *\r
+ * @param otherDTS DataTypeDescriptor to compare with.\r
+ *\r
+ * @return boolean Whether or not the 2 DTSs have the same type and length.\r
+ */\r
+ public boolean isExactTypeAndLengthMatch(DataTypeDescriptor otherDTS)\r
+ {\r
+ /* Do both sides have the same length? */\r
+ if (getMaximumWidth() != otherDTS.getMaximumWidth()) \r
+ {\r
+ return false;\r
+ }\r
+ if (getScale() != otherDTS.getScale())\r
+ {\r
+ return false;\r
+ }\r
+\r
+ if (getPrecision() != otherDTS.getPrecision())\r
+ { \r
+ return false;\r
+ }\r
+\r
+ TypeId thisType = getTypeId();\r
+ TypeId otherType = otherDTS.getTypeId();\r
+\r
+ /* Do both sides have the same type? */\r
+ if ( ! thisType.equals(otherType))\r
+ {\r
+ return false;\r
+ }\r
+\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * @see TypeDescriptor#getMaximumWidth\r
+ */\r
+ public int getMaximumWidth()\r
+ {\r
+ return typeDescriptor.getMaximumWidth();\r
+ }\r
+\r
+ /**\r
+ * @see TypeDescriptor#getMaximumWidthInBytes\r
+ */\r
+ public int getMaximumWidthInBytes()\r
+ {\r
+ return typeDescriptor.getMaximumWidthInBytes();\r
+ }\r
+\r
+ /**\r
+ * Gets the TypeId for the datatype.\r
+ *\r
+ * @return The TypeId for the datatype.\r
+ */\r
+ public TypeId getTypeId()\r
+ {\r
+ return typeId;\r
+ }\r
+\r
+ /**\r
+ Get a Null for this type.\r
+ */\r
+ public DataValueDescriptor getNull() throws StandardException {\r
+ DataValueDescriptor returnDVD = typeId.getNull();\r
+ //If we are dealing with default collation, then we have got the\r
+ //right DVD already. Just return it.\r
+ if (typeDescriptor.getCollationType() == StringDataValue.COLLATION_TYPE_UCS_BASIC)\r
+ return returnDVD; \r
+ //If we are dealing with territory based collation and returnDVD is \r
+ //of type StringDataValue, then we need to return a StringDataValue \r
+ //with territory based collation.\r
+ if (returnDVD instanceof StringDataValue) {\r
+ try {\r
+ RuleBasedCollator rbs = ConnectionUtil.getCurrentLCC().getDataValueFactory().\r
+ getCharacterCollator(typeDescriptor.getCollationType());\r
+ return ((StringDataValue)returnDVD).getValue(rbs);\r
+ }\r
+ catch( java.sql.SQLException sqle)\r
+ {\r
+ throw StandardException.plainWrapException( sqle);\r
+ }\r
+ }\r
+ else\r
+ return returnDVD; \r
+ }\r
+\r
+ /**\r
+ * Gets the name of this datatype.\r
+ * \r
+ *\r
+ * @return the name of this datatype\r
+ */\r
+ public String getTypeName()\r
+ {\r
+ return typeId.getSQLTypeName();\r
+ }\r
+\r
+ /**\r
+ * Get the jdbc type id for this type. JDBC type can be\r
+ * found in java.sql.Types. \r
+ *\r
+ * @return a jdbc type, e.g. java.sql.Types.DECIMAL \r
+ *\r
+ * @see Types\r
+ */\r
+ public int getJDBCTypeId()\r
+ {\r
+ return typeId.getJDBCTypeId();\r
+ }\r
+\r
+ /**\r
+ * Returns the number of decimal digits for the datatype, if applicable.\r
+ *\r
+ * @return The number of decimal digits for the datatype. Returns\r
+ * zero for non-numeric datatypes.\r
+ */\r
+ public int getPrecision()\r
+ {\r
+ return typeDescriptor.getPrecision();\r
+ }\r
+\r
+ /**\r
+ * Returns the number of digits to the right of the decimal for\r
+ * the datatype, if applicable.\r
+ *\r
+ * @return The number of digits to the right of the decimal for\r
+ * the datatype. Returns zero for non-numeric datatypes.\r
+ */\r
+ public int getScale()\r
+ {\r
+ return typeDescriptor.getScale();\r
+ }\r
+\r
+ /** @see TypeDescriptor#getCollationType() */\r
+ public int getCollationType()\r
+ {\r
+ return typeDescriptor.getCollationType();\r
+ }\r
+\r
+ /**\r
+ * Gets the name of this datatype.\r
+ * <p>\r
+ * Used to generate strings decribing collation type for error messages.\r
+ * \r
+ *\r
+ * @return the name of the collation being used in this type.\r
+ */\r
+ public String getCollationName()\r
+ {\r
+ return(typeDescriptor.getCollationName());\r
+ }\r
+\r
+ /**\r
+ * Set the collation type of this TypeDescriptor\r
+ * @param collationTypeValue This will be COLLATION_TYPE_UCS_BASIC\r
+ * or COLLATION_TYPE_TERRITORY_BASED\r
+ * \r
+ * @see StringDataValue#COLLATION_TYPE_UCS_BASIC\r
+ * @see StringDataValue#COLLATION_TYPE_TERRITORY_BASED\r
+ */\r
+ public void setCollationType(int collationTypeValue)\r
+ {\r
+ typeDescriptor.setCollationType(collationTypeValue);\r
+ }\r
+\r
+ /** @see TypeDescriptor#getCollationDerivation() */\r
+ public int getCollationDerivation()\r
+ {\r
+ return typeDescriptor.getCollationDerivation();\r
+ }\r
+\r
+ /**\r
+ * Set the collation derivation of this DTD\r
+ * @param collationDerivationValue This will be \r
+ * COLLATION_DERIVATION_NONE/COLLATION_DERIVATION_IMPLICIT/COLLATION_DERIVATION_EXPLICIT\r
+ * In Derby 10.3, we do not expect to get value COLLATION_DERIVATION_EXPLICIT.\r
+ * \r
+ * @see StringDataValue#COLLATION_DERIVATION_NONE\r
+ * @see StringDataValue#COLLATION_DERIVATION_IMPLICIT\r
+ * @see StringDataValue#COLLATION_DERIVATION_EXPLICIT\r
+\r
+ */\r
+ public void setCollationDerivation(int collationDerivationValue)\r
+ {\r
+ typeDescriptor.setCollationDerivation(collationDerivationValue);\r
+ }\r
+\r
+ /**\r
+ * Returns TRUE if the datatype can contain NULL, FALSE if not.\r
+ * JDBC supports a return value meaning "nullability unknown" -\r
+ * I assume we will never have columns where the nullability is unknown.\r
+ *\r
+ * @return TRUE if the datatype can contain NULL, FALSE if not.\r
+ */\r
+ public boolean isNullable()\r
+ {\r
+ return typeDescriptor.isNullable();\r
+ }\r
+\r
+ /**\r
+ * Set the nullability of the datatype described by this descriptor\r
+ *\r
+ * @param nullable TRUE means set nullability to TRUE, FALSE\r
+ * means set it to FALSE\r
+ */\r
+ public void setNullability(boolean nullable)\r
+ {\r
+ typeDescriptor.setNullability(nullable);\r
+ }\r
+ \r
+ /**\r
+ * Return a type descriptor identical to the this type\r
+ * with the exception of its nullability. If the nullablity\r
+ * required matches the nullability of this then this is returned.\r
+ * \r
+ * @param isNullable True to return a nullable type, false otherwise.\r
+ */\r
+ public DataTypeDescriptor getNullabilityType(boolean isNullable)\r
+ {\r
+ if (isNullable() == isNullable)\r
+ return this;\r
+ \r
+ return new DataTypeDescriptor(this, isNullable);\r
+ }\r
+\r
+ /**\r
+ Compare if two TypeDescriptors are exactly the same\r
+ @param aTypeDescriptor the typeDescriptor to compare to.\r
+ */\r
+ public boolean equals(Object aTypeDescriptor)\r
+ {\r
+ return typeDescriptor.equals(aTypeDescriptor);\r
+ }\r
+\r
+ /**\r
+ * Check if this type is comparable with the passed type.\r
+ * \r
+ * @param compareWithDTD the type of the instance to compare with this type.\r
+ * @param forEquals True if this is an = or <> comparison, false\r
+ * otherwise.\r
+ * @param cf A ClassFactory\r
+ * @return true if compareWithDTD is comparable to this type, else false.\r
+ */\r
+ public boolean comparable(DataTypeDescriptor compareWithDTD,\r
+ boolean forEquals,\r
+ ClassFactory cf){\r
+\r
+ TypeId compareWithTypeID = compareWithDTD.getTypeId();\r
+ int compareWithJDBCTypeId = compareWithTypeID.getJDBCTypeId();\r
+\r
+ // Long types cannot be compared. \r
+ // XML types also fall in this window\r
+ // Says SQL/XML[2003] spec:\r
+ // 4.2.2 XML comparison and assignment\r
+ // "XML values are not comparable."\r
+ // An XML value cannot be compared to any type--\r
+ // not even to other XML values.\r
+ if (compareWithTypeID.isLongConcatableTypeId() || typeId.isLongConcatableTypeId())\r
+ return false;\r
+\r
+ // Ref types cannot be compared\r
+ if (typeId.isRefTypeId() || compareWithTypeID.isRefTypeId())\r
+ return false;\r
+ \r
+ //If this DTD is not user defined type but the DTD to be compared with \r
+ //is user defined type, then let the other DTD decide what should be the\r
+ //outcome of the comparable method.\r
+ if (!(typeId.isUserDefinedTypeId()) && \r
+ (compareWithTypeID.isUserDefinedTypeId()))\r
+ return compareWithDTD.comparable(this, forEquals, cf);\r
+\r
+ //Numeric types are comparable to numeric types, boolean types and to \r
+ //comparable user types\r
+ if (typeId.isNumericTypeId())\r
+ return (compareWithTypeID.isNumericTypeId() || \r
+ compareWithTypeID.isBooleanTypeId());\r
+\r
+ //CHAR, VARCHAR and LONGVARCHAR are comparable to strings, boolean, \r
+ //DATE/TIME/TIMESTAMP and to comparable user types\r
+ if (typeId.isStringTypeId()) {\r
+ if((compareWithTypeID.isDateTimeTimeStampTypeID() ||\r
+ compareWithTypeID.isBooleanTypeId()))\r
+ return true;\r
+ //If both the types are string types, then we need to make sure\r
+ //they have the same collation set on them\r
+ if (compareWithTypeID.isStringTypeId() && typeId.isStringTypeId()) {\r
+ return compareCollationInfo(compareWithDTD); \r
+ } else\r
+ return false;//can't be compared \r
+ }\r
+\r
+ //Are comparable to other bit types and comparable user types\r
+ if (typeId.isBitTypeId()) \r
+ return (compareWithTypeID.isBitTypeId()); \r
+ \r
+ //Booleans are comparable to Boolean, string, numeric and to \r
+ //comparable user types \r
+ if (typeId.isBooleanTypeId())\r
+ return (compareWithTypeID.getSQLTypeName().equals(typeId.getSQLTypeName()) ||\r
+ compareWithTypeID.isStringTypeId() ||\r
+ compareWithTypeID.isNumericTypeId()); \r
+\r
+ //Dates are comparable to dates, strings and to comparable\r
+ //user types.\r
+ if (typeId.getJDBCTypeId() == Types.DATE)\r
+ if (compareWithJDBCTypeId == Types.DATE || \r
+ compareWithTypeID.isStringTypeId())\r
+ return true;\r
+ else\r
+ return false;\r
+\r
+ //Times are comparable to times, strings and to comparable\r
+ //user types.\r
+ if (typeId.getJDBCTypeId() == Types.TIME)\r
+ if (compareWithJDBCTypeId == Types.TIME || \r
+ compareWithTypeID.isStringTypeId())\r
+ return true;\r
+ else\r
+ return false;\r
+\r
+ //Timestamps are comparable to timestamps, strings and to\r
+ //comparable user types.\r
+ if (typeId.getJDBCTypeId() == Types.TIMESTAMP)\r
+ if (compareWithJDBCTypeId == Types.TIMESTAMP || \r
+ compareWithTypeID.isStringTypeId())\r
+ return true;\r
+ else\r
+ return false;\r
+\r
+ //User types are comparable to other user types only if\r
+ //(for now) they are the same type and are being used to\r
+ //implement some JDBC type. This is sufficient for\r
+ //date/time types; it may be generalized later for e.g.\r
+ //comparison of any user type with one of its subtypes.\r
+ if (typeId.isUserDefinedTypeId() || typeId.getJDBCTypeId() == Types.OTHER) {\r
+ if (forEquals)\r
+ return true;\r
+ try {\r
+ \r
+ Class thisClass = cf.getClassInspector().getClass(\r
+ typeId.getCorrespondingJavaTypeName());\r
+ \r
+ return java.lang.Comparable.class.isAssignableFrom(thisClass);\r
+ } catch (ClassNotFoundException cnfe) {\r
+ return false;\r
+ } \r
+ }\r
+\r
+ return false;\r
+ }\r
+ \r
+ /**\r
+ * Compare the collation info on this DTD with the passed DTD. The rules\r
+ * for comparison are as follows (these are as per SQL standard 2003 \r
+ * Section 9.13)\r
+ * \r
+ * 1)If both the DTDs have collation derivation of NONE, then they can't be\r
+ * compared and we return false.\r
+ * 2)If both the DTDs have same collation derivation (which in Derby's case\r
+ * at this point will mean collation derivation of IMPLICIT), then check\r
+ * the collation types. If they match, then return true. If they do not \r
+ * match, then they can't be compared and hence return false.\r
+ * 3)If one DTD has collation derivation of IMPLICIT and other DTD has\r
+ * collation derivation of NONE, then 2 DTDs are comparable using the\r
+ * collation type of DTD with collation derivation of IMPLICIT. Derby does\r
+ * not implement this rule currently and it is being traked as DERBY-2678.\r
+ * Derby's current behavior is to throw an exception if both the DTDs \r
+ * involved in collation operation do not have collation derivation of \r
+ * IMPLICIT. This behavior is a subset of SQL standard.\r
+ * 4)Derby currently does not support collation derivation of EXPLICIT and\r
+ * hence we do not have the code to enforce rules as mentioned in Section\r
+ * 9.13 of SQL spec for collation derivation of EXPLICIT. When we implement\r
+ * collation derivation of EXPLICIT, we should make sure that we follow the\r
+ * rules as specified in the SQL spec for comparability.\r
+ * \r
+ * @param compareWithDTD compare this DTD's collation info\r
+ * \r
+ * @return value depends on the algorithm above.\r
+ */\r
+ public boolean compareCollationInfo(DataTypeDescriptor compareWithDTD){\r
+ //both the operands can not have the collation derivation of\r
+ //NONE. This is because in that case, we do not know what kind\r
+ //of collation to use for comparison.\r
+ if (getCollationDerivation() == compareWithDTD.getCollationDerivation() &&\r
+ getCollationDerivation() == StringDataValue.COLLATION_DERIVATION_NONE)\r
+ return false;\r
+ if (getCollationDerivation() == compareWithDTD.getCollationDerivation() &&\r
+ getCollationType() == compareWithDTD.getCollationType())\r
+ return true;//collation matches\r
+ else\r
+ return false;//collation does not match\r
+ }\r
+ \r
+ /**\r
+ * Converts this data type descriptor (including length/precision)\r
+ * to a string. E.g.\r
+ *\r
+ * VARCHAR(30)\r
+ *\r
+ * or\r
+ *\r
+ * java.util.Hashtable \r
+ *\r
+ * @return String version of datatype, suitable for running through\r
+ * the Parser.\r
+ */\r
+ public String getSQLstring()\r
+ {\r
+ return typeId.toParsableString( this );\r
+ }\r
+\r
+ /**\r
+ * Get the simplified type descriptor that is intended to be stored\r
+ * in the system tables.\r
+ */\r
+ public TypeDescriptorImpl getCatalogType()\r
+ {\r
+ return typeDescriptor;\r
+ }\r
+\r
+ /**\r
+ * Get the estimated memory usage for this type descriptor.\r
+ */\r
+ public double estimatedMemoryUsage() {\r
+ switch (typeId.getTypeFormatId())\r
+ {\r
+ case StoredFormatIds.LONGVARBIT_TYPE_ID:\r
+ /* Who knows? Let's just use some big number */\r
+ return 10000.0;\r
+\r
+ case StoredFormatIds.BIT_TYPE_ID:\r
+ return (double) ( ( ((float) getMaximumWidth()) / 8.0) + 0.5);\r
+\r
+ case StoredFormatIds.BOOLEAN_TYPE_ID:\r
+ return 4.0;\r
+\r
+ case StoredFormatIds.CHAR_TYPE_ID:\r
+ case StoredFormatIds.VARCHAR_TYPE_ID:\r
+ return (double) (2.0 * getMaximumWidth());\r
+\r
+ case StoredFormatIds.LONGVARCHAR_TYPE_ID:\r
+ /* Who knows? Let's just use some big number */\r
+ return 10000.0;\r
+\r
+ case StoredFormatIds.DECIMAL_TYPE_ID:\r
+ /*\r
+ ** 0.415 converts from number decimal digits to number of 8-bit digits. \r
+ ** Add 1.0 for the sign byte, and 0.5 to force it to round up.\r
+ */\r
+ return (double) ( (getPrecision() * 0.415) + 1.5 );\r
+\r
+ case StoredFormatIds.DOUBLE_TYPE_ID:\r
+ return 8.0;\r
+\r
+ case StoredFormatIds.INT_TYPE_ID:\r
+ return 4.0;\r
+\r
+ case StoredFormatIds.LONGINT_TYPE_ID:\r
+ return 8.0;\r
+\r
+ case StoredFormatIds.REAL_TYPE_ID:\r
+ return 4.0;\r
+\r
+ case StoredFormatIds.SMALLINT_TYPE_ID:\r
+ return 2.0;\r
+\r
+ case StoredFormatIds.TINYINT_TYPE_ID:\r
+ return 1.0;\r
+\r
+ case StoredFormatIds.REF_TYPE_ID:\r
+ /* I think 12 is the right number */\r
+ return 12.0;\r
+\r
+ case StoredFormatIds.USERDEFINED_TYPE_ID_V3:\r
+ if (typeId.userType()) {\r
+ /* Who knows? Let's just use some medium-sized number */\r
+ return 256.0;\r
+ }\r
+ case StoredFormatIds.DATE_TYPE_ID:\r
+ case StoredFormatIds.TIME_TYPE_ID:\r
+ case StoredFormatIds.TIMESTAMP_TYPE_ID:\r
+ return 12.0; \r
+\r
+ default:\r
+ return 0.0;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Compare JdbcTypeIds to determine if they represent equivalent\r
+ * SQL types. For example Types.NUMERIC and Types.DECIMAL are\r
+ * equivalent\r
+ *\r
+ * @param existingType JDBC type id of Derby data type\r
+ * @param jdbcTypeId JDBC type id passed in from application.\r
+ *\r
+ * @return boolean true if types are equivalent, false if not\r
+ */\r
+\r
+ public static boolean isJDBCTypeEquivalent(int existingType, int jdbcTypeId)\r
+ {\r
+ // Any type matches itself.\r
+ if (existingType == jdbcTypeId)\r
+ return true;\r
+\r
+ // To a numeric type\r
+ if (DataTypeDescriptor.isNumericType(existingType)) {\r
+ if (DataTypeDescriptor.isNumericType(jdbcTypeId))\r
+ return true;\r
+\r
+ if (DataTypeDescriptor.isCharacterType(jdbcTypeId))\r
+ return true;\r
+\r
+ return false;\r
+ }\r
+\r
+ // To character type.\r
+ if (DataTypeDescriptor.isCharacterType(existingType)) {\r
+\r
+ if (DataTypeDescriptor.isCharacterType(jdbcTypeId))\r
+ return true;\r
+\r
+ if (DataTypeDescriptor.isNumericType(jdbcTypeId))\r
+ return true;\r
+\r
+\r
+ switch (jdbcTypeId) {\r
+ case Types.DATE:\r
+ case Types.TIME:\r
+ case Types.TIMESTAMP:\r
+ return true;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ \r
+ return false;\r
+\r
+ }\r
+\r
+ // To binary type\r
+ if (DataTypeDescriptor.isBinaryType(existingType)) {\r
+\r
+ if (DataTypeDescriptor.isBinaryType(jdbcTypeId))\r
+ return true;\r
+\r
+ return false;\r
+ }\r
+\r
+ // To DATE, TIME\r
+ if (existingType == Types.DATE || existingType == Types.TIME) {\r
+ if (DataTypeDescriptor.isCharacterType(jdbcTypeId))\r
+ return true;\r
+\r
+ if (jdbcTypeId == Types.TIMESTAMP)\r
+ return true;\r
+\r
+ return false;\r
+ }\r
+\r
+ // To TIMESTAMP\r
+ if (existingType == Types.TIMESTAMP) {\r
+ if (DataTypeDescriptor.isCharacterType(jdbcTypeId))\r
+ return true;\r
+\r
+ if (jdbcTypeId == Types.DATE)\r
+ return true;\r
+\r
+ return false;\r
+ }\r
+ \r
+ // To CLOB\r
+ if (existingType == Types.CLOB && DataTypeDescriptor.isCharacterType(jdbcTypeId))\r
+ return true;\r
+\r
+ return false;\r
+ }\r
+\r
+ public static boolean isNumericType(int jdbcType) {\r
+\r
+ switch (jdbcType) {\r
+ case Types.BIT:\r
+ case org.apache.derby.iapi.reference.JDBC30Translation.SQL_TYPES_BOOLEAN:\r
+ case Types.TINYINT:\r
+ case Types.SMALLINT:\r
+ case Types.INTEGER:\r
+ case Types.BIGINT:\r
+ case Types.REAL:\r
+ case Types.FLOAT:\r
+ case Types.DOUBLE:\r
+ case Types.DECIMAL:\r
+ case Types.NUMERIC:\r
+ return true;\r
+ default:\r
+ return false;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Check whether a JDBC type is one of the character types that are\r
+ * compatible with the Java type <code>String</code>.\r
+ *\r
+ * <p><strong>Note:</strong> <code>CLOB</code> is not compatible with\r
+ * <code>String</code>. See tables B-4, B-5 and B-6 in the JDBC 3.0\r
+ * Specification.\r
+ *\r
+ * <p> There are some non-character types that are compatible with\r
+ * <code>String</code> (examples: numeric types, binary types and\r
+ * time-related types), but they are not covered by this method.\r
+ *\r
+ * @param jdbcType a JDBC type\r
+ * @return <code>true</code> iff <code>jdbcType</code> is a character type\r
+ * and compatible with <code>String</code>\r
+ * @see java.sql.Types\r
+ */\r
+ private static boolean isCharacterType(int jdbcType) {\r
+\r
+ switch (jdbcType) {\r
+ case Types.CHAR:\r
+ case Types.VARCHAR:\r
+ case Types.LONGVARCHAR:\r
+ return true;\r
+ default:\r
+ return false;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Check whether a JDBC type is compatible with the Java type\r
+ * <code>byte[]</code>.\r
+ *\r
+ * <p><strong>Note:</strong> <code>BLOB</code> is not compatible with\r
+ * <code>byte[]</code>. See tables B-4, B-5 and B-6 in the JDBC 3.0\r
+ * Specification.\r
+ *\r
+ * @param jdbcType a JDBC type\r
+ * @return <code>true</code> iff <code>jdbcType</code> is compatible with\r
+ * <code>byte[]</code>\r
+ * @see java.sql.Types\r
+ */\r
+ private static boolean isBinaryType(int jdbcType) {\r
+ switch (jdbcType) {\r
+ case Types.BINARY:\r
+ case Types.VARBINARY:\r
+ case Types.LONGVARBINARY:\r
+ return true;\r
+ default:\r
+ return false;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Determine if an ASCII stream can be inserted into a column or parameter\r
+ * of type <code>jdbcType</code>.\r
+ *\r
+ * @param jdbcType JDBC type of column or parameter\r
+ * @return <code>true</code> if an ASCII stream can be inserted;\r
+ * <code>false</code> otherwise\r
+ */\r
+ public static boolean isAsciiStreamAssignable(int jdbcType) {\r
+ return jdbcType == Types.CLOB || isCharacterType(jdbcType);\r
+ }\r
+\r
+ /**\r
+ * Determine if a binary stream can be inserted into a column or parameter\r
+ * of type <code>jdbcType</code>.\r
+ *\r
+ * @param jdbcType JDBC type of column or parameter\r
+ * @return <code>true</code> if a binary stream can be inserted;\r
+ * <code>false</code> otherwise\r
+ */\r
+ public static boolean isBinaryStreamAssignable(int jdbcType) {\r
+ return jdbcType == Types.BLOB || isBinaryType(jdbcType);\r
+ }\r
+\r
+ /**\r
+ * Determine if a character stream can be inserted into a column or\r
+ * parameter of type <code>jdbcType</code>.\r
+ *\r
+ * @param jdbcType JDBC type of column or parameter\r
+ * @return <code>true</code> if a character stream can be inserted;\r
+ * <code>false</code> otherwise\r
+ */\r
+ public static boolean isCharacterStreamAssignable(int jdbcType) {\r
+ // currently, we support the same types for ASCII streams and\r
+ // character streams\r
+ return isAsciiStreamAssignable(jdbcType);\r
+ }\r
+\r
+ public String toString()\r
+ {\r
+ return typeDescriptor.toString();\r
+ }\r
+\r
+ // Formatable methods\r
+\r
+ /**\r
+ * Read this object from a stream of stored objects.\r
+ *\r
+ * @param in read this.\r
+ *\r
+ * @exception IOException thrown on error\r
+ * @exception ClassNotFoundException thrown on error\r
+ */\r
+ public void readExternal( ObjectInput in )\r
+ throws IOException, ClassNotFoundException\r
+ {\r
+ /* NOTE: We only write out the generic type id.\r
+ * typeId will be reset to be the generic type id\r
+ * when we get read back in since the generic\r
+ * one is all that is needed at execution time.\r
+ */\r
+ typeId = (TypeId) in.readObject();\r
+ typeDescriptor = (TypeDescriptorImpl) in.readObject();\r
+ }\r
+\r
+ /**\r
+ * Write this object to a stream of stored objects.\r
+ *\r
+ * @param out write bytes here.\r
+ *\r
+ * @exception IOException thrown on error\r
+ */\r
+ public void writeExternal( ObjectOutput out )\r
+ throws IOException\r
+ {\r
+ out.writeObject( typeId );\r
+ out.writeObject( typeDescriptor );\r
+ }\r
+ \r
+ /**\r
+ * Get the formatID which corresponds to this class.\r
+ *\r
+ * @return the formatID of this class\r
+ */\r
+ public int getTypeFormatId() { return StoredFormatIds.DATA_TYPE_SERVICES_IMPL_V01_ID; }\r
+\r
+ /**\r
+ * Check to make sure that this type id is something a user can create\r
+ * him/herself directly through an SQL CREATE TABLE statement.\r
+ * \r
+ * This method is used for CREATE TABLE AS ... WITH [NO] DATA binding\r
+ * because it's possible for the query to return types which are not\r
+ * actually creatable for a user. DERBY-2605.\r
+ *\r
+ * Three examples are:\r
+ *\r
+ * BOOLEAN: A user can select boolean columns from system tables, but\r
+ * s/he is not allowed to create such a column him/herself.\r
+ *\r
+ * JAVA_OBJECT: A user can select columns of various java object types\r
+ * from system tables, but s/he is not allowed to create such a column\r
+ * him/herself.\r
+ * \r
+ * DECIMAL: A user can specify a VALUES clause with a constant that\r
+ * has a precision of greater than 31. Derby can apparently handle\r
+ * such a value internally, but the user is not supposed to be able\r
+ * create such a column him/herself.\r
+ * \r
+ * @return True if the type associated with this DTD can be created via\r
+ * the CREATE TABLE syntax; false otherwise.\r
+ */\r
+ public boolean isUserCreatableType() throws StandardException\r
+ {\r
+ switch (typeId.getJDBCTypeId())\r
+ {\r
+ case Types.BOOLEAN:\r
+ case Types.JAVA_OBJECT:\r
+ return false;\r
+ case Types.DECIMAL:\r
+ return\r
+ (getPrecision() <= typeId.getMaximumPrecision()) &&\r
+ (getScale() <= typeId.getMaximumScale()) &&\r
+ (getMaximumWidth() <= typeId.getMaximumMaximumWidth());\r
+ default: break;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Return the SQL type name and, if applicable, scale/precision/length\r
+ * for this DataTypeDescriptor. Note that we want the values from *this*\r
+ * object specifically, not the max values defined on this.typeId.\r
+ */\r
+ public String getFullSQLTypeName()\r
+ {\r
+ StringBuffer sbuf = new StringBuffer(typeId.getSQLTypeName());\r
+ if (typeId.isDecimalTypeId() || typeId.isNumericTypeId())\r
+ {\r
+ sbuf.append("(");\r
+ sbuf.append(getPrecision());\r
+ sbuf.append(", ");\r
+ sbuf.append(getScale());\r
+ sbuf.append(")");\r
+ }\r
+ else if (typeId.variableLength())\r
+ {\r
+ sbuf.append("(");\r
+ sbuf.append(getMaximumWidth());\r
+ sbuf.append(")");\r
+ }\r
+\r
+ return sbuf.toString();\r
+ }\r
+\r
+ /* Return the typename with the collation name for \r
+ * String types.\r
+ */\r
+ public String getSQLTypeNameWithCollation() {\r
+ String name = typeId.getSQLTypeName();\r
+ if (typeId.isStringTypeId()) {\r
+ name = name + " (" + getCollationName() + ")";\r
+ }\r
+ return name; \r
+ }\r
+}\r
+\r