diff --git a/src/interfaces/jdbc/postgresql/Connection.java b/src/interfaces/jdbc/postgresql/Connection.java index 103b4e4b8f2..7c565e145fa 100644 --- a/src/interfaces/jdbc/postgresql/Connection.java +++ b/src/interfaces/jdbc/postgresql/Connection.java @@ -2,6 +2,7 @@ package postgresql; import java.io.*; import java.lang.*; +import java.lang.reflect.*; import java.net.*; import java.util.*; import java.sql.*; @@ -87,6 +88,12 @@ public class Connection implements java.sql.Connection // be across all connections, which could be to different backends. protected Hashtable fieldCache = new Hashtable(); + // This is used by Field to cache oid -> names. + // It's here, because it's shared across this connection only. + // Hence it cannot be static within the Field class, because it would then + // be across all connections, which could be to different backends. + protected Hashtable fieldCache = new Hashtable(); + /** * This is the current date style of the backend */ @@ -916,23 +923,90 @@ public class Connection implements java.sql.Connection * You can use the getValue() or setValue() methods to handle the returned * object. Custom objects can have their own methods. * + * In 6.4, this is extended to use the postgresql.util.Serialize class to + * allow the Serialization of Java Objects into the database without using + * Blobs. Refer to that class for details on how this new feature works. + * * @return PGobject for this type, and set to value * @exception SQLException if value is not correct for this type + * @see postgresql.util.Serialize */ - protected PGobject getObject(String type,String value) throws SQLException + protected Object getObject(String type,String value) throws SQLException { - PGobject obj = null; try { - String name = (String)objectTypes.get(type); - obj = (PGobject)(Class.forName(name==null?"postgresql.util.PGobject":name).newInstance()); + Object o = objectTypes.get(type); + + // If o is null, then the type is unknown, so check to see if type + // is an actual table name. If it does, see if a Class is known that + // can handle it + if(o == null) { + Serialize ser = new Serialize(this,type); + objectTypes.put(type,ser); + return ser.fetch(Integer.parseInt(value)); + } + + // If o is not null, and it is a String, then its a class name that + // extends PGobject. + // + // This is used to implement the postgresql unique types (like lseg, + // point, etc). + if(o instanceof String) { + // 6.3 style extending PG_Object + PGobject obj = null; + obj = (PGobject)(Class.forName((String)o).newInstance()); + obj.setType(type); + obj.setValue(value); + return (Object)obj; + } else { + // If it's an object, it should be an instance of our Serialize class + // If so, then call it's fetch method. + if(o instanceof Serialize) + return ((Serialize)o).fetch(Integer.parseInt(value)); + } + } catch(SQLException sx) { + throw sx; } catch(Exception ex) { throw new SQLException("Failed to create object for "+type+": "+ex); } - if(obj!=null) { - obj.setType(type); - obj.setValue(value); + + // should never be reached + return null; + } + + /** + * This stores an object into the database. + * @param o Object to store + * @return OID of the new rectord + * @exception SQLException if value is not correct for this type + * @see postgresql.util.Serialize + */ + protected int putObject(Object o) throws SQLException + { + try { + String type = o.getClass().getName(); + Object x = objectTypes.get(type); + + // If x is null, then the type is unknown, so check to see if type + // is an actual table name. If it does, see if a Class is known that + // can handle it + if(x == null) { + Serialize ser = new Serialize(this,type); + objectTypes.put(type,ser); + return ser.store(o); + } + + // If it's an object, it should be an instance of our Serialize class + // If so, then call it's fetch method. + if(x instanceof Serialize) + return ((Serialize)x).store(o); + } catch(SQLException sx) { + throw sx; + } catch(Exception ex) { + throw new SQLException("Failed to store object: "+ex); } - return obj; + + // should never be reached + return 0; } /** diff --git a/src/interfaces/jdbc/postgresql/DatabaseMetaData.java b/src/interfaces/jdbc/postgresql/DatabaseMetaData.java index 3aef2068cd3..bfcfc6f7cf6 100644 --- a/src/interfaces/jdbc/postgresql/DatabaseMetaData.java +++ b/src/interfaces/jdbc/postgresql/DatabaseMetaData.java @@ -2354,7 +2354,53 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData */ public java.sql.ResultSet getTypeInfo() throws SQLException { - // XXX-Not Implemented + ResultSet rs = connection.ExecSQL("select typname from pg_type"); + if(rs!=null) { + Field f[] = new Field[18]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32); + f[1] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2); + f[2] = new Field(connection, new String("PRECISION"), iInt4Oid, 4); + f[3] = new Field(connection, new String("LITERAL_PREFIX"), iVarcharOid, 32); + f[4] = new Field(connection, new String("LITERAL_SUFFIX"), iVarcharOid, 32); + f[5] = new Field(connection, new String("CREATE_PARAMS"), iVarcharOid, 32); + f[6] = new Field(connection, new String("NULLABLE"), iInt2Oid, 2); + f[7] = new Field(connection, new String("CASE_SENSITIVE"), iBoolOid, 1); + f[8] = new Field(connection, new String("SEARCHABLE"), iInt2Oid, 2); + f[9] = new Field(connection, new String("UNSIGNED_ATTRIBUTE"), iBoolOid, 1); + f[10] = new Field(connection, new String("FIXED_PREC_SCALE"), iBoolOid, 1); + f[11] = new Field(connection, new String("AUTO_INCREMENT"), iBoolOid, 1); + f[12] = new Field(connection, new String("LOCAL_TYPE_NAME"), iVarcharOid, 32); + f[13] = new Field(connection, new String("MINIMUM_SCALE"), iInt2Oid, 2); + f[14] = new Field(connection, new String("MAXIMUM_SCALE"), iInt2Oid, 2); + f[15] = new Field(connection, new String("SQL_DATA_TYPE"), iInt4Oid, 4); + f[16] = new Field(connection, new String("SQL_DATETIME_SUB"), iInt4Oid, 4); + f[17] = new Field(connection, new String("NUM_PREC_RADIX"), iInt4Oid, 4); + + while(rs.next()) { + byte[][] tuple = new byte[18][]; + String typname=rs.getString(1); + tuple[0] = typname.getBytes(); + tuple[1] = Integer.toString(Field.getSQLType(typname)).getBytes(); + tuple[2] = "9".getBytes(); // for now + tuple[6] = Integer.toString(typeNoNulls).getBytes(); // for now + tuple[7] = "f".getBytes(); // false for now - not case sensitive + tuple[8] = Integer.toString(typeSearchable).getBytes(); + tuple[9] = "f".getBytes(); // false for now - it's signed + tuple[10] = "f".getBytes(); // false for now - must handle money + tuple[11] = "f".getBytes(); // false for now - handle autoincrement + // 12 - LOCAL_TYPE_NAME is null + // 13 & 14 ? + // 15 & 16 are unused so we return null + tuple[17] = "10".getBytes(); // everything is base 10 + v.addElement(tuple); + } + rs.close(); + return new ResultSet(connection, f, v, "OK", 1); + } + return null; } diff --git a/src/interfaces/jdbc/postgresql/PreparedStatement.java b/src/interfaces/jdbc/postgresql/PreparedStatement.java index 1f82314e115..86121c57347 100644 --- a/src/interfaces/jdbc/postgresql/PreparedStatement.java +++ b/src/interfaces/jdbc/postgresql/PreparedStatement.java @@ -492,13 +492,21 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta { setObject(parameterIndex, x, targetSqlType, 0); } - + + /** + * This stores an Object into a parameter. + *

