Abandoning PGobject - Mailing list pgsql-jdbc

From Markus Schaber
Subject Abandoning PGobject
Date
Msg-id 42232643.2050006@logi-track.com
Whole thread Raw
Responses Re: Abandoning PGobject
List pgsql-jdbc
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;
}

pgsql-jdbc by date:

Previous
From: Markus Schaber
Date:
Subject: Re: Abandoning PGobject
Next
From: Guillaume Cottenceau
Date:
Subject: impossible to update rows specifying columns with NULL value?