/* Copyright (c) 2001-2019, 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 5968 2019-04-27 12:55:27Z fredt $
 */

package org.hsqldb.server;

import java.sql.SQLException;

import org.hsqldb.HsqlException;
import org.hsqldb.Session;
import org.hsqldb.jdbc.JDBCUtil;
import org.hsqldb.types.Type;
import org.hsqldb.types.Types;

Postgres types.
Author:Blaine Simpson (blaine dot simpson at admc dot com)
Since:1.9.0
/** * Postgres types. * * @author Blaine Simpson (blaine dot simpson at admc dot com) * @since 1.9.0 */
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 Also:
  • PgType(Type, int, Integer, Integer)
/** * 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 Also:
  • PgType(Type, int, Integer, Integer)
/** * 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, Integer.valueOf(typeWidth), null); }
Convenience wrapper for PgType constructor, when there is no fixed width for the type.
Params:
  • 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 Also:
/** * 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, Integer.valueOf((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); } }
Params:
  • hType – HyperSQL data type
  • oid – Numeric Object ID for the driver-side type.
  • typeWidthObject – Fixed width for the type
  • lpConstraintObject – Either length or Precision setting for this instance of the type. IMPORTANT! for all types with positive lpConstraint other than Timestamps and Times, add an extra 4 to satisfy crazy driver protocol.
/** * @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 satisfy 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(); } public static 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 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 constraint 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, Integer.valueOf(8), hType.precision); case Types.SQL_TIME_WITH_TIME_ZONE : return new PgType(hType, TYPE_TIME_WITH_TMZONE, Integer.valueOf(12), hType.precision); case Types.SQL_TIMESTAMP : return new PgType(hType, TYPE_TIMESTAMP_NO_TMZONE, Integer.valueOf(8), hType.precision); case Types.SQL_TIMESTAMP_WITH_TIME_ZONE : return new PgType(hType, TYPE_TIMESTAMP, Integer.valueOf(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.

This method will not be called for binary types. Binary values are just loaded directly into the Object parameter array.

Throws:
  • SQLException – if either argument is not acceptable.
/** * 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 following 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 void throwError(HsqlException e) throws SQLException { throw JDBCUtil.sqlException(e.getMessage(), e.getSQLState(), e.getErrorCode(), e); } 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"); */ } }