All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hsqldb.server.PgType Maven / Gradle / Ivy

There is a newer version: 2.7.2
Show newest version
/* 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
 */
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, Integer.valueOf(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, 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);
        }
    }

    /**
     * @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.
     *                     IMPORTANT! 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. */ 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"); */ } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy