Index: pgjdbc/org/postgresql/jdbc2/AbstractJdbc2Connection.java =================================================================== --- pgjdbc.orig/org/postgresql/jdbc2/AbstractJdbc2Connection.java +++ pgjdbc/org/postgresql/jdbc2/AbstractJdbc2Connection.java @@ -143,6 +143,11 @@ public abstract class AbstractJdbc2Conne BitSet binaryOids = new BitSet(); if (binaryTransfer && protoConnection.getProtocolVersion() >= 3) { binaryOids.set(Oid.BYTEA); + binaryOids.set(Oid.INT2); + binaryOids.set(Oid.INT4); + binaryOids.set(Oid.INT8); + binaryOids.set(Oid.FLOAT4); + binaryOids.set(Oid.FLOAT8); } // split for receive and send for better control Index: pgjdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java =================================================================== --- pgjdbc.orig/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java +++ pgjdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java @@ -26,6 +26,7 @@ import java.util.Calendar; import java.util.Locale; import org.postgresql.core.*; import org.postgresql.largeobject.*; +import org.postgresql.util.ByteConverter; import org.postgresql.util.PGobject; import org.postgresql.util.PGbytea; import org.postgresql.util.PGtokenizer; @@ -1880,6 +1881,12 @@ public abstract class AbstractJdbc2Resul checkResultSet(columnIndex); if (wasNullFlag) return false; // SQL NULL + + if (isBinary(columnIndex)) { + int col = columnIndex - 1; + return readDoubleValue(this_row[col], fields[col].getOID(), + "boolean") == 1; + } return toBoolean( getString(columnIndex) ); } @@ -1893,6 +1900,15 @@ public abstract class AbstractJdbc2Resul if (wasNullFlag) return 0; // SQL NULL + if (isBinary(columnIndex)) { + int col = columnIndex - 1; + // there is no Oid for byte so must always do conversion from + // some other numeric type + return (byte) + readLongValue(this_row[col], fields[col].getOID(), Byte.MIN_VALUE, + Byte.MAX_VALUE, "byte"); + } + String s = getString(columnIndex); if (s != null ) @@ -1942,6 +1958,16 @@ public abstract class AbstractJdbc2Resul if (wasNullFlag) return 0; // SQL NULL + if (isBinary(columnIndex)) { + int col = columnIndex - 1; + int oid = fields[col].getOID(); + if (oid == Oid.INT2) { + return ByteConverter.int2(this_row[col], 0); + } + return (short) readLongValue(this_row[col], oid, Short.MIN_VALUE, + Short.MAX_VALUE, "short"); + } + String s = getFixedString(columnIndex); if (s != null) @@ -1984,6 +2010,16 @@ public abstract class AbstractJdbc2Resul if (wasNullFlag) return 0; // SQL NULL + if (isBinary(columnIndex)) { + int col = columnIndex - 1; + int oid = fields[col].getOID(); + if (oid == Oid.INT4) { + return ByteConverter.int4(this_row[col], 0); + } + return (int) readLongValue(this_row[col], oid, Integer.MIN_VALUE, + Integer.MAX_VALUE, "int"); + } + Encoding encoding = connection.getEncoding(); if (encoding.hasAsciiNumbers()) { try { @@ -2000,6 +2036,16 @@ public abstract class AbstractJdbc2Resul if (wasNullFlag) return 0; // SQL NULL + if (isBinary(columnIndex)) { + int col = columnIndex - 1; + int oid = fields[col].getOID(); + if (oid == Oid.INT8) { + return ByteConverter.int8(this_row[col], 0); + } + return readLongValue(this_row[col], oid, Long.MIN_VALUE, + Long.MAX_VALUE, "long"); + } + Encoding encoding = connection.getEncoding(); if (encoding.hasAsciiNumbers()) { try { @@ -2199,6 +2245,15 @@ public abstract class AbstractJdbc2Resul if (wasNullFlag) return 0; // SQL NULL + if (isBinary(columnIndex)) { + int col = columnIndex - 1; + int oid = fields[col].getOID(); + if (oid == Oid.FLOAT4) { + return ByteConverter.float4(this_row[col], 0); + } + return (float) readDoubleValue(this_row[col], oid, "float"); + } + return toFloat( getFixedString(columnIndex) ); } @@ -2208,6 +2263,15 @@ public abstract class AbstractJdbc2Resul if (wasNullFlag) return 0; // SQL NULL + if (isBinary(columnIndex)) { + int col = columnIndex - 1; + int oid = fields[col].getOID(); + if (oid == Oid.FLOAT8) { + return ByteConverter.float8(this_row[col], 0); + } + return readDoubleValue(this_row[col], oid, "double"); + } + return toDouble( getFixedString(columnIndex) ); } @@ -2926,6 +2990,88 @@ public abstract class AbstractJdbc2Resul } } + /** + * Converts any numeric binary field to double value. This method + * does no overflow checking. + * + * @param bytes The bytes of the numeric field. + * @param oid The oid of the field. + * @param targetType The target type. Used for error reporting. + * @return The value as double. + * @throws PSQLException If the field type is not supported numeric type. + */ + private double readDoubleValue(byte[] bytes, int oid, + String targetType) throws PSQLException { + // currently implemented binary encoded fields + switch (oid) { + case Oid.INT2: + return ByteConverter.int2(bytes, 0); + case Oid.INT4: + return ByteConverter.int4(bytes, 0); + case Oid.INT8: + // might not fit but there still should be no overflow checking + return ByteConverter.int8(bytes, 0); + case Oid.FLOAT4: + return ByteConverter.float4(bytes, 0); + case Oid.FLOAT8: + return ByteConverter.float8(bytes, 0); + } + throw new PSQLException (GT.tr("Cannot convert the column of type {0} to requested type {1}.", + new Object[]{Oid.toString(oid), targetType}), + PSQLState.DATA_TYPE_MISMATCH); + } + + /** + * Converts any numeric binary field to long value. + *

