diff --git a/src/interfaces/jdbc/postgresql/util/Serialize.java b/src/interfaces/jdbc/postgresql/util/Serialize.java new file mode 100644 index 0000000000000000000000000000000000000000..56018490688e40d7421e43abaf072056a8750094 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/util/Serialize.java @@ -0,0 +1,342 @@ +package postgresql.util; + +import java.io.*; +import java.lang.*; +import java.lang.reflect.*; +import java.net.*; +import java.util.*; +import java.sql.*; + +/** + * This class uses PostgreSQL's object oriented features to store Java Objects. + * + * It does this by mapping a Java Class name to a table in the database. Each + * entry in this new table then represents a Serialized instance of this + * class. As each entry has an OID (Object IDentifier), this OID can be + * included in another table. + * + * This is too complex to show here, and will be documented in the main + * documents in more detail. + * + */ +public class Serialize +{ + // This is the connection that the instance refers to + protected postgresql.Connection conn; + + // This is the table name + protected String tableName; + + // This is the class name + protected String className; + + // This is the Class for this serialzed object + protected Class ourClass; + + /** + * This creates an instance that can be used to serialize or deserialize + * a Java object from a PostgreSQL table. + */ + public Serialize(postgresql.Connection c,String type) throws SQLException + { + try { + conn = c; + tableName = type.toLowerCase(); + className = toClassName(type); + ourClass = Class.forName(className); + } catch(ClassNotFoundException cnfe) { + throw new SQLException("No class found for '"+type+"`"); + } + + // Second check, the type must be a table + boolean status = false; + ResultSet rs = conn.ExecSQL("select typname from pg_type,pg_class where typname=relname and typname='"+type+"'"); + if(rs!=null) { + if(rs.next()) + status=true; + rs.close(); + } + // This should never occur, as postgresql has it's own internal checks + if(!status) + throw new SQLException("The table for "+type+" is not in the database. Contact the DBA, as the database is in an inconsistent state."); + + // Finally cache the fields within the table + } + + /** + * This fetches an object from a table, given it's OID + * @param oid The oid of the object + * @return Object relating to oid + * @exception SQLException on error + */ + public Object fetch(int oid) throws SQLException + { + try { + Object obj = ourClass.newInstance(); + + // NB: we use java.lang.reflect here to prevent confusion with + // the postgresql.Field + java.lang.reflect.Field f[] = ourClass.getDeclaredFields(); + boolean hasOID=false; + int oidFIELD=-1; + StringBuffer sb = new StringBuffer("select"); + char sep=' '; + for(int i=0;i<f.length;i++) { + String n = f[i].getName(); + if(n.equals("oid")) { + hasOID=true; + oidFIELD=i; + } + sb.append(sep); + sb.append(n); + sep=','; + } + sb.append(" from "); + sb.append(tableName); + sb.append(" where oid="); + sb.append(oid); + + DriverManager.println("store: "+sb.toString()); + ResultSet rs = conn.ExecSQL(sb.toString()); + if(rs!=null) { + if(rs.next()) { + for(int i=0;i<f.length;i++) { + f[i].set(obj,rs.getObject(i+1)); + } + } + rs.close(); + } else + throw new SQLException("Unexpected result from query"); + return obj; + } catch(IllegalAccessException iae) { + throw new SQLException(iae.toString()); + } catch(InstantiationException ie) { + throw new SQLException(ie.toString()); + } + } + + /** + * This stores an object into a table, returning it's OID.<p> + * + * If the object has an int called OID, and it is > 0, then + * that value is used for the OID, and the table will be updated. + * If the value of OID is 0, then a new row will be created, and the + * value of OID will be set in the object. This enables an object's + * value in the database to be updateable. + * + * If the object has no int called OID, then the object is stored. However + * if the object is later retrieved, amended and stored again, it's new + * state will be appended to the table, and will not overwrite the old + * entries. + * + * @param o Object to store (must implement Serializable) + * @return oid of stored object + * @exception SQLException on error + */ + public int store(Object o) throws SQLException + { + try { + // NB: we use java.lang.reflect here to prevent confusion with + // the postgresql.Field + java.lang.reflect.Field f[] = ourClass.getDeclaredFields(); + boolean hasOID=false; + int oidFIELD=-1; + boolean update=false; + + // Find out if we have an oid value + for(int i=0;i<f.length;i++) { + String n = f[i].getName(); + if(n.equals("oid")) { + hasOID=true; + oidFIELD=i; + + // We are an update if oid != 0 + update = f[i].getInt(o)>0; + } + } + + StringBuffer sb = new StringBuffer(update?"update "+tableName+" set":"insert into "+tableName+" values "); + char sep=update?' ':'('; + for(int i=0;i<f.length;i++) { + String n = f[i].getName(); + sb.append(sep); + sb.append(n); + sep=','; + if(update) { + sb.append('='); + if(f[i].getType().getName().equals("java.lang.String")) { + sb.append('\''); + sb.append(f[i].get(o).toString()); + sb.append('\''); + } else + sb.append(f[i].get(o).toString()); + } + } + + if(!update) { + sb.append(") values "); + sep='('; + for(int i=0;i<f.length;i++) { + String n = f[i].getName(); + if(f[i].getType().getName().equals("java.lang.String")) { + sb.append('\''); + sb.append(f[i].get(o).toString()); + sb.append('\''); + } else + sb.append(f[i].get(o).toString()); + } + sb.append(')'); + } + + DriverManager.println("store: "+sb.toString()); + ResultSet rs = conn.ExecSQL(sb.toString()); + if(rs!=null) { + rs.close(); + } + + // fetch the OID for returning + int oid=0; + if(hasOID) { + // set the oid in the object + f[oidFIELD].setInt(o,oid); + } + return oid; + + } catch(IllegalAccessException iae) { + throw new SQLException(iae.toString()); + } + } + + /** + * This method is not used by the driver, but it creates a table, given + * a Serializable Java Object. It should be used before serializing any + * objects. + * @param c Connection to database + * @param o Object to base table on + * @exception SQLException on error + */ + public static void create(postgresql.Connection con,Object o) throws SQLException + { + create(con,o.getClass()); + } + + /** + * This method is not used by the driver, but it creates a table, given + * a Serializable Java Object. It should be used before serializing any + * objects. + * @param c Connection to database + * @param o Class to base table on + * @exception SQLException on error + */ + public static void create(postgresql.Connection con,Class c) throws SQLException + { + if(c.isInterface()) + throw new SQLException("Cannot serialize an Interface"); + + // See if the table exists + String tableName = toPostgreSQL(c.getName()); + + ResultSet rs = con.ExecSQL("select relname from pg_class where relname = '"+tableName+"'"); + if(!rs.next()) { + DriverManager.println("found "+rs.getString(1)); + // No entries returned, so the table doesn't exist + + StringBuffer sb = new StringBuffer("create table "); + sb.append(tableName); + char sep='('; + + java.lang.reflect.Field[] fields = c.getDeclaredFields(); + for(int i=0;i<fields.length;i++) { + Class type = fields[i].getType(); + + // oid is a special field + if(!fields[i].getName().equals("oid")) { + sb.append(sep); + sb.append(fields[i].getName()); + sb.append(' '); + sep=','; + + if(type.isArray()) { + // array handling + } else { + // convert the java type to postgresql, recursing if a class + // is found + String n = fields[i].getType().getName(); + int j=0; + for(;j<tp.length && !tp[j][0].equals(n);j++); + if(j<tp.length) + sb.append(tp[j][1]); + else { + create(con,fields[i].getType()); + sb.append(toPostgreSQL(n)); + } + } + } + } + sb.append(")"); + + // Now create the table + DriverManager.println("Serialize.create:"+sb); + con.ExecSQL(sb.toString()); + rs.close(); + } else { + DriverManager.println("Serialize.create: table "+tableName+" exists, skipping"); + } + } + + // This is used to translate between Java primitives and PostgreSQL types. + private static final String tp[][] = { + {"boolean", "int1"}, + {"double", "float8"}, + {"float", "float4"}, + {"int", "int4"}, + {"long", "int4"}, + {"short", "int2"}, + {"java.lang.String", "text"}, + {"java.lang.Integer", "int4"}, + {"java.lang.Float", "float4"}, + {"java.lang.Double", "float8"}, + {"java.lang.Short", "int2"} + }; + + /** + * This converts a Java Class name to a postgresql table, by replacing . with + * _<p> + * + * Because of this, a Class name may not have _ in the name.<p> + * Another limitation, is that the entire class name (including packages) + * cannot be longer than 32 characters (a limit forced by PostgreSQL). + * + * @param name Class name + * @return PostgreSQL table name + * @exception SQLException on error + */ + public static String toPostgreSQL(String name) throws SQLException + { + name = name.toLowerCase(); + + if(name.indexOf("_")>-1) + throw new SQLException("Class names may not have _ in them: "+name); + + if(name.length()>32) + throw new SQLException("Class & Package name length cannot be longer than 32 characters. "+name+" is "+name.length()+" characters."); + + return name.replace('.','_'); + } + + + /** + * This converts a postgresql table to a Java Class name, by replacing _ with + * .<p> + * + * @param name PostgreSQL table name + * @return Class name + * @exception SQLException on error + */ + public static String toClassName(String name) throws SQLException + { + name = name.toLowerCase(); + return name.replace('_','.'); + } + +} diff --git a/src/interfaces/libpq/fe-print.c b/src/interfaces/libpq/fe-print.c index 493b37d0a3e3a851455f388a357fa9b909549756..140658b9422ad3f512529e3afa127595dd92faaf 100644 --- a/src/interfaces/libpq/fe-print.c +++ b/src/interfaces/libpq/fe-print.c @@ -9,10 +9,11 @@ * didn't really belong there. * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-print.c,v 1.3 1998/06/15 19:30:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-print.c,v 1.4 1998/06/16 06:57:27 momjian Exp $ * *------------------------------------------------------------------------- */ +#include <postgres.h> #include <stdlib.h> #include <stdio.h> #include <signal.h>