New for 6.4, if the object is not recognised, but it is + * Serializable, then the object is serialised using the + * postgresql.util.Serialize class. + */ public void setObject(int parameterIndex, Object x) throws SQLException { if (x instanceof String) setString(parameterIndex, (String)x); else if (x instanceof BigDecimal) setBigDecimal(parameterIndex, (BigDecimal)x); + else if (x instanceof Short) + setShort(parameterIndex, ((Short)x).shortValue()); else if (x instanceof Integer) setInt(parameterIndex, ((Integer)x).intValue()); else if (x instanceof Long) @@ -520,7 +528,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta else if (x instanceof PGobject) setString(parameterIndex, ((PGobject)x).getValue()); else - throw new SQLException("Unknown object type"); + setLong(parameterIndex, connection.putObject(x)); } /** @@ -548,6 +556,26 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta return super.execute(s.toString()); // in Statement class } + /** + * Returns the SQL statement with the current template values + * substituted. + */ + public String toString() { + StringBuffer s = new StringBuffer(); + int i; + + for (i = 0 ; i < inStrings.length ; ++i) + { + if (inStrings[i] == null) + s.append( '?' ); + else + s.append (templateStrings[i]); + s.append (inStrings[i]); + } + s.append(templateStrings[inStrings.length]); + return s.toString(); + } + // ************************************************************** // END OF PUBLIC INTERFACE // ************************************************************** diff --git a/src/interfaces/jdbc/postgresql/ResultSetMetaData.java b/src/interfaces/jdbc/postgresql/ResultSetMetaData.java index 7a1dfbbf26b..7a01a136ecd 100644 --- a/src/interfaces/jdbc/postgresql/ResultSetMetaData.java +++ b/src/interfaces/jdbc/postgresql/ResultSetMetaData.java @@ -296,6 +296,8 @@ public class ResultSetMetaData implements java.sql.ResultSetMetaData return 16; case Types.DOUBLE: return 16; + case Types.VARCHAR: + return 0; default: return 0; }