Re: JDBC Array Support, Take 2 - Mailing list pgsql-patches
From | Bruce Momjian |
---|---|
Subject | Re: JDBC Array Support, Take 2 |
Date | |
Msg-id | 200108101829.f7AIT2a24723@candle.pha.pa.us Whole thread Raw |
In response to | Re: JDBC Array Support, Take 2 (Rene Pijlman <rpijlman@wanadoo.nl>) |
List | pgsql-patches |
The problem is that there is wrapping in the patch. Attached is a cleaned up version. > On Fri, 10 Aug 2001 12:38:43 -0400, Greg Zoller wrote: > >patch -p1 < PATH_TO_PATCH_FILE/patch.txt > > I'm still getting this: > > $ patch -p1 <patch.txt > patching file errors.properties > patch: **** malformed patch at line 5: # This is the default > errors > > Any ideas what may be causing this? I'm on Red Hat Linux 7.1 and > patch -version says: > > patch 2.5.4 > Copyright 1984-1988 Larry Wall > Copyright 1989-1999 Free Software Foundation, Inc. > > Regards, > Ren? Pijlman > > ---------------------------(end of broadcast)--------------------------- > TIP 2: you can get off all lists at once with the unregister command > (send "unregister YourEmailAddressHere" to majordomo@postgresql.org) > -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 853-3000 + If your life is a hard drive, | 830 Blythe Avenue + Christ can be your backup. | Drexel Hill, Pennsylvania 19026 diff -bBdNrw -U5 OLD/errors.properties NEW/errors.properties --- OLD/errors.properties Thu Jan 18 09:37:13 2001 +++ NEW/errors.properties Tue Aug 7 12:54:15 2001 @@ -1,6 +1,7 @@ # This is the default errors +postgresql.arr.range:The array index is out of range. postgresql.drv.version:An internal error has occured. Please recompilethe driver. postgresql.con.auth:The authentication type {0} is not supported. Check that you have configured thepg_hba.conf file to include the client's IP address or Subnet, and that it is using an authentication scheme supportedby the driver. postgresql.con.authfail:An error occured while getting the authentication request. postgresql.con.call:CallableStatements are not supported at this time. postgresql.con.creobj:Failed to create object for{0} {1} diff -bBdNrw -U5 OLD/jdbc2/Array.java NEW/jdbc2/Array.java --- OLD/jdbc2/Array.java Wed Dec 31 16:00:00 1969 +++ NEW/jdbc2/Array.java Fri Aug 10 12:25:22 2001 @@ -0,0 +1,494 @@ +package org.postgresql.jdbc2; + +import java.text.*; +import java.sql.*; +import java.util.*; +import java.math.BigDecimal; +import org.postgresql.Field; +import org.postgresql.util.*; + +/** + * Array is used collect one column of query result data. + * + * <p>Read a field of type Array into either a natively-typed + * Java array object or a ResultSet. Accessor methods provide + * the ability to capture array slices. + * + * <p>Other than the constructor all methods are direct implementations + * of those specified for java.sql.Array. Please refer to the javadoc + * for java.sql.Array for detailed descriptions of the functionality + * and parameters of the methods of this class. + * + * @see ResultSet#getArray + */ + + +public class Array implements java.sql.Array +{ + public static final int BOOLEAN_ARRAY = 1000; + public static final int CHAR_ARRAY = 1002; + public static final int INT2_ARRAY = 1005; + public static final int INT4_ARRAY = 1007; + public static final int INT8_ARRAY = 1016; + public static final int VARCHAR_ARRAY = 1015; + public static final int FLOAT4_ARRAY = 1021; + public static final int FLOAT8_ARRAY = 1022; + public static final int DATE_ARRAY = 1182; + public static final int TIME_ARRAY = 1183; + public static final int TIMESTAMP_ARRAY = 1185; + public static final int NUMERIC_ARRAY = 1231; + //------------------------------------------ + public static final int BOOLEAN = 16; + public static final int CHAR = 18; + public static final int INT2 = 21; + public static final int INT4 = 23; + public static final int INT8 = 20; + public static final int VARCHAR = 1043; + public static final int FLOAT4 = 700; + public static final int FLOAT8 = 701; + public static final int DATE = 1082; + public static final int TIME = 1083; + public static final int TIMESTAMP = 1184; + public static final int NUMERIC = 1700; + + private org.postgresql.Connection conn = null; + private org.postgresql.Field field = null; + private org.postgresql.jdbc2.ResultSet rs = null; + private int idx = 0; + + /** + * Create a new Array + * + * @param conn a database connection + * @param idx 1-based index of the query field to load into this Array + * @param field the Field descriptor for the field to load into this Array + * @param rs the ResultSet from which to get the data for this Array + */ + public Array( org.postgresql.Connection conn, int idx, Field field, org.postgresql.jdbc2.ResultSet rs ) { + this.conn = conn; + this.field = field; + this.rs = rs; + this.idx = idx; + } + + public Object getArray() throws SQLException { + return getArray( 1, 0, null ); + } + + public Object getArray(long index, int count) throws SQLException { + return getArray( index, count, null ); + } + + public Object getArray(Map map) throws SQLException { + return getArray( 1, 0, map ); + } + + public Object getArray(long index, int count, Map map) throws SQLException { + if( map != null ) // For now maps aren't supported. + throw org.postgresql.Driver.notImplemented(); + + if (index < 1) + throw new PSQLException("postgresql.arr.range"); + Object retVal = null; + + ArrayList array = new ArrayList(); + String raw = rs.getFixedString(idx); + if( raw != null ) { + char[] chars = raw.toCharArray(); + StringBuffer sbuf = new StringBuffer(); + boolean foundOpen = false; + boolean insideString = false; + for( int i=0; i<chars.length; i++ ) { + if( chars[i] == '{' ) { + if( foundOpen ) // Only supports 1-D arrays for now + throw org.postgresql.Driver.notImplemented(); + foundOpen = true; + continue; + } + if( chars[i] == '"' ) { + insideString = !insideString; + continue; + } + if( (!insideString && chars[i] == ',') || chars[i] == '}' || i == chars.length-1) { + if( chars[i] != '"' && chars[i] != '}' && chars[i] != ',' ) + sbuf.append(chars[i]); + array.add( sbuf.toString() ); + sbuf = new StringBuffer(); + continue; + } + sbuf.append( chars[i] ); + } + } + String[] arrayContents = (String[]) array.toArray( new String[array.size()] ); + if( count == 0 ) + count = arrayContents.length; + index--; + if( index+count > arrayContents.length ) + throw new PSQLException("postgresql.arr.range"); + + int i = 0; + switch (field.getOID()) + { + case BOOLEAN: + case BOOLEAN_ARRAY: + retVal = new boolean[ count ]; + for( ; count > 0; count-- ) { + String s = arrayContents[(int)index++]; + try + { + char c = s.charAt(0); + ((boolean[])retVal)[i++] = ((c == 't') || (c == 'T')); + } catch (NumberFormatException e) { + throw new PSQLException ("postgresql.res.badbyte",s); + } + } + break; + case INT2: + case INT4: + case INT2_ARRAY: + case INT4_ARRAY: + retVal = new int[ count ]; + for( ; count > 0; count-- ) { + String s = arrayContents[(int)index++]; + try + { + ((int[])retVal)[i++] = Integer.parseInt( s ); + } catch (NumberFormatException e) { + throw new PSQLException ("postgresql.res.badint",s); + } + } + break; + case INT8: + case INT8_ARRAY: + retVal = new long[ count ]; + for( ; count > 0; count-- ) { + String s = arrayContents[(int)index++]; + try + { + ((long[])retVal)[i++] = Long.parseLong(s); + } catch (NumberFormatException e) { + throw new PSQLException ("postgresql.res.badlong",s); + } + } + break; + case NUMERIC: + case NUMERIC_ARRAY: + retVal = new BigDecimal[ count ]; + for( ; count > 0; count-- ) { + String s = arrayContents[(int)index++]; + try + { + ((BigDecimal[])retVal)[i] = new BigDecimal(s); + ((BigDecimal[])retVal)[i++].setScale(0); + } catch (NumberFormatException e) { + throw new PSQLException ("postgresql.res.badbigdec",s); + } catch (ArithmeticException e) { + throw new PSQLException ("postgresql.res.badbigdec",s); + } + } + break; + case FLOAT4: + case FLOAT4_ARRAY: + retVal = new float[ count ]; + for( ; count > 0; count-- ) { + String s = arrayContents[(int)index++]; + try + { + ((float[])retVal)[i++] = Float.parseFloat(s); + } catch (NumberFormatException e) { + throw new PSQLException ("postgresql.res.badfloat",s); + } + } + break; + case FLOAT8: + case FLOAT8_ARRAY: + retVal = new double[ count ]; + for( ; count > 0; count-- ) { + String s = arrayContents[(int)index++]; + try + { + ((double[])retVal)[i++] = Double.parseDouble(s); + } catch (NumberFormatException e) { + throw new PSQLException ("postgresql.res.baddouble",s); + } + } + break; + case CHAR: + case VARCHAR: + case CHAR_ARRAY: + case VARCHAR_ARRAY: + retVal = new String[ count ]; + for( ; count > 0; count-- ) + ((String[])retVal)[i++] = arrayContents[(int)index++]; + break; + case DATE: + case DATE_ARRAY: + retVal = new java.sql.Date[ count ]; + for( ; count > 0; count-- ) { + if( arrayContents[(int)index] == null ) + ((java.sql.Date[])retVal)[i++] = null; + else + ((java.sql.Date[])retVal)[i++] = java.sql.Date.valueOf(arrayContents[(int)index] ); + index++; + } + break; + case TIME: + case TIME_ARRAY: + retVal = new java.sql.Time[ count ]; + for( ; count > 0; count-- ) { + if( arrayContents[(int)index] == null ) + ((java.sql.Time[])retVal)[i++] = null; + else + ((java.sql.Time[])retVal)[i++] = java.sql.Time.valueOf(arrayContents[(int)index] ); + index++; + } + break; + case TIMESTAMP: + case TIMESTAMP_ARRAY: + retVal = new Timestamp[ count ]; + StringBuffer sbuf = null; + for( ; count > 0; count-- ) { + if( arrayContents[(int)index] == null ) { + ((java.sql.Timestamp[])retVal)[i++] = null; + index++; + continue; + } + boolean subsecond = true;; + //if string contains a '.' we have fractional seconds + if (arrayContents[i].indexOf('.') == -1) + subsecond = false; + + //here we are modifying the string from ISO format to a format java can understand + //java expects timezone info as 'GMT-08:00' instead of '-08' in postgres ISO format + //and java expects three digits if fractional seconds are present instead of two for postgres + //so this code strips off timezone info and adds on the GMT+/-... + //as well as adds a third digit for partial seconds if necessary + synchronized(this) { + // We must be synchronized here incase more theads access the Array + // bad practice but possible. Anyhow this is to protect sbuf and + // SimpleDateFormat objects + + // First time? + if(sbuf==null) + sbuf = new StringBuffer(); + + String s = arrayContents[(int)index++]; + sbuf.setLength(0); + sbuf.append(s); + + char sub = sbuf.charAt(sbuf.length()-3); + if (sub == '+' || sub == '-') { + sbuf.setLength(sbuf.length()-3); + if (subsecond) { + sbuf.append('0').append("GMT").append(s.substring(s.length()-3)).append(":00"); + } else { + sbuf.append("GMT").append(s.substring(s.length()-3)).append(":00"); + } + } else if (subsecond) { + sbuf.append('0'); + } + + // could optimize this a tad to remove too many object creations... + SimpleDateFormat df = null; + + if (sbuf.length()>23 && subsecond) { + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSzzzzzzzzz"); + } else if (sbuf.length()>23 && !subsecond) { + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzzzzzzzz"); + } else if (sbuf.length()>10 && subsecond) { + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + } else if (sbuf.length()>10 && !subsecond) { + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } else { + df = new SimpleDateFormat("yyyy-MM-dd"); + } + + try { + ((java.sql.Timestamp[])retVal)[i++] = new Timestamp(df.parse(sbuf.toString()).getTime()); + } catch(ParseException e) { + throw new PSQLException("postgresql.res.badtimestamp",new Integer(e.getErrorOffset()),s); + } + } + } + break; + + // Other datatypes not currently supported. If you are really using other types ask + // yourself if an array of non-trivial data types is really good database design. + default: + throw org.postgresql.Driver.notImplemented(); + } + return retVal; + } + + public int getBaseType() throws SQLException { + switch( field.getOID() ) { + case BOOLEAN_ARRAY: return Field.getSQLType("bool"); + case CHAR_ARRAY: return Field.getSQLType("char"); + case INT2_ARRAY: return Field.getSQLType("int2"); + case INT4_ARRAY: return Field.getSQLType("int4"); + case VARCHAR_ARRAY: return Field.getSQLType("varchar"); + case INT8_ARRAY: return Field.getSQLType("int8"); + case FLOAT4_ARRAY: return Field.getSQLType("float4"); + case FLOAT8_ARRAY: return Field.getSQLType("float8"); + case DATE_ARRAY: return Field.getSQLType("date"); + case TIME_ARRAY: return Field.getSQLType("time"); + case TIMESTAMP_ARRAY: return Field.getSQLType("timestamp"); + case NUMERIC_ARRAY: return Field.getSQLType("numeric"); + default: + throw org.postgresql.Driver.notImplemented(); + } + } + + public String getBaseTypeName() throws SQLException { + switch( field.getOID() ) { + case BOOLEAN_ARRAY: return "bool"; + case CHAR_ARRAY: return "char"; + case INT2_ARRAY: return "int2"; + case INT4_ARRAY: return "int4"; + case VARCHAR_ARRAY: return "varchar"; + case INT8_ARRAY: return "int8"; + case FLOAT4_ARRAY: return "float4"; + case FLOAT8_ARRAY: return "float8"; + case DATE_ARRAY: return "date"; + case TIME_ARRAY: return "time"; + case TIMESTAMP_ARRAY: return "timestamp"; + case NUMERIC_ARRAY: return "numeric"; + default: + throw org.postgresql.Driver.notImplemented(); + } + } + + public java.sql.ResultSet getResultSet() throws SQLException { + return getResultSet( 1, 0, null ); + } + + public java.sql.ResultSet getResultSet(long index, int count) throws SQLException { + return getResultSet( index, count, null ); + } + + public java.sql.ResultSet getResultSet(Map map) throws SQLException { + return getResultSet( 1, 0, map ); + } + + public java.sql.ResultSet getResultSet(long index, int count, java.util.Map map) throws SQLException { + Object array = getArray( index, count, map ); + Vector rows = new Vector(); + Field[] fields = new Field[2]; + fields[0] = new Field(conn, "INDEX", INT2, 2); + switch (field.getOID() ) + { + case BOOLEAN_ARRAY: + boolean[] booleanArray = (boolean[]) array; + fields[1] = new Field(conn, "VALUE", BOOLEAN, 1); + for( int i=0; i<booleanArray.length; i++ ) { + byte[][] tuple = new byte[2][0]; + tuple[0] = Integer.toString((int)index+i).getBytes(); // Index + tuple[1] = (booleanArray[i]?"YES":"NO").getBytes(); // Value + rows.addElement(tuple); + } + case INT2_ARRAY: + fields[1] = new Field(conn, "VALUE", INT2, 2); + case INT4_ARRAY: + int[] intArray = (int[]) array; + if( fields[1] == null ) + fields[1] = new Field(conn, "VALUE", INT4, 4); + for( int i=0; i<intArray.length; i++ ) { + byte[][] tuple = new byte[2][0]; + tuple[0] = Integer.toString((int)index+i).getBytes(); // Index + tuple[1] = Integer.toString(intArray[i]).getBytes(); // Value + rows.addElement(tuple); + } + break; + case INT8_ARRAY: + long[] longArray = (long[]) array; + fields[1] = new Field(conn, "VALUE", INT8, 8); + for( int i=0; i<longArray.length; i++ ) { + byte[][] tuple = new byte[2][0]; + tuple[0] = Integer.toString((int)index+i).getBytes(); // Index + tuple[1] = Long.toString(longArray[i]).getBytes(); // Value + rows.addElement(tuple); + } + break; + case NUMERIC_ARRAY: + BigDecimal[] bdArray = (BigDecimal[]) array; + fields[1] = new Field(conn, "VALUE", NUMERIC, -1); + for( int i=0; i<bdArray.length; i++ ) { + byte[][] tuple = new byte[2][0]; + tuple[0] = Integer.toString((int)index+i).getBytes(); // Index + tuple[1] = bdArray[i].toString().getBytes(); // Value + rows.addElement(tuple); + } + break; + case FLOAT4_ARRAY: + float[] floatArray = (float[]) array; + fields[1] = new Field(conn, "VALUE", FLOAT4, 4); + for( int i=0; i<floatArray.length; i++ ) { + byte[][] tuple = new byte[2][0]; + tuple[0] = Integer.toString((int)index+i).getBytes(); // Index + tuple[1] = Float.toString(floatArray[i]).getBytes(); // Value + rows.addElement(tuple); + } + break; + case FLOAT8_ARRAY: + double[] doubleArray = (double[]) array; + fields[1] = new Field(conn, "VALUE", FLOAT8, 8); + for( int i=0; i<doubleArray.length; i++ ) { + byte[][] tuple = new byte[2][0]; + tuple[0] = Integer.toString((int)index+i).getBytes(); // Index + tuple[1] = Double.toString(doubleArray[i]).getBytes(); // Value + rows.addElement(tuple); + } + break; + case CHAR_ARRAY: + fields[1] = new Field(conn, "VALUE", CHAR, 1); + case VARCHAR_ARRAY: + String[] strArray = (String[]) array; + if( fields[1] == null ) + fields[1] = new Field(conn, "VALUE", VARCHAR, -1); + for( int i=0; i<strArray.length; i++ ) { + byte[][] tuple = new byte[2][0]; + tuple[0] = Integer.toString((int)index+i).getBytes(); // Index + tuple[1] = strArray[i].getBytes(); // Value + rows.addElement(tuple); + } + break; + case DATE_ARRAY: + java.sql.Date[] dateArray = (java.sql.Date[]) array; + fields[1] = new Field(conn, "VALUE", DATE, 4); + for( int i=0; i<dateArray.length; i++ ) { + byte[][] tuple = new byte[2][0]; + tuple[0] = Integer.toString((int)index+i).getBytes(); // Index + tuple[1] = dateArray[i].toString().getBytes(); // Value + rows.addElement(tuple); + } + break; + case TIME_ARRAY: + java.sql.Time[] timeArray = (java.sql.Time[]) array; + fields[1] = new Field(conn, "VALUE", TIME, 8); + for( int i=0; i<timeArray.length; i++ ) { + byte[][] tuple = new byte[2][0]; + tuple[0] = Integer.toString((int)index+i).getBytes(); // Index + tuple[1] = timeArray[i].toString().getBytes(); // Value + rows.addElement(tuple); + } + break; + case TIMESTAMP_ARRAY: + java.sql.Timestamp[] timestampArray = (java.sql.Timestamp[]) array; + fields[1] = new Field(conn, "VALUE", TIMESTAMP, 8); + for( int i=0; i<timestampArray.length; i++ ) { + byte[][] tuple = new byte[2][0]; + tuple[0] = Integer.toString((int)index+i).getBytes(); // Index + tuple[1] = timestampArray[i].toString().getBytes(); // Value + rows.addElement(tuple); + } + break; + + // Other datatypes not currently supported. If you are really using other types ask + // yourself if an array of non-trivial data types is really good database design. + default: + throw org.postgresql.Driver.notImplemented(); + } + return new ResultSet((org.postgresql.jdbc2.Connection)conn, fields, rows, "OK", 1 ); + } +} + diff -bBdNrw -U5 OLD/jdbc2/ResultSet.java NEW/jdbc2/ResultSet.java --- OLD/jdbc2/ResultSet.java Tue May 22 07:46:46 2001 +++ NEW/jdbc2/ResultSet.java Tue Aug 7 12:54:44 2001 @@ -931,18 +931,21 @@ current_row = 0; this_row = (byte [][])rows.elementAt(current_row); return true; } - public Array getArray(String colName) throws SQLException + public java.sql.Array getArray(String colName) throws SQLException { return getArray(findColumn(colName)); } - public Array getArray(int i) throws SQLException + public java.sql.Array getArray(int i) throws SQLException { - throw org.postgresql.Driver.notImplemented(); + if (i < 1 || i > fields.length) + throw new PSQLException("postgresql.res.colrange"); + first(); + return (java.sql.Array) new org.postgresql.jdbc2.Array( connection, i, fields[i-1], this ); } public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException { // Now must call BigDecimal with a scale otherwise JBuilder barfs
pgsql-patches by date: