Hi,
There have been multiple discussions about new custom type
implementations on this list, and the necessary extensions that are
needed to allow binary transfer.
I just implemented the raw skeleton of my current idea how such thing
may work. It is based on what I remember from earlier.
The basic idea is that primitive types, Strings and byte[] are handled
by the driver directly, and all other built-in datatypes (e. G.
BigDecimal or Timestamp) as well as extension datatypes are handled by
this new model.
As it may make sense to call getInt on a BigDecimal, for reading all
methods are present, they are meant to be called 1:1 by the underlying
ResultSet. For writing, this is different, and everything can be handled
by a single setObject() method. I'm not really shure about which of the
given variants is the best one, so I included some of them.
It allows having different handlers for reading and writing, as well as
it allows a single handler to handle several sql and java types. It can
be made backwards compatible by including a TypeTextReader/
TypeTextWriter implementation that handles all PGobject subclasses.
This code is just a rough skeleton for my idea. I would like to get your
feedback what you think about it before I put too much effort into this.
Open issues are:
- What do you think about SUNs jdbc type map approach? I personally
think that, unfortunately, this is not flexible enough.
- How do we handle endianness? PostGIS canonical binary representation
includes endian information, but what about the other types?
- Handling of NULL values. I currently assume that the TypeDrivers are
not used for NULL values, because they can be handled in a type
independend manner by the driver itsself.
Markus
--
markus schaber | dipl. informatiker
logi-track ag | rennweg 14-16 | ch 8001 zürich
phone +41-43-888 62 52 | fax +41-43-888 62 53
mailto:schabios@logi-track.com | www.logi-track.com
/* TypeBinaryWriter.java
*
* (C) 28.02.2005 Markus Schaber, Logi-Track ag, CH 8001 Zuerich
*
* $Id: $
*/
package org.postgresql.types;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.Calendar;
public interface TypeBinaryReader extends TypeDriver {
String getString(byte[] data, int start, int length) throws SQLException;
boolean getBoolean(byte[] data, int start, int length) throws SQLException;
byte getByte(byte[] data, int start, int length) throws SQLException;
short getShort(byte[] data, int start, int length) throws SQLException;
int getInt(byte[] data, int start, int length) throws SQLException;
long getLong(byte[] data, int start, int length) throws SQLException;
float getFloat(byte[] data, int start, int length) throws SQLException;
double getDouble(byte[] data, int start, int length) throws SQLException;
BigDecimal getBigDecimal(byte[] data, int start, int length) throws SQLException;
byte[] getBytes(byte[] data, int start, int length) throws SQLException;
java.sql.Date getDate(byte[] data, int start, int length) throws SQLException;
java.sql.Date getDate(byte[] data, int start, int length, Calendar cal) throws SQLException;
java.sql.Time getTime(byte[] data, int start, int length) throws SQLException;
java.sql.Time getTime(byte[] data, int start, int length, Calendar cal) throws SQLException;
java.sql.Timestamp getTimestamp(byte[] data, int start, int length) throws SQLException;
java.sql.Timestamp getTimestamp(byte[] data, int start, int length, Calendar cal) throws SQLException;
java.io.InputStream getAsciiStream(byte[] data, int start, int length) throws SQLException;
java.io.InputStream getUnicodeStream(byte[] data, int start, int length) throws SQLException;
java.io.InputStream getBinaryStream(byte[] data, int start, int length) throws SQLException;
Object getObject(byte[] data, int start, int length) throws SQLException;
java.io.Reader getCharacterStream(byte[] data, int start, int length) throws SQLException;
java.net.URL getURL(byte[] data, int start, int length) throws SQLException;
}
/*
* TypeBinaryWriter.java
*
* (C) 28.02.2005 Markus Schaber, Logi-Track ag, CH 8001 Zuerich
*
* $Id: $
*/
package org.postgresql.types;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.SQLException;
public interface TypeBinaryWriter extends TypeDriver {
/**
* Return the length of an encoded object in bytes.
*
* This method can calculate the length on actual data. You can call it with
* any object the driver accepts via set* methods.
*
* @return the lenght in bytes, -1 for unknown.
* @throws SQLException if the driver does not know to handle the object or
* something else goes wrong.
*/
int objectBytesLength(Object data) throws SQLException;
/** Render the object into the SQL binary representation */
void renderObject(Object data, byte[] target, int startindex);
/**
* Render the object into the SQL binary representation
*/
void renderObject(Object data, OutputStream outstream) throws SQLException, IOException;
}
/*
* TypeDriver.java
*
* (C) 28.02.2005 Markus Schaber, Logi-Track ag, CH 8001 Zuerich
*
* $Id: $
*/
package org.postgresql.types;
import org.postgresql.PGConnection;
import java.sql.SQLException;
public interface TypeDriver {
/**
* Return all classes that are known to be supported at compile-time.
*/
public Class[] getSupportedClasses();
/** Return all sql types that are known to be supported. */
public String[] getSupportedTypes();
/** Test whether we support the SQL type. */
public boolean supports(String type);
/**
* Test whether we support the following class.
*
* This must return true for all classes that are in getSupportedClasses. It
* is also allowed to return true for other classes it knows to write, for
* example third-party subclasses that are not known at compile time.
*/
public boolean supports(Class klass);
/** Which SQL type do we produce for this object
* Note that this mapping is not 1:1, a single SQL type may correspond
* to a cuple of java types.
*/
public String objectType(Object data) throws SQLException;
/** Do we support creation of binary representation?
* @returns true on instances of TypeBinaryWriter
*/
public boolean supportsBinaryWrite();
/** Do we support creation of text representation?
* @returns true on instances of TypeTextWriter
*/
public boolean supportsTextWrite();
/** Do we support parsing of binary representation?
* @returns true on instances of TypeBinaryReader
*/
public boolean supportsBinaryReading();
/** Do we support parsing of text representation?
* @returns true on instances of TypeTextReader
*/
public boolean supportsTextReading();
/**
* Initialize the TypeDriver for a new connection.
*
* This method may do read-only queries on the database. It allows the
* driver to make adoptions for different protocols, server versions or
* installed data types. An example for this is PostGIS, where older
* versions do not support binary representation on the server side.
*
* XXX: Should we only use java.sql.Connection here?
*
* @returns the TypeDriver instance, which may be the same as the input
* parameter if no adoption is made.
*/
public TypeDriver forConnection(PGConnection conn) throws SQLException;
}
/*
* TypeMap.java
*
* (C) 28.02.2005 Markus Schaber, Logi-Track ag, CH 8001 Zuerich
*
* $Id: $
*/
package org.postgresql.types;
import org.postgresql.PGConnection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
public class TypeMap {
protected HashMap byType = new HashMap();
protected HashMap byClass = new HashMap();
protected ArrayList drivers = new ArrayList();
/**
* Search the driver for a given SQL type
*
* @returns the driver, null if none is found
*/
public TypeDriver getDriverForType(String type) {
return (TypeDriver) byType.get(type);
}
/**
* Search the driver for a given java object
*
* @returns the driver, null if none is found
*/
public TypeDriver getDriverForObject(Object obj) {
Class klass = obj.getClass();
return getDriverForClass(klass);
}
/**
* Search the driver for a given java class
*
* @returns the driver, null if none is found
*/
private TypeDriver getDriverForClass(Class klass) {
TypeDriver d = (TypeDriver) byClass.get(klass);
if (d == null) {
for (int i = 0; i < drivers.size(); i++) {
TypeDriver drv = (TypeDriver) drivers.get(i);
if (drv.supports(klass)) {
d = drv;
break;
}
}
if (d != null) { // cache the search result
byClass.put(klass, d);
}
}
return d;
}
/** Register a new driver */
public void addDriver(TypeDriver drv) {
Class[] classes = drv.getSupportedClasses();
for (int i = 0; i < classes.length; i++) {
byClass.put(classes[i], drv);
}
String[] types = drv.getSupportedTypes();
for (int i = 0; i < types.length; i++) {
byType.put(types[i], drv);
}
drivers.add(drv);
}
/**
* Clones the TypeDriver for a new connection.
*
* This is e. G. used by the driver to get connection specific instances
* from the default TypeDriver, and by Connection.setCatalog() to re-init
* the typemap on a database change.
*
* This method may do read-only queries on the database, as it calls
* TypeDriver.forConnection() for each known driver.
*
* XXX: Should we only use java.sql.Connection here?
*
* @returns the TypeDriver instance
*/
public TypeMap forConnection(PGConnection conn) throws SQLException {
TypeMap result = new TypeMap();
for (int i = 0; i < drivers.size(); i++) {
TypeDriver drv = (TypeDriver) drivers.get(i);
TypeDriver newdrv = drv.forConnection(conn);
result.addDriver(newdrv);
}
return result;
}
}
/* TypeBinaryWriter.java
*
* (C) 28.02.2005 Markus Schaber, Logi-Track ag, CH 8001 Zuerich
*
* $Id: $
*/
package org.postgresql.types;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.Calendar;
public interface TypeTextReader extends TypeDriver {
String getString(String data) throws SQLException;
boolean getBoolean(String data) throws SQLException;
byte getByte(String data) throws SQLException;
short getShort(String data) throws SQLException;
int getInt(String data) throws SQLException;
long getLong(String data) throws SQLException;
float getFloat(String data) throws SQLException;
double getDouble(String data) throws SQLException;
BigDecimal getBigDecimal(String data) throws SQLException;
byte[] getBytes(String data) throws SQLException;
java.sql.Date getDate(String data) throws SQLException;
java.sql.Date getDate(String data, Calendar cal) throws SQLException;
java.sql.Time getTime(String data) throws SQLException;
java.sql.Time getTime(String data, Calendar cal) throws SQLException;
java.sql.Timestamp getTimestamp(String data) throws SQLException;
java.sql.Timestamp getTimestamp(String data, Calendar cal) throws SQLException;
java.io.InputStream getAsciiStream(String data) throws SQLException;
java.io.InputStream getUnicodeStream(String data) throws SQLException;
java.io.InputStream getBinaryStream(String data) throws SQLException;
Object getObject(String data) throws SQLException;
java.io.Reader getCharacterStream(String data) throws SQLException;
java.net.URL getURL(String data) throws SQLException;
}
/*
* TypeBinaryWriter.java
*
* (C) 28.02.2005 Markus Schaber, Logi-Track ag, CH 8001 Zuerich
*
* $Id: $
*/
package org.postgresql.types;
import java.io.IOException;
import java.io.Writer;
import java.sql.SQLException;
public interface TypeTextWriter extends TypeDriver {
/**
* Return the length of an encoded object in characters.
*
* This method can calculate the length on actual data. You can call it with
* any object the driver accepts via set* methods.
*
* @return the lenght in bytes, -1 for unknown.
* @throws SQLException if the driver does not know to handle the object or
* something else goes wrong.
*/
int objectCharLength(Object data) throws SQLException;
/** Render the object into the SQL text representation */
String renderObject(Object data);
/** Render the object into the SQL text representation */
void renderObject(Object data, StringBuffer sb);
/** Render the object into the SQL text representation */
void renderObject(Object data, char[] target, int startindex);
/** Render the object into the SQL text representation
* @throws IOException */
void renderObject(Object data, Writer target) throws IOException;
}