blob: 443e316ffeeb0321d973ec85f19aeadcc4025f23 [file] [log] [blame]
/* Copyright (c) 2001-2010, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* $Id: PgType.java 3481 2010-02-26 18:05:06Z fredt $
*/
package org.hsqldb.server;
import org.hsqldb.types.Type;
import org.hsqldb.types.Types;
import org.hsqldb.types.NumberType;
import org.hsqldb.types.BooleanType;
import org.hsqldb.types.CharacterType;
import org.hsqldb.types.DateTimeType;
import org.hsqldb.Session;
import org.hsqldb.types.Types;
import java.sql.SQLException;
import java.io.Serializable;
import org.hsqldb.types.JavaObjectData;
import org.hsqldb.HsqlException;
import org.hsqldb.types.BinaryData;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.jdbc.Util;
public class PgType {
private int oid;
private int typeWidth = -1;
private int lpConstraint = -1; // Length or Precision
private Type hType;
public int getOid() {
return oid;
}
public int getTypeWidth() {
return typeWidth;
}
public int getLPConstraint() {
return lpConstraint;
}
/**
* Convenience wrapper for PgType constructor, when there is no
* type width, length, or precision setting for the type.
*
* @see #PgType(Type, int, Integer, Integer)
*/
protected PgType(Type hType, int oid) {
this(hType, oid, null, null);
}
/**
* Convenience wrapper for PgType constructor, when there is no
* length or precision setting for the type.
*
* @see #PgType(Type, int, Integer, Integer)
*/
protected PgType(Type hType, int oid, int typeWidth) {
this(hType, oid, new Integer(typeWidth), null);
}
/**
* Convenience wrapper for PgType constructor, when there is no fixed
* width for the type.
*
* @param dummy Normally pass null. This is a dummy parameter just to make
* a unique method signature. If non-null, will be treated
* exactly the same as the typeWidthObject from the 3-param
* constructor.
* @see #PgType(Type, int, Integer, Integer)
*/
protected PgType(Type hType, int oid, Integer dummy, long lpConstraint)
throws RecoverableOdbcFailure {
this(hType, oid, dummy, new Integer((int) lpConstraint));
if (lpConstraint < 0) {
throw new RecoverableOdbcFailure(
"Length/Precision value is below minimum value of 0");
}
if (lpConstraint > Integer.MAX_VALUE) {
throw new RecoverableOdbcFailure(
"Length/Precision value is above maximum value of "
+ Integer.MAX_VALUE);
}
}
/**
* @param hType HyperSQL data type
* @param oid Numeric Object ID for the driver-side type.
* @param typeWidthObject Fixed width for the type
* @param lpConstraintObject Either length or Precision setting for this
* instance of the type.
* <b>IMPORTANT!</b> for all types with positive
* lpConstraint other than Timestamps and Times,
* add an extra 4 to satisy crazy driver protocol.
*/
protected PgType(Type hType,
int oid, Integer typeWidthObject, Integer lpConstraintObject) {
this.hType = hType;
this.oid = oid;
this.typeWidth = (typeWidthObject == null)
? -1 : typeWidthObject.intValue();
this.lpConstraint = (lpConstraintObject == null)
? -1 : lpConstraintObject.intValue();
}
static public PgType getPgType(Type hType, boolean directColumn)
throws RecoverableOdbcFailure {
switch (hType.typeCode) {
case Types.TINYINT:
return tinyIntSingleton;
case Types.SQL_SMALLINT:
return int2singleton;
case Types.SQL_INTEGER:
return int4singleton;
case Types.SQL_BIGINT:
return int8singleton;
case Types.SQL_NUMERIC:
case Types.SQL_DECIMAL:
return new PgType(hType, TYPE_NUMERIC, null,
(hType.precision << 16) + hType.scale + 4);
case Types.SQL_FLOAT:
// TODO:
// Improve the driver to make use of the Float precision
// return new PgType(hType, TYPE_FLOAT8, null, hType.precision);
case Types.SQL_DOUBLE:
case Types.SQL_REAL:
return doubleSingleton;
case Types.BOOLEAN:
return boolSingleton;
case Types.SQL_CHAR: // = CHARACTER
if (directColumn) {
return new PgType(hType,
TYPE_BPCHAR, null, hType.precision + 4);
}
return unknownSingleton; // constant value
case Types.SQL_VARCHAR: // = CHARACTER VARYING = LONGVARCHAR
case Types.VARCHAR_IGNORECASE: // Don't know if possible here
if (hType.precision < 0) {
throw new RecoverableOdbcFailure (
"Length/Precision value is below minimum value of 0");
}
if (hType.precision > Integer.MAX_VALUE) {
throw new RecoverableOdbcFailure (
"Length/Precision value is above maximum value of "
+ Integer.MAX_VALUE);
}
return (hType.precision != 0 && directColumn)
? new PgType(hType, TYPE_VARCHAR, null, hType.precision + 4)
: textSingleton;
// Return TEXT type for both unlimited VARCHARs, and for
// Non-direct-table-col results.
case Types.SQL_CLOB: // = CHARACTER LARGE OBJECT
throw new RecoverableOdbcFailure (
"Driver doesn't support type 'CLOB' yet");
case Types.SQL_BLOB: // = BINARY LARGE OBJECT
return new PgType(hType, TYPE_BLOB, null, hType.precision);
case Types.SQL_BINARY:
case Types.SQL_VARBINARY: // = BINARY VARYING
return new PgType(hType, TYPE_BYTEA, null, hType.precision);
// Note that we are returning SQL_BINARY data as if they were
// variable. I don't think the unnecessary variability will
// have any side-effects.
// No reason to differentiate here, since the client's
// atttypm parameter is where we would communicate the length
// in both cases.
case Types.OTHER:
throw new RecoverableOdbcFailure (
"Driver doesn't support type 'OTHER' yet");
case Types.SQL_BIT:
return bitSingleton;
case Types.SQL_BIT_VARYING:
return bitVaryingSingleton;
// I have no idea why length contstaint spec is not needed for
// BIT_VARYING.
case Types.SQL_DATE:
return dateSingleton;
// 4 bytes
case Types.SQL_TIME :
return new PgType(hType, TYPE_TIME, new Integer(8),
hType.precision);
case Types.SQL_TIME_WITH_TIME_ZONE :
return new PgType(hType, TYPE_TIME_WITH_TMZONE,
new Integer(12), hType.precision);
case Types.SQL_TIMESTAMP :
return new PgType(hType, TYPE_TIMESTAMP_NO_TMZONE,
new Integer(8), hType.precision);
case Types.SQL_TIMESTAMP_WITH_TIME_ZONE :
return new PgType(hType, TYPE_TIMESTAMP, new Integer(8),
hType.precision);
// Postgresql is returning type DATETIME for this case.
// It should return TYPE_TIMESTAMP, no?
/* *********************************************************
* For INTERVALs, we get the more specific type here, not just
* SQL_INTERVAL.
case Types.SQL_INTERVAL:
*
* The reason no precisions are passed to the ODBC client is that I
* have so far been unsuccessful at figuring out exactly how the
* driver wants the atttypmod formatted. See doc/odbc.txt for
* notes about this.
*/
case Types.SQL_INTERVAL_YEAR:
case Types.SQL_INTERVAL_YEAR_TO_MONTH:
case Types.SQL_INTERVAL_MONTH:
// Need to test these, since the driver Interval type is
// intended for second-resolution only, not month resolution.
throw new RecoverableOdbcFailure (
"Driver doesn't support month-resolution 'INTERVAL's yet");
case Types.SQL_INTERVAL_DAY:
case Types.SQL_INTERVAL_DAY_TO_HOUR:
case Types.SQL_INTERVAL_DAY_TO_MINUTE:
case Types.SQL_INTERVAL_HOUR:
case Types.SQL_INTERVAL_HOUR_TO_MINUTE:
case Types.SQL_INTERVAL_MINUTE:
// Our server uses the type to distinguish the resolution here.
// The driver expects these types to be distinguished in the
// value itself, like "99 days".
// Therefore, these types are incompatible until driver is
// enhanced.
throw new RecoverableOdbcFailure (
"Driver doesn't support non-second-resolution 'INTERVAL's "
+ "yet");
case Types.SQL_INTERVAL_DAY_TO_SECOND:
PgType.ignoredConstraintWarning(hType);
return daySecIntervalSingleton;
case Types.SQL_INTERVAL_HOUR_TO_SECOND:
PgType.ignoredConstraintWarning(hType);
return hourSecIntervalSingleton;
case Types.SQL_INTERVAL_MINUTE_TO_SECOND:
PgType.ignoredConstraintWarning(hType);
return minSecIntervalSingleton;
case Types.SQL_INTERVAL_SECOND:
PgType.ignoredConstraintWarning(hType);
return secIntervalSingleton;
default:
throw new RecoverableOdbcFailure (
"Unsupported type: " + hType.getNameString());
}
}
/**
* This method copied from JDBCPreparedStatement.java.
*
* The internal parameter value setter always converts the parameter to
* the Java type required for data transmission.
* <P>
* This method will not be called for binary types. Binary values are
* just loaded directly into the Object parameter array.
* </P>
*
* @throws SQLException if either argument is not acceptable.
*/
public Object getParameter(String inString, Session session)
throws SQLException, RecoverableOdbcFailure {
if (inString == null) {
return null;
}
Object o = inString;
switch (hType.typeCode) {
case Types.SQL_BOOLEAN :
if (inString.length() == 1) switch (inString.charAt(0)) {
case 'T':
case 't':
case 'Y':
case 'y':
case '1':
return Boolean.TRUE;
default:
return Boolean.FALSE;
}
return Boolean.valueOf(inString);
case Types.SQL_BINARY :
case Types.SQL_VARBINARY :
case Types.SQL_BLOB :
throw new RecoverableOdbcFailure(
"This data type should be transmitted to server in binary "
+ "format: " + hType.getNameString());
case Types.OTHER :
case Types.SQL_CLOB :
throw new RecoverableOdbcFailure(
"Type not supported yet: " + hType.getNameString());
/*
case Types.OTHER :
try {
if (o instanceof Serializable) {
o = new JavaObjectData((Serializable) o);
break;
}
} catch (HsqlException e) {
PgType.throwError(e);
}
PgType.throwError(Error.error(ErrorCode.X_42565));
break;
case Types.SQL_BLOB :
//setBlobParameter(i, o);
//break;
case Types.SQL_CLOB :
//setClobParameter(i, o);
//break;
*/
case Types.SQL_DATE :
case Types.SQL_TIME_WITH_TIME_ZONE :
case Types.SQL_TIMESTAMP_WITH_TIME_ZONE :
case Types.SQL_TIME :
case Types.SQL_TIMESTAMP : {
try {
o = hType.convertToType(session, o, Type.SQL_VARCHAR);
} catch (HsqlException e) {
PgType.throwError(e);
}
break;
}
case Types.TINYINT :
case Types.SQL_SMALLINT :
case Types.SQL_INTEGER :
case Types.SQL_BIGINT :
case Types.SQL_REAL :
case Types.SQL_FLOAT :
case Types.SQL_DOUBLE :
case Types.SQL_NUMERIC :
case Types.SQL_DECIMAL :
try {
o = hType.convertToType(session, o, Type.SQL_VARCHAR);
} catch (HsqlException e) {
PgType.throwError(e);
}
break;
default :
/*
throw new RecoverableOdbcFailure(
"Parameter value is of unexpected type: "
+ hType.getNameString());
*/
try {
o = hType.convertToDefaultType(session, o);
// Supposed to handle String -> SQL_BIT. Not working.
} catch (HsqlException e) {
PgType.throwError(e);
}
break;
}
return o;
}
public String valueString(Object datum) {
String dataString = hType.convertToString(datum);
switch (hType.typeCode) {
case Types.SQL_BOOLEAN :
return String.valueOf(((Boolean) datum).booleanValue()
? 't' : 'f');
// Default would probably work fine, since the Driver looks at
// only the first byte, but this why send an extra 3 or 4 bytes
// with every data, plus there could be some dependency upon
// single-character in the driver code somewhere.
case Types.SQL_VARBINARY :
case Types.SQL_BINARY :
dataString = OdbcUtil.hexCharsToOctalOctets(dataString);
break;
}
return dataString;
}
/*
* The followign settings are a Java port of pgtypes.h
*/
public static final int TYPE_BOOL = 16;
public static final int TYPE_BYTEA = 17;
public static final int TYPE_CHAR = 18;
public static final int TYPE_NAME = 19;
public static final int TYPE_INT8 = 20;
public static final int TYPE_INT2 = 21;
public static final int TYPE_INT2VECTOR = 22;
public static final int TYPE_INT4 = 23;
public static final int TYPE_REGPROC = 24;
public static final int TYPE_TEXT = 25;
public static final int TYPE_OID = 26;
public static final int TYPE_TID = 27;
public static final int TYPE_XID = 28;
public static final int TYPE_CID = 29;
public static final int TYPE_OIDVECTOR = 30;
public static final int TYPE_SET = 32;
public static final int TYPE_XML = 142;
public static final int TYPE_XMLARRAY = 143;
public static final int TYPE_CHAR2 = 409;
public static final int TYPE_CHAR4 = 410;
public static final int TYPE_CHAR8 = 411;
public static final int TYPE_POINT = 600;
public static final int TYPE_LSEG = 601;
public static final int TYPE_PATH = 602;
public static final int TYPE_BOX = 603;
public static final int TYPE_POLYGON = 604;
public static final int TYPE_FILENAME = 605;
public static final int TYPE_CIDR = 650;
public static final int TYPE_FLOAT4 = 700;
public static final int TYPE_FLOAT8 = 701;
public static final int TYPE_ABSTIME = 702;
public static final int TYPE_RELTIME = 703;
public static final int TYPE_TINTERVAL = 704;
public static final int TYPE_UNKNOWN = 705;
public static final int TYPE_MONEY = 790;
public static final int TYPE_OIDINT2 = 810;
public static final int TYPE_MACADDR = 829;
public static final int TYPE_INET = 869;
public static final int TYPE_OIDINT4 = 910;
public static final int TYPE_OIDNAME = 911;
public static final int TYPE_TEXTARRAY = 1009;
public static final int TYPE_BPCHARARRAY = 1014;
public static final int TYPE_VARCHARARRAY = 1015;
public static final int TYPE_BPCHAR = 1042;
public static final int TYPE_VARCHAR = 1043;
public static final int TYPE_DATE = 1082;
public static final int TYPE_TIME = 1083;
public static final int TYPE_TIMESTAMP_NO_TMZONE = 1114; /* since 7.2 */
public static final int TYPE_DATETIME = 1184;
public static final int TYPE_TIME_WITH_TMZONE = 1266; /* since 7.1 */
public static final int TYPE_TIMESTAMP = 1296; /* deprecated since 7.0 */
public static final int TYPE_NUMERIC = 1700;
public static final int TYPE_RECORD = 2249;
public static final int TYPE_VOID = 2278;
public static final int TYPE_UUID = 2950;
// Numbering new HyperSQL-only client-side types beginning with 9999 and
// getting lower, to reduce chance of conflict with future PostreSQL types.
public static final int TYPE_BLOB = 9998;
public static final int TYPE_TINYINT = 9999;
// Apparenly new additions, from Postgresql server file pg_type.h:
public static final int TYPE_BIT = 1560;
// Also defined is _bit. No idea what that is about
public static final int TYPE_VARBIT = 1562;
// Also defined is _varbit. No idea what that is about
/* Following stuff is to support code copied from
* JDBCPreparedStatement.java. */
static final void throwError(HsqlException e) throws SQLException {
//#ifdef JAVA6
throw Util.sqlException(e.getMessage(), e.getSQLState(),
e.getErrorCode(), e);
//#else
/*
throw new SQLException(e.getMessage(), e.getSQLState(),
e.getErrorCode());
*/
//#endif JAVA6
}
static protected final PgType tinyIntSingleton =
new PgType(Type.TINYINT, TYPE_TINYINT, 1);
static protected final PgType int2singleton =
new PgType(Type.SQL_SMALLINT, TYPE_INT2, 2);
static protected final PgType int4singleton =
new PgType(Type.SQL_INTEGER, TYPE_INT4, 4);
static protected final PgType int8singleton =
new PgType(Type.SQL_BIGINT, TYPE_INT8, 8);
static protected final PgType doubleSingleton =
new PgType(Type.SQL_DOUBLE, TYPE_FLOAT8, 8);
static protected final PgType boolSingleton =
new PgType(Type.SQL_BOOLEAN, TYPE_BOOL, 1);
static protected final PgType textSingleton =
new PgType(Type.SQL_VARCHAR, TYPE_TEXT);
static protected final PgType dateSingleton =
new PgType(Type.SQL_DATE, TYPE_DATE, 4);
static protected final PgType unknownSingleton =
new PgType(Type.SQL_CHAR_DEFAULT, TYPE_UNKNOWN, -2);
static protected final PgType bitSingleton =
new PgType(Type.SQL_BIT, TYPE_BIT);
static protected final PgType bitVaryingSingleton =
new PgType(Type.SQL_BIT_VARYING, TYPE_VARBIT);
static protected final PgType daySecIntervalSingleton =
new PgType(Type.SQL_INTERVAL_DAY_TO_SECOND, TYPE_TINTERVAL, 16);
static protected final PgType hourSecIntervalSingleton =
new PgType(Type.SQL_INTERVAL_HOUR_TO_SECOND, TYPE_TINTERVAL, 16);
static protected final PgType minSecIntervalSingleton =
new PgType(Type.SQL_INTERVAL_MINUTE_TO_SECOND, TYPE_TINTERVAL, 16);
static protected final PgType secIntervalSingleton =
new PgType(Type.SQL_INTERVAL_SECOND, TYPE_TINTERVAL, 16);
static private void ignoredConstraintWarning(Type hsqldbType) {
if (hsqldbType.precision == 0 && hsqldbType.scale == 0) {
return;
}
// TODO: Use logging system!
/*
System.err.println(
"WARNING: Not passing INTERVAL precision setting "
+ "or second precision setting to ODBC client");
*/
}
}