--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.catalog.types.TypeDescriptorImpl\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.catalog.types;\r
+\r
+import org.apache.derby.iapi.services.io.StoredFormatIds;\r
+import org.apache.derby.iapi.services.io.Formatable;\r
+\r
+import org.apache.derby.catalog.TypeDescriptor;\r
+\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.StringDataValue;\r
+import org.apache.derby.iapi.reference.Property;\r
+\r
+import java.io.ObjectOutput;\r
+import java.io.ObjectInput;\r
+import java.io.IOException;\r
+import java.sql.Types;\r
+ \r
+public class TypeDescriptorImpl 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
+ private BaseTypeIdImpl typeId;\r
+ private int precision;\r
+ private int scale;\r
+ private boolean isNullable;\r
+ private int maximumWidth;\r
+ /** @see TypeDescriptor#getCollationType() */\r
+ private int collationType = StringDataValue.COLLATION_TYPE_UCS_BASIC;\r
+ /** @see TypeDescriptor#getCollationDerivation() */\r
+ private int collationDerivation = StringDataValue.COLLATION_DERIVATION_IMPLICIT;\r
+\r
+ /**\r
+ * Public niladic constructor. Needed for Formatable interface to work.\r
+ *\r
+ */\r
+ public TypeDescriptorImpl() {}\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 TypeDescriptorImpl(\r
+ BaseTypeIdImpl typeId,\r
+ int precision,\r
+ int scale,\r
+ boolean isNullable,\r
+ int maximumWidth)\r
+ {\r
+ this.typeId = typeId;\r
+ this.precision = precision;\r
+ this.scale = scale;\r
+ this.isNullable = isNullable;\r
+ this.maximumWidth = 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 TypeDescriptorImpl(\r
+ BaseTypeIdImpl typeId,\r
+ int precision,\r
+ int scale,\r
+ boolean isNullable,\r
+ int maximumWidth,\r
+ int collationType,\r
+ int collationDerivation)\r
+ {\r
+ this.typeId = typeId;\r
+ this.precision = precision;\r
+ this.scale = scale;\r
+ this.isNullable = isNullable;\r
+ this.maximumWidth = maximumWidth;\r
+ this.collationType = collationType;\r
+ this.collationDerivation = 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 TypeDescriptorImpl(\r
+ BaseTypeIdImpl typeId,\r
+ boolean isNullable,\r
+ int maximumWidth)\r
+ {\r
+ this.typeId = typeId;\r
+ this.isNullable = isNullable;\r
+ this.maximumWidth = maximumWidth;\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 TypeDescriptorImpl(\r
+ TypeDescriptorImpl source, \r
+ int precision,\r
+ int scale,\r
+ boolean isNullable,\r
+ int maximumWidth)\r
+ {\r
+ this.typeId = source.typeId;\r
+ this.precision = precision;\r
+ this.scale = scale;\r
+ this.isNullable = isNullable;\r
+ this.maximumWidth = maximumWidth;\r
+ }\r
+\r
+ public TypeDescriptorImpl(\r
+ TypeDescriptorImpl source, \r
+ int precision,\r
+ int scale,\r
+ boolean isNullable,\r
+ int maximumWidth,\r
+ int collationType,\r
+ int collationDerivation)\r
+ {\r
+ this.typeId = source.typeId;\r
+ this.precision = precision;\r
+ this.scale = scale;\r
+ this.isNullable = isNullable;\r
+ this.maximumWidth = maximumWidth;\r
+ this.collationType = collationType;\r
+ this.collationDerivation = collationDerivation;\r
+ }\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 TypeDescriptorImpl(\r
+ TypeDescriptorImpl source,\r
+ boolean isNullable,\r
+ int maximumWidth)\r
+ {\r
+ this.typeId = source.typeId;\r
+ this.precision = source.precision;\r
+ this.scale = source.scale;\r
+ this.isNullable = isNullable;\r
+ this.maximumWidth = maximumWidth;\r
+ }\r
+\r
+ /**\r
+ * @see TypeDescriptor#getMaximumWidth\r
+ */\r
+ public int getMaximumWidth()\r
+ {\r
+ return maximumWidth;\r
+ }\r
+\r
+ /**\r
+ * Return the length of this type in bytes. Note that\r
+ * while the JDBC API _does_ define a need for\r
+ * returning length in bytes of a type, it doesn't\r
+ * state clearly what that means for the various\r
+ * types. We assume therefore that the values here\r
+ * are meant to match those specified by the ODBC\r
+ * specification (esp. since ODBC clients are more\r
+ * likely to need this value than a Java client).\r
+ * The ODBC spec that defines the values we use here\r
+ * can be found at the following link:\r
+ * \r
+ * http://msdn.microsoft.com/library/default.asp?url=/library/\r
+ * en-us/odbc/htm/odbctransfer_octet_length.asp\r
+ *\r
+ * @see TypeDescriptor#getMaximumWidthInBytes\r
+ */\r
+ public int getMaximumWidthInBytes()\r
+ {\r
+ switch (typeId.getJDBCTypeId()) {\r
+\r
+ case Types.BIT:\r
+ case Types.TINYINT:\r
+ case Types.SMALLINT:\r
+ case Types.INTEGER:\r
+ case Types.REAL:\r
+ case Types.DOUBLE:\r
+ case Types.FLOAT:\r
+ case Types.BINARY:\r
+ case Types.VARBINARY:\r
+ case Types.LONGVARBINARY:\r
+ case Types.BLOB:\r
+\r
+ // For all of these, just take the maximumWidth,\r
+ // since that already holds the length in bytes.\r
+ return maximumWidth;\r
+\r
+ // For BIGINT values, ODBC spec says to return\r
+ // 40 because max length of a C/C++ BIGINT in\r
+ // string form is 20 and we assume the client\r
+ // character set is Unicode (spec says to\r
+ // multiply by 2 for unicode).\r
+ case Types.BIGINT:\r
+ return 40;\r
+\r
+ // ODBC spec explicitly declares what the lengths\r
+ // should be for datetime values, based on the\r
+ // declared fields of SQL_DATE_STRUCT, SQL_TIME_STRUCT,\r
+ // and SQL_TIMESTAMP_STRUCT. So we just use those\r
+ // values.\r
+ case Types.DATE:\r
+ case Types.TIME:\r
+ return 6;\r
+\r
+ case Types.TIMESTAMP:\r
+ return 16;\r
+\r
+ // ODBC spec says that for numeric/decimal values,\r
+ // we should use max number of digits plus 2\r
+ // (for sign and decimal point), since that's\r
+ // the length of a decimal value in string form.\r
+ // And since we assume client character set\r
+ // is unicode, we have to multiply by 2 to\r
+ // get the number of bytes.\r
+ case Types.NUMERIC:\r
+ case Types.DECIMAL:\r
+ return 2 * (precision + 2);\r
+\r
+ // ODBC spec says to use length in chars\r
+ // for character types, times two if we\r
+ // assume client character set is unicode.\r
+ // If 2 * character length is greater than\r
+ // variable type (in this case, integer),\r
+ // then we return the max value for an\r
+ // integer.\r
+ case Types.CHAR:\r
+ case Types.VARCHAR:\r
+ case Types.LONGVARCHAR:\r
+ case Types.CLOB:\r
+ if ((maximumWidth > 0) && (2 * maximumWidth < 0))\r
+ // integer overflow; return max integer possible.\r
+ return Integer.MAX_VALUE;\r
+ else\r
+ return 2 * maximumWidth;\r
+\r
+ case Types.ARRAY:\r
+ case Types.DISTINCT:\r
+ case Types.NULL:\r
+ case Types.OTHER:\r
+ case Types.REF:\r
+ case Types.STRUCT:\r
+ case Types.JAVA_OBJECT:\r
+ default:\r
+\r
+ // For these we don't know, so return the "don't-know"\r
+ // indicator.\r
+ return -1;\r
+\r
+ }\r
+\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
+ * 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
+ * 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 precision;\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 scale;\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 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
+ isNullable = nullable;\r
+ }\r
+\r
+ /** @see TypeDescriptor#getCollationType() */\r
+ public int getCollationType()\r
+ {\r
+ return collationType;\r
+ }\r
+\r
+ /** @see DataTypeDescriptor#setCollationType(int) */\r
+ public void setCollationType(int collationTypeValue)\r
+ {\r
+ collationType = collationTypeValue;\r
+ }\r
+\r
+ /** @see TypeDescriptor#getCollationDerivation() */\r
+ public int getCollationDerivation()\r
+ {\r
+ return collationDerivation;\r
+ }\r
+\r
+ /** @see DataTypeDescriptor#setCollationDerivation(int) */\r
+ public void setCollationDerivation(int collationDerivationValue)\r
+ {\r
+ collationDerivation = collationDerivationValue;\r
+ }\r
+\r
+ /**\r
+ * Gets the name of the collation type in this descriptor if the collation\r
+ * derivation is not NONE. If the collation derivation is NONE, then this\r
+ * method will return "NONE".\r
+ * <p>\r
+ * This method is used for generating error messages which will use correct\r
+ * string describing collation type/derivation.\r
+ * \r
+ *\r
+ * @return the name of the collation being used in this type.\r
+ */\r
+ public String getCollationName()\r
+ {\r
+ return(\r
+ collationDerivation == StringDataValue.COLLATION_DERIVATION_NONE ?\r
+ Property.COLLATION_NONE :\r
+ collationType == StringDataValue.COLLATION_TYPE_UCS_BASIC ?\r
+ Property.UCS_BASIC_COLLATION :\r
+ Property.TERRITORY_BASED_COLLATION);\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
+ public String toString()\r
+ {\r
+ String s = getSQLstring();\r
+ if (!isNullable())\r
+ return s + " NOT NULL";\r
+ return s;\r
+ }\r
+\r
+ /**\r
+ * Get the type Id stored within this type descriptor.\r
+ */\r
+ public BaseTypeIdImpl getTypeId()\r
+ {\r
+ return typeId;\r
+ }\r
+\r
+ /**\r
+ Compare if two TypeDescriptors are exactly the same\r
+ @param object the dataTypeDescriptor to compare to.\r
+ */\r
+ public boolean equals(Object object)\r
+ {\r
+ TypeDescriptor typeDescriptor = (TypeDescriptor)object;\r
+\r
+ if(!this.getTypeName().equals(typeDescriptor.getTypeName()) ||\r
+ this.precision != typeDescriptor.getPrecision() ||\r
+ this.scale != typeDescriptor.getScale() ||\r
+ this.isNullable != typeDescriptor.isNullable() ||\r
+ this.maximumWidth != typeDescriptor.getMaximumWidth())\r
+ return false;\r
+ else\r
+ {\r
+ switch (typeId.getJDBCTypeId()) {\r
+ case Types.CHAR:\r
+ case Types.VARCHAR:\r
+ case Types.LONGVARCHAR:\r
+ case Types.CLOB:\r
+ //if we are dealing with character types, then we should \r
+ //also compare the collation information on them.\r
+ if(this.collationDerivation != typeDescriptor.getCollationDerivation() ||\r
+ this.collationType != typeDescriptor.getCollationType())\r
+ return false;\r
+ else\r
+ return true;\r
+ default:\r
+ //no collation checking required if we are dealing with \r
+ //non-char datatypes.\r
+ return true;\r
+ }\r
+ }\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
+ typeId = (BaseTypeIdImpl) in.readObject();\r
+ precision = in.readInt();\r
+ \r
+ //Scale does not apply to character data types. Starting 10.3 release,\r
+ //the scale field in TypeDescriptor in SYSCOLUMNS will be used to save\r
+ //the collation type of the character data types. Because of this, in\r
+ //this method, we check if we are dealing with character types. If yes,\r
+ //then read the on-disk scale field of TypeDescriptor into collation\r
+ //type. In other words, the on-disk scale field has 2 different \r
+ //meanings depending on what kind of data type we are dealing with.\r
+ //For character data types, it really represents the collation type of\r
+ //the character data type. For all the other data types, it represents\r
+ //the scale of that data type.\r
+ switch (typeId.getJDBCTypeId()) {\r
+ case Types.CHAR:\r
+ case Types.VARCHAR:\r
+ case Types.LONGVARCHAR:\r
+ case Types.CLOB:\r
+ scale = 0;\r
+ collationType = in.readInt();\r
+ //I am assuming that the readExternal gets called only on \r
+ //persistent columns. Since all persistent character string type\r
+ //columns always have the collation derivation of implicit, I will \r
+ //simply use that value for collation derivation here for character \r
+ //string type columns.\r
+ collationDerivation = StringDataValue.COLLATION_DERIVATION_IMPLICIT;\r
+ break;\r
+ default:\r
+ scale = in.readInt();\r
+ collationType = 0;\r
+ collationDerivation = StringDataValue.COLLATION_DERIVATION_IMPLICIT;\r
+ break;\r
+ }\r
+ \r
+ isNullable = in.readBoolean();\r
+ maximumWidth = in.readInt();\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.writeInt( precision );\r
+\r
+ //Scale does not apply to character data types. Starting 10.3 release,\r
+ //the scale field in TypeDescriptor in SYSCOLUMNS will be used to save\r
+ //the collation type of the character data types. Because of this, in\r
+ //this method, we check if we are dealing with character types. If yes,\r
+ //then write the collation type into the on-disk scale field of \r
+ //TypeDescriptor. But if we are dealing with non-character data types,\r
+ //then write the scale of that data type into the on-disk scale field\r
+ //of TypeDescriptor. In other words, the on-disk scale field has 2 \r
+ //different meanings depending on what kind of data type we are dealing \r
+ //with. For character data types, it really represents the collation \r
+ //type of the character data type. For all the other data types, it \r
+ //represents the scale of that data type.\r
+ switch (typeId.getJDBCTypeId()) {\r
+ case Types.CHAR:\r
+ case Types.VARCHAR:\r
+ case Types.LONGVARCHAR:\r
+ case Types.CLOB:\r
+ out.writeInt( collationType );\r
+ break;\r
+ default:\r
+ out.writeInt( scale );\r
+ break;\r
+ } \r
+ \r
+ out.writeBoolean( isNullable );\r
+ out.writeInt( maximumWidth );\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_IMPL_DESCRIPTOR_V01_ID; }\r
+}\r