diff options
Diffstat (limited to 'src/interfaces/jdbc/org/postgresql')
| -rw-r--r-- | src/interfaces/jdbc/org/postgresql/util/Serialize.java | 241 | 
1 files changed, 181 insertions, 60 deletions
| diff --git a/src/interfaces/jdbc/org/postgresql/util/Serialize.java b/src/interfaces/jdbc/org/postgresql/util/Serialize.java index 3af43e6eb84..a89fba5baf3 100644 --- a/src/interfaces/jdbc/org/postgresql/util/Serialize.java +++ b/src/interfaces/jdbc/org/postgresql/util/Serialize.java @@ -23,16 +23,16 @@ public class Serialize  {    // This is the connection that the instance refers to    protected org.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. @@ -41,16 +41,16 @@ public class Serialize    {      try {        conn = c; -      tableName = type.toLowerCase(); -      className = toClassName(type); +      tableName = toPostgreSQL(type); +      className = type;        ourClass = Class.forName(className);      } catch(ClassNotFoundException cnfe) {        throw new PSQLException("postgresql.serial.noclass",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+"'"); +    ResultSet rs = conn.ExecSQL("select typname from pg_type,pg_class where typname=relname and typname='" + tableName + "'");      if(rs!=null) {        if(rs.next())  	status=true; @@ -59,10 +59,26 @@ public class Serialize      // This should never occur, as org.postgresql has it's own internal checks      if(!status)        throw new PSQLException("postgresql.serial.table",type); -     +      // Finally cache the fields within the table    } -   + +  /** +   * Constructor when Object is passed in +   */ +  public Serialize(org.postgresql.Connection c,Object o) throws SQLException +  { +    this(c, o.getClass().getName()); +  } + +  /** +   * Constructor when Class is passed in +   */ +  public Serialize(org.postgresql.Connection c, Class cls) throws SQLException +  { +    this(c, cls.getName()); +  } +    /**     * This fetches an object from a table, given it's OID     * @param oid The oid of the object @@ -73,14 +89,20 @@ public class Serialize    {      try {        Object obj = ourClass.newInstance(); -       +        // NB: we use java.lang.reflect here to prevent confusion with        // the org.postgresql.Field -      java.lang.reflect.Field f[] = ourClass.getDeclaredFields(); + +      // used getFields to get only public fields. We have no way to set values +      // for other declarations. Maybe look for setFieldName() methods? +      java.lang.reflect.Field f[] = ourClass.getFields(); +        boolean hasOID=false;        int oidFIELD=-1;        StringBuffer sb = new StringBuffer("select");        char sep=' '; + +      // build a select for the fields. Look for the oid field to use in the where        for(int i=0;i<f.length;i++) {  	String n = f[i].getName();  	if(n.equals("oid")) { @@ -95,13 +117,37 @@ public class Serialize        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)); +            if ( !Modifier.isFinal(f[i].getModifiers()) ) { + +              if (f[i].getType().getName().equals("short")){ +                f[i].setShort(obj, rs.getShort(i+1)); +              } +              else +              if (f[i].getType().getName().equals("char")){ +                f[i].setChar(obj, rs.getString(i+1).toCharArray()[0]); +              } +              else +              if (f[i].getType().getName().equals("byte")){ +                f[i].setByte(obj, rs.getByte(i+1)); +              } +              else +              // booleans come out of pgsql as a t or an f +              if (f[i].getType().getName().equals("boolean")){ +                if ( rs.getString(i+1).equals("t")) +                  f[i].setBoolean(obj, true); +                else +                  f[i].setBoolean(obj, false); +              } +              else{ +	        f[i].set(obj,rs.getObject(i+1)); +              } +            }  	  }  	}  	rs.close(); @@ -114,7 +160,7 @@ public class Serialize        throw new SQLException(ie.toString());      }    } -   +    /**     * This stores an object into a table, returning it's OID.<p>     * @@ -138,24 +184,27 @@ public class Serialize      try {        // NB: we use java.lang.reflect here to prevent confusion with        // the org.postgresql.Field -      java.lang.reflect.Field f[] = ourClass.getDeclaredFields(); + +      // don't save private fields since we would not be able to fetch them +      java.lang.reflect.Field f[] = ourClass.getFields(); +        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 "); + +      StringBuffer sb = new StringBuffer(update?"update "+tableName+" set":"insert into " + tableName);        char sep=update?' ':'(';        for(int i=0;i<f.length;i++) {  	String n = f[i].getName(); @@ -164,49 +213,108 @@ public class Serialize  	sep=',';  	if(update) {  	  sb.append('='); -	  if(f[i].getType().getName().equals("java.lang.String")) { +          // handle unset values +          if (f[i].get(o) == null) +            sb.append("null"); +          else +	  if(f[i].getType().getName().equals("java.lang.String") || +             f[i].getType().getName().equals("char")) {  	    sb.append('\''); -	    sb.append(f[i].get(o).toString()); +            // don't allow single qoutes or newlines in the string +	    sb.append(fixString(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(sep); +	  sep=','; +          // handle unset values +          if (f[i].get(o) == null) +            sb.append("null"); +          else +	  if(f[i].getType().getName().equals("java.lang.String") || +             f[i].getType().getName().equals("char")) {  	    sb.append('\''); -	    sb.append(f[i].get(o).toString()); +            // don't allow single quotes or newlines in the string +	    sb.append(fixString(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(); -      } -       +      org.postgresql.ResultSet rs = (org.postgresql.ResultSet)conn.ExecSQL(sb.toString()); +        // fetch the OID for returning        int oid=0;        if(hasOID) { -	// set the oid in the object +	// If an update use the existing oid in the object  	f[oidFIELD].setInt(o,oid);        } +      else { +        String statStr = rs.getStatusString(); +        oid = Integer.parseInt(statStr.substring(statStr.indexOf(" ") + 1, statStr.lastIndexOf(" "))); +      } + +      if(rs!=null) { +	rs.close(); +      } +        return oid; -       +      } catch(IllegalAccessException iae) {        throw new SQLException(iae.toString());      }    } -   + +  /** +   * +   */ +   private String fixString(String s) { + +   int idx = -1; + +     // handle null +     if (s == null) +       return ""; + +     // if the string has single quotes in it escape them +     if ((idx = s.indexOf("'")) > -1) { +       StringBuffer buf = new StringBuffer(); +       StringTokenizer tok = new StringTokenizer(s, "'"); +       // handle quote as 1St charater +       if (idx > 0) buf.append(tok.nextToken()); + +       while(tok.hasMoreTokens()) +         buf.append("\\'").append(tok.nextToken()); + +       s = buf.toString(); +     } + +     // if the string has newlines in it convert them to \n +     if ((idx = s.indexOf("\n")) > -1) { +       StringBuffer buf = new StringBuffer(); +       StringTokenizer tok = new StringTokenizer(s, "\n"); +       if (idx > 0) buf.append(tok.nextToken()); + +       while(tok.hasMoreTokens()) +         buf.append("\\n").append(tok.nextToken()); + +       s = buf.toString(); +     } + +     return s; + +   } +    /**     * 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 @@ -219,7 +327,7 @@ public class Serialize    {      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 @@ -232,49 +340,50 @@ public class Serialize    {      if(c.isInterface())        throw new PSQLException("postgresql.serial.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)); +//      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(); + +//      java.lang.reflect.Field[] fields = c.getDeclaredFields(); +      java.lang.reflect.Field[] fields = c.getFields();        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 org.postgresql, recursing if a class  	    // is found -	    String n = fields[i].getType().getName(); +	    String n = type.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()); +	      create(con, type);  	      sb.append(toPostgreSQL(n));  	    }  	  }  	}        }        sb.append(")"); -       +        // Now create the table        DriverManager.println("Serialize.create:"+sb);        con.ExecSQL(sb.toString()); @@ -283,22 +392,26 @@ public class Serialize        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"}, +//    {"boolean",			"int1"}, +    {"boolean",			"bool"},      {"double",			"float8"},      {"float",			"float4"},      {"int",			"int4"}, -    {"long",			"int4"}, +//    {"long",			"int4"}, +    {"long",			"int8"},      {"short",			"int2"},      {"java.lang.String",	"text"},      {"java.lang.Integer",	"int4"},      {"java.lang.Float",		"float4"},      {"java.lang.Double",	"float8"}, -    {"java.lang.Short",		"int2"} +    {"java.lang.Short",		"int2"}, +    {"char",                    "char"}, +    {"byte",                    "int2"}    }; -   +    /**     * This converts a Java Class name to a org.postgresql table, by replacing . with     * _<p> @@ -314,17 +427,25 @@ public class Serialize    public static String toPostgreSQL(String name) throws SQLException    {      name = name.toLowerCase(); -     +      if(name.indexOf("_")>-1)        throw new PSQLException("postgresql.serial.underscore"); -     -    if(name.length()>32) -      throw new PSQLException("postgresql.serial.namelength",name,new Integer(name.length())); -     + +    // Postgres table names can only be 32 character long +    // If the full class name with package is too long +    // then just use the class name. If the class name is +    // too long throw an exception. +    if(name.length() > 32) { +      name = name.substring(name.lastIndexOf(".") + 1); + +      if(name.length()>32) +        throw new PSQLException("postgresql.serial.namelength",name,new Integer(name.length())); +    } +      return name.replace('.','_');    } -   -   + +    /**     * This converts a org.postgresql table to a Java Class name, by replacing _ with     * .<p> @@ -338,5 +459,5 @@ public class Serialize      name = name.toLowerCase();      return name.replace('_','.');    } -   +  } | 