+ * This method is used by getByte,getShort,getInt and getLong. + * It must support a subset of the following java types that use Binary + * encoding. (fields that use text encoding use a different code path). + *

+ * byte,short,int,long,float,double,BigDecimal,boolean,string. + + * @param bytes The bytes of the numeric field. + * @param oid The oid of the field. + * @param minVal the minimum value allowed. + * @param minVal the maximum value allowed. + * @param targetType The target type. Used for error reporting. + * @return The value as long. + * @throws PSQLException If the field type is not supported numeric type + * or if the value is out of range. + */ + private long readLongValue(byte[] bytes, int oid, long minVal, long maxVal, + String targetType) + throws PSQLException { + long val; + // currently implemented binary encoded fields + switch (oid) { + case Oid.INT2: + val = ByteConverter.int2(bytes, 0); + break; + case Oid.INT4: + val = ByteConverter.int4(bytes, 0); + break; + case Oid.INT8: + val = ByteConverter.int8(bytes, 0); + break; + case Oid.FLOAT4: + val = (long) ByteConverter.float4(bytes, 0); + break; + case Oid.FLOAT8: + val = (long) ByteConverter.float8(bytes, 0); + break; + default: + throw new PSQLException (GT.tr("Cannot convert the column of type {0} to requested type {1}.", + new Object[]{Oid.toString(oid), targetType}), + PSQLState.DATA_TYPE_MISMATCH); + } + if (val < minVal || val > maxVal) { + throw new PSQLException(GT.tr("Bad value for type {0} : {1}", new Object[]{targetType, Long.valueOf(val)}), + PSQLState.NUMERIC_VALUE_OUT_OF_RANGE); + } + return val; + } + protected void updateValue(int columnIndex, Object value) throws SQLException { checkUpdateable(); Index: pgjdbc/org/postgresql/util/ByteConverter.java =================================================================== --- /dev/null +++ pgjdbc/org/postgresql/util/ByteConverter.java @@ -0,0 +1,157 @@ +/*------------------------------------------------------------------------- + * + * Copyright (c) 2006, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgjdbc/org/postgresql/util/ByteConverter.java,v 1.1 2006/10/30 18:30:16 jurka Exp $ + * + *------------------------------------------------------------------------- + */ +package org.postgresql.util; + +/** + * Helper methods to parse java base types from byte arrays. + * + * @author Mikko Tiihonen + */ +public class ByteConverter { + + private ByteConverter() { + // prevent instantiation of static helper class + } + + /** + * Parses a long value from the byte array. + * + * @param bytes The byte array to parse. + * @param idx The starting index of the parse in the byte array. + * @return parsed long value. + */ + public static long int8(byte[] bytes, int idx) { + return + ((long)(bytes[idx+0] & 255) << 56) + + ((long)(bytes[idx+1] & 255) << 48) + + ((long)(bytes[idx+2] & 255) << 40) + + ((long)(bytes[idx+3] & 255) << 32) + + ((long)(bytes[idx+4] & 255) << 24) + + ((long)(bytes[idx+5] & 255) << 16) + + ((long)(bytes[idx+6] & 255) << 8) + + (bytes[idx+7] & 255); + } + + /** + * Parses an int value from the byte array. + * + * @param bytes The byte array to parse. + * @param idx The starting index of the parse in the byte array. + * @return parsed int value. + */ + public static int int4(byte[] bytes, int idx) { + return + ((bytes[idx ] & 255) << 24) + + ((bytes[idx+1] & 255) << 16) + + ((bytes[idx+2] & 255) << 8) + + ((bytes[idx+3] & 255) ); + } + + /** + * Parses a short value from the byte array. + * + * @param bytes The byte array to parse. + * @param idx The starting index of the parse in the byte array. + * @return parsed short value. + */ + public static short int2(byte[] bytes, int idx) { + return (short) + (((bytes[idx ] & 255) << 8) + + ((bytes[idx+1] & 255) )); + } + + /** + * Parses a float value from the byte array. + * + * @param bytes The byte array to parse. + * @param idx The starting index of the parse in the byte array. + * @return parsed float value. + */ + public static float float4(byte[] bytes, int idx) { + return Float.intBitsToFloat(int4(bytes, idx)); + } + + /** + * Parses a double value from the byte array. + * + * @param bytes The byte array to parse. + * @param idx The starting index of the parse in the byte array. + * @return parsed double value. + */ + public static double float8(byte[] bytes, int idx) { + return Double.longBitsToDouble(int8(bytes, idx)); + } + + /** + * Encodes a long value to the byte array. + * + * @param target The byte array to encode to. + * @param idx The starting index in the byte array. + * @param value The value to encode. + */ + public static void int8(byte[] target, int idx, long value) { + target[idx+0] = (byte) (value >>> 56); + target[idx+1] = (byte) (value >>> 48); + target[idx+2] = (byte) (value >>> 40); + target[idx+3] = (byte) (value >>> 32); + target[idx+4] = (byte) (value >>> 24); + target[idx+5] = (byte) (value >>> 16); + target[idx+6] = (byte) (value >>> 8); + target[idx+7] = (byte) value; + } + + /** + * Encodes a int value to the byte array. + * + * @param target The byte array to encode to. + * @param idx The starting index in the byte array. + * @param value The value to encode. + */ + public static void int4(byte[] target, int idx, int value) { + target[idx+0] = (byte) (value >>> 24); + target[idx+1] = (byte) (value >>> 16); + target[idx+2] = (byte) (value >>> 8); + target[idx+3] = (byte) value; + } + + /** + * Encodes a int value to the byte array. + * + * @param target The byte array to encode to. + * @param idx The starting index in the byte array. + * @param value The value to encode. + */ + public static void int2(byte[] target, int idx, int value) { + target[idx+0] = (byte) (value >>> 8); + target[idx+1] = (byte) value; + } + + /** + * Encodes a int value to the byte array. + * + * @param target The byte array to encode to. + * @param idx The starting index in the byte array. + * @param value The value to encode. + */ + public static void float4(byte[] target, int idx, float value) { + int4(target, idx, Float.floatToRawIntBits(value)); + } + + /** + * Encodes a int value to the byte array. + * + * @param target The byte array to encode to. + * @param idx The starting index in the byte array. + * @param value The value to encode. + */ + public static void float8(byte[] target, int idx, double value) { + int8(target, idx, Double.doubleToRawLongBits(value)); + } +}