From ba977c086c01726affcc96219a79584a11ccc78e Mon Sep 17 00:00:00 2001 From: "Marc G. Fournier" Date: Sun, 11 Jan 1998 21:14:56 +0000 Subject: [PATCH] Peter's Mega-Patch for JDBC... see README_6.3 for list of changes --- src/interfaces/jdbc/Makefile | 112 ++++- src/interfaces/jdbc/README | 22 +- src/interfaces/jdbc/README_6.3 | 78 +++ .../jdbc/postgresql/CallableStatement.java | 234 +++++++-- .../jdbc/postgresql/Connection.java | 244 +++++++++- .../jdbc/postgresql/DatabaseMetaData.java | 452 +++++++++++++++--- src/interfaces/jdbc/postgresql/Driver.java | 40 +- src/interfaces/jdbc/postgresql/Field.java | 122 +++-- src/interfaces/jdbc/postgresql/PG_Stream.java | 36 ++ .../jdbc/postgresql/PreparedStatement.java | 125 +++-- src/interfaces/jdbc/postgresql/ResultSet.java | 94 ++-- .../jdbc/postgresql/ResultSetMetaData.java | 3 - src/interfaces/jdbc/postgresql/Statement.java | 20 +- 13 files changed, 1276 insertions(+), 306 deletions(-) create mode 100644 src/interfaces/jdbc/README_6.3 diff --git a/src/interfaces/jdbc/Makefile b/src/interfaces/jdbc/Makefile index 469b552a69..928aafae5e 100644 --- a/src/interfaces/jdbc/Makefile +++ b/src/interfaces/jdbc/Makefile @@ -4,7 +4,7 @@ # Makefile for Java JDBC interface # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/Makefile,v 1.2 1997/09/29 20:11:42 scrappy Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/Makefile,v 1.3 1998/01/11 21:14:29 scrappy Exp $ # #------------------------------------------------------------------------- @@ -22,41 +22,70 @@ RM = rm -f $(JAVAC) $< .SUFFIXES: .class .java -.PHONY: all clean doc +.PHONY: all clean doc examples all: postgresql.jar + @echo ------------------------------------------------------------ + @echo The JDBC driver has now been built. To make it available to + @echo other applications, copy the postgresql.jar file to a public + @echo "place (under unix this could be /usr/local/lib) and add it" + @echo to the class path. + @echo + @echo Then either add -Djdbc.drivers=postgresql.Driver to the + @echo commandline when running your application, or edit the + @echo "properties file (~/.hotjava/properties under unix), and" + @echo add a line containing jdbc.drivers=postgresql.Driver + @echo + @echo More details are in the README file. + @echo ------------------------------------------------------------ + @echo To build the examples, type: + @echo " make examples" + @echo ------------------------------------------------------------ + @echo +# This rule builds the javadoc documentation doc: - $(JAVADOC) -public postgresql + export CLASSPATH=.;\ + $(JAVADOC) -public \ + postgresql \ + postgresql.fastpath \ + postgresql.largeobject +# These classes form the driver. These, and only these are placed into +# the jar file. OBJS= postgresql/CallableStatement.class \ postgresql/Connection.class \ postgresql/DatabaseMetaData.class \ postgresql/Driver.class \ postgresql/Field.class \ - postgresql/PG_Object.class \ postgresql/PG_Stream.class \ - postgresql/PGbox.class \ - postgresql/PGcircle.class \ - postgresql/PGlobj.class \ - postgresql/PGlseg.class \ - postgresql/PGpath.class \ - postgresql/PGpoint.class \ - postgresql/PGpolygon.class \ - postgresql/PGtokenizer.class \ postgresql/PreparedStatement.class \ postgresql/ResultSet.class \ postgresql/ResultSetMetaData.class \ - postgresql/Statement.class + postgresql/Statement.class \ + postgresql/fastpath/Fastpath.class \ + postgresql/fastpath/FastpathArg.class \ + postgresql/geometric/PGbox.class \ + postgresql/geometric/PGcircle.class \ + postgresql/geometric/PGlseg.class \ + postgresql/geometric/PGpath.class \ + postgresql/geometric/PGpoint.class \ + postgresql/geometric/PGpolygon.class \ + postgresql/largeobject/LargeObject.class \ + postgresql/largeobject/LargeObjectManager.class \ + postgresql/util/PGobject.class \ + postgresql/util/PGtokenizer.class postgresql.jar: $(OBJS) - $(JAR) -c0vf $@ $^ + $(JAR) -c0vf $@ $$($(FIND) postgresql -name "*.class" -print) # This rule removes any temporary and compiled files from the source tree. clean: $(FIND) . -name "*~" -exec $(RM) {} \; $(FIND) . -name "*.class" -exec $(RM) {} \; + $(FIND) . -name "*.html" -exec $(RM) {} \; $(RM) postgresql.jar + -$(RM) -rf Package-postgresql *output ####################################################################### # This helps make workout what classes are from what source files @@ -69,21 +98,56 @@ postgresql/Connection.class: postgresql/Connection.java postgresql/DatabaseMetaData.class: postgresql/DatabaseMetaData.java postgresql/Driver.class: postgresql/Driver.java postgresql/Field.class: postgresql/Field.java -postgresql/PG_Object.class: postgresql/PG_Object.java postgresql/PG_Stream.class: postgresql/PG_Stream.java -postgresql/PGbox.class: postgresql/PGbox.java -postgresql/PGcircle.class: postgresql/PGcircle.java -postgresql/PGlobj.class: postgresql/PGlobj.java -postgresql/PGlseg.class: postgresql/PGlseg.java -postgresql/PGpath.class: postgresql/PGpath.java -postgresql/PGpoint.class: postgresql/PGpoint.java -postgresql/PGpolygon.class: postgresql/PGpolygon.java -postgresql/PGtokenizer.class: postgresql/PGtokenizer.java postgresql/PreparedStatement.class: postgresql/PreparedStatement.java postgresql/ResultSet.class: postgresql/ResultSet.java postgresql/ResultSetMetaData.class: postgresql/ResultSetMetaData.java postgresql/Statement.class: postgresql/Statement.java +postgresql/fastpath/Fastpath.class: postgresql/fastpath/Fastpath.java +postgresql/fastpath/FastpathArg.class: postgresql/fastpath/FastpathArg.java +postgresql/geometric/PGbox.class: postgresql/geometric/PGbox.java +postgresql/geometric/PGcircle.class: postgresql/geometric/PGcircle.java +postgresql/geometric/PGlseg.class: postgresql/geometric/PGlseg.java +postgresql/geometric/PGpath.class: postgresql/geometric/PGpath.java +postgresql/geometric/PGpoint.class: postgresql/geometric/PGpoint.java +postgresql/geometric/PGpolygon.class: postgresql/geometric/PGpolygon.java +postgresql/largeobject/LargeObject.class: postgresql/largeobject/LargeObject.java +postgresql/largeobject/LargeObjectManager.class: postgresql/largeobject/LargeObjectManager.java +postgresql/util/PGobject.class: postgresql/util/PGobject.java +postgresql/util/PGtokenizer.class: postgresql/util/PGtokenizer.java +####################################################################### +# These classes are in the example directory, and form the examples +EX= example/basic.class \ + example/blobtest.class \ + example/datestyle.class \ + example/psql.class \ + example/ImageViewer.class +# This rule builds the examples +examples: postgresql.jar $(EX) + @echo ------------------------------------------------------------ + @echo The examples have been built. + @echo + @echo For instructions on how to use them, simply run them. For example: + @echo + @echo " java example.blobtest" + @echo + @echo This would display instructions on how to run the example. + @echo ------------------------------------------------------------ + @echo Available examples: + @echo + @echo " example.basic Basic JDBC useage" + @echo " example.blobtest Binary Large Object tests" + @echo " example.datestyle Shows how datestyles are handled" + @echo " example.ImageViewer Example application storing images" + @echo " example.psql Simple java implementation of psql" + @echo ------------------------------------------------------------ + @echo - +example/basic.class: example/basic.java +example/blobtest.class: example/blobtest.java +example/datestyle.class: example/datestyle.java +example/psql.class: example/psql.java +example/ImageViewer.class: example/ImageViewer.java +####################################################################### diff --git a/src/interfaces/jdbc/README b/src/interfaces/jdbc/README index cd43a5af2f..035ad008bf 100644 --- a/src/interfaces/jdbc/README +++ b/src/interfaces/jdbc/README @@ -10,6 +10,14 @@ or the JDBC mailing list: http://www.blackdown.org +For problems with this driver, then refer to the postgres-interfaces email +list: + + http://www.postgresql.org + +By the time V6.3 is released, full documentation will be on the web, and in +the distribution. + --------------------------------------------------------------------------- COMPILING @@ -115,15 +123,21 @@ them to the URL. eg: By default, the driver doesn't use password authentication. You can enable this by adding the argument auth. ie: - jdbc:postgresql:database?user=me&password=mypass&auth=y + jdbc:postgresql:database?user=me&password=mypass&auth=password or if passing the user & password directly via DriverManager.getConnection(): - jdbc:postgresql:database?auth=y + jdbc:postgresql:database?auth=password -PS: Password authentication is enabled if the value of auth starts with 'y'. +PS: Password authentication is enabled if the value of auth starts with 'p'. It is case insensitive. +As of postgresql 6.3, Ident (RFC 1413) authentication is also supported. +Simply use auth=ident in the url. + +Also, as of 6.3, a system property of postgresql.auth is supported. This +defines the default authentication to use. The auth property overides this. + --------------------------------------------------------------------------- That's the basics related to this driver. You'll need to read the JDBC Docs @@ -180,7 +194,7 @@ syntax for writing these to the database. --------------------------------------------------------------------------- -Peter T Mount, October 28 1997 +Peter T Mount, January 11 1998 home email: pmount@maidast.demon.co.uk http://www.demon.co.uk/finder work email: peter@maidstone.gov.uk http://www.maidstone.gov.uk diff --git a/src/interfaces/jdbc/README_6.3 b/src/interfaces/jdbc/README_6.3 new file mode 100644 index 0000000000..797d3ff99b --- /dev/null +++ b/src/interfaces/jdbc/README_6.3 @@ -0,0 +1,78 @@ +Ok, here's the JDBC patch. + +The jdbc6.3.tar.gz file that I've uploaded ftp.postgresql.org contains the +following files: + +README_6.3 This message +blob.patch The patch to src/backend/tcop/fastpath.c fixing large objects +jdbc.tar The entire jdbc driver + +I've put the entire driver here, rather than a patch, because its become +too complicated to do one this time. Files have been moved, two files +removed because they were obsolete, and there are a lot of new files. + +Heres what the patch does: + +* Memory overflow problem in the backend causing large objects to fail in + both libpq & jdbc (causing the backend to crash with a Segmentation + Violation) +* Problem with equals() method on the geometric support classes if the + class being checked wasn't the same class +* Fixed output of PGpath and PGpolygon support classes (missing , separator) +* Optimised the geometric support classes +* HTMLised the inline documentation, so the output of javadoc is easier + to read (mainly paragraphs) +* Removed obsolete class PGlobj (it never worked, and has been replaced + to read (mainly paragraphs) +* Removed obsolete class PGlobj (it never worked, and has been replaced + by the postgresql.largeobject package) +* Removed obsolete example JDBC_Test.java (replaced by new examples) +* Added < and > to nesting in PGtokenizer. +* Added fastpath support as a new package +* Added large object support as a new package +* Added ability of user code to handle custom storage types. +* Added new example testing the importing and exporting of a large object +* Added example application showing how to store and display images stored + as large objects +* Added example implementing part of psql client. This shows how to find out + what tables/columns are in a database (not yet complete) +* ResultSet.getBytes() now returns large object if field is an oid +* ResultSet.getString() now doesn't call getBytes() as this now would + cause an infinite loop because of large object support in getBytes() +* PreparedStatement.setBytes() now create a large object, and store its + oid into the column +* Reworked date style handling to make it easier to support new styles +* Added german and ISO styles, now all styles supported by postgresql + are now supported by the driver +* Fixed DatabaseMetaData.getTables() +* DatabaseMetaData.getTableTypes() returns our supported types. +* Fixed DatabaseMetaData.getColumns() + +These three are required for Borland's JBuilder to work. For now they +return an empty result, as I'm not sure yet on how to get the +required results. +* DatabaseMetaData.getBestRowIdentifier() +* DatabaseMetaData.getProcedureColumns() +* DatabaseMetaData.getIndexInfo() + +Finally, one change that is incompatible with earlier versions of the +driver. This change only affects any client code that uses the geometric +classes (eg: PGpoint) or the getObject()/setObject() methods. + +Because of a problem with javac, if user code includes the line: + +import postgresql.*; + +then javac will fail, saying that interfaces cannot be instanciated. + +To fix this, I've moved these classes into a new sub package, +postgresql.geometric and the PG_Object (renamed PGobject), and PGtokenizer +to postgresql.util.So the above line would become: + +import postgresql.geometric.*; + +Anyhow, I'm going to start writing some proper documentation for the +driver. For now, there is some available temporarily at: + + http://www.demon.co.uk/finder/postgres/jdbc/packages.html + diff --git a/src/interfaces/jdbc/postgresql/CallableStatement.java b/src/interfaces/jdbc/postgresql/CallableStatement.java index ede69bbb12..7f56a42b2a 100644 --- a/src/interfaces/jdbc/postgresql/CallableStatement.java +++ b/src/interfaces/jdbc/postgresql/CallableStatement.java @@ -4,46 +4,114 @@ import java.sql.*; import java.math.*; /** - * JDBC Interface to Postgres95 functions + * CallableStatement is used to execute SQL stored procedures. + * + *

JDBC provides a stored procedure SQL escape that allows stored + * procedures to be called in a standard way for all RDBMS's. This escape + * syntax has one form that includes a result parameter and one that does + * not. If used, the result parameter must be registered as an OUT + * parameter. The other parameters may be used for input, output or both. + * Parameters are refered to sequentially, by number. The first parameter + * is 1. + * + * {?= call [,, ...]} + * {call [,, ...]} + * + * + *

IN parameter values are set using the set methods inherited from + * PreparedStatement. The type of all OUT parameters must be registered + * prior to executing the stored procedure; their values are retrieved + * after execution via the get methods provided here. + * + *

A Callable statement may return a ResultSet or multiple ResultSets. + * Multiple ResultSets are handled using operations inherited from + * Statement. + * + *

For maximum portability, a call's ResultSets and update counts should + * be processed prior to getting the values of output parameters. + * + * @see Connection#prepareCall + * @see ResultSet */ -// Copy methods from the Result set object here. - public class CallableStatement extends PreparedStatement implements java.sql.CallableStatement { + /** + * @exception SQLException on failure + */ CallableStatement(Connection c,String q) throws SQLException { super(c,q); } - // Before executing a stored procedure call you must explicitly - // call registerOutParameter to register the java.sql.Type of each - // out parameter. + /** + * Before executing a stored procedure call you must explicitly + * call registerOutParameter to register the java.sql.Type of each + * out parameter. + * + *

Note: When reading the value of an out parameter, you must use + * the getXXX method whose Java type XXX corresponds to the + * parameter's registered SQL type. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param sqlType SQL type code defined by java.sql.Types; for + * parameters of type Numeric or Decimal use the version of + * registerOutParameter that accepts a scale value + * @exception SQLException if a database-access error occurs. + */ public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { } - // You must also specify the scale for numeric/decimal types: + /** + * You must also specify the scale for numeric/decimal types: + * + *

Note: When reading the value of an out parameter, you must use + * the getXXX method whose Java type XXX corresponds to the + * parameter's registered SQL type. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param sqlType use either java.sql.Type.NUMERIC or java.sql.Type.DECIMAL + * @param scale a value greater than or equal to zero representing the + * desired number of digits to the right of the decimal point + * @exception SQLException if a database-access error occurs. + */ public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException { } - public boolean isNull(int parameterIndex) throws SQLException { - return true; - } + // Old api? + //public boolean isNull(int parameterIndex) throws SQLException { + //return true; + //} - // New API (JPM) + /** + * An OUT parameter may have the value of SQL NULL; wasNull + * reports whether the last value read has this special value. + * + *

Note: You must first call getXXX on a parameter to read its + * value and then call wasNull() to see if the value was SQL NULL. + * @return true if the last parameter read was SQL NULL + * @exception SQLException if a database-access error occurs. + */ public boolean wasNull() throws SQLException { // check to see if the last access threw an exception return false; // fake it for now } - // Methods for retrieving OUT parameters from this statement. - public String getChar(int parameterIndex) throws SQLException { - return null; - } + // Old api? + //public String getChar(int parameterIndex) throws SQLException { + //return null; + //} - // New API (JPM) + /** + * Get the value of a CHAR, VARCHAR, or LONGVARCHAR parameter as a + * Java String. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ public String getString(int parameterIndex) throws SQLException { return null; } @@ -51,64 +119,148 @@ public class CallableStatement extends PreparedStatement implements java.sql.Cal // return null; //} - public String getLongVarChar(int parameterIndex) throws SQLException { - return null; - } + //public String getLongVarChar(int parameterIndex) throws SQLException { + //return null; + //} - // New API (JPM) (getBit) + /** + * Get the value of a BIT parameter as a Java boolean. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is false + * @exception SQLException if a database-access error occurs. + */ public boolean getBoolean(int parameterIndex) throws SQLException { return false; } - // New API (JPM) (getTinyInt) + /** + * Get the value of a TINYINT parameter as a Java byte. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ public byte getByte(int parameterIndex) throws SQLException { return 0; } - // New API (JPM) (getSmallInt) + /** + * Get the value of a SMALLINT parameter as a Java short. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ public short getShort(int parameterIndex) throws SQLException { return 0; } - // New API (JPM) (getInteger) - public int getInt(int parameterIndex) throws SQLException { + /** + * Get the value of an INTEGER parameter as a Java int. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ +public int getInt(int parameterIndex) throws SQLException { return 0; } - // New API (JPM) (getBigInt) + /** + * Get the value of a BIGINT parameter as a Java long. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ public long getLong(int parameterIndex) throws SQLException { return 0; } + /** + * Get the value of a FLOAT parameter as a Java float. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ public float getFloat(int parameterIndex) throws SQLException { return (float) 0.0; } + /** + * Get the value of a DOUBLE parameter as a Java double. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ public double getDouble(int parameterIndex) throws SQLException { return 0.0; } + /** + * Get the value of a NUMERIC parameter as a java.math.BigDecimal + * object. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param scale a value greater than or equal to zero representing the + * desired number of digits to the right of the decimal point + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { return null; } - // New API (JPM) (getBinary) + /** + * Get the value of a SQL BINARY or VARBINARY parameter as a Java + * byte[] + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ public byte[] getBytes(int parameterIndex) throws SQLException { return null; } // New API (JPM) (getLongVarBinary) - public byte[] getBinaryStream(int parameterIndex) throws SQLException { - return null; - } + //public byte[] getBinaryStream(int parameterIndex) throws SQLException { + //return null; + //} + /** + * Get the value of a SQL DATE parameter as a java.sql.Date object + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ public java.sql.Date getDate(int parameterIndex) throws SQLException { return null; } + + /** + * Get the value of a SQL TIME parameter as a java.sql.Time object. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ public java.sql.Time getTime(int parameterIndex) throws SQLException { return null; } + + /** + * Get the value of a SQL TIMESTAMP parameter as a java.sql.Timestamp object. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ public java.sql.Timestamp getTimestamp(int parameterIndex) throws SQLException { return null; @@ -119,12 +271,30 @@ public class CallableStatement extends PreparedStatement implements java.sql.Cal // You can obtain a ParameterMetaData object to get information // about the parameters to this CallableStatement. - public DatabaseMetaData getMetaData() { - return null; - } + //public DatabaseMetaData getMetaData() { + //return null; + //} // getObject returns a Java object for the parameter. // See the JDBC spec's "Dynamic Programming" chapter for details. + /** + * Get the value of a parameter as a Java object. + * + *

This method returns a Java object whose type coresponds to the + * SQL type that was registered for this parameter using + * registerOutParameter. + * + *

Note that this method may be used to read datatabase-specific, + * abstract data types. This is done by specifying a targetSqlType + * of java.sql.types.OTHER, which allows the driver to return a + * database-specific Java type. + * + *

See the JDBC spec's "Dynamic Programming" chapter for details. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return A java.lang.Object holding the OUT parameter value. + * @exception SQLException if a database-access error occurs. + */ public Object getObject(int parameterIndex) throws SQLException { return null; diff --git a/src/interfaces/jdbc/postgresql/Connection.java b/src/interfaces/jdbc/postgresql/Connection.java index 09344e1fe0..1c13b520d5 100644 --- a/src/interfaces/jdbc/postgresql/Connection.java +++ b/src/interfaces/jdbc/postgresql/Connection.java @@ -5,22 +5,21 @@ import java.lang.*; import java.net.*; import java.util.*; import java.sql.*; -import postgresql.*; +import postgresql.fastpath.*; +import postgresql.largeobject.*; +import postgresql.util.*; /** - * @version 1.0 15-APR-1997 - * @author Adrian Hall - * * A Connection represents a session with a specific database. Within the * context of a Connection, SQL statements are executed and results are * returned. * - * A Connection's database is able to provide information describing + *

A Connection's database is able to provide information describing * its tables, its supported SQL grammar, its stored procedures, the * capabilities of this connection, etc. This information is obtained * with the getMetaData method. * - * Note: By default, the Connection automatically commits changes + *

Note: By default, the Connection automatically commits changes * after executing each statement. If auto-commit has been disabled, an * explicit commit must be done or database changes will not be saved. * @@ -28,8 +27,12 @@ import postgresql.*; */ public class Connection implements java.sql.Connection { + // This is the network stream associated with this connection protected PG_Stream pg_stream; + // This is set by postgresql.Statement.setMaxRows() + protected int maxrows = 0; // maximum no. of rows; 0 = unlimited + private String PG_HOST; private int PG_PORT; private String PG_USER; @@ -59,7 +62,39 @@ public class Connection implements java.sql.Connection private String cursor = null; // The positioned update cursor name // This is false for US, true for European date formats - protected boolean europeanDates = false; + //protected boolean europeanDates = false; + + /** + * This is the current date style of the backend + */ + protected int currentDateStyle; + + /** + * This defines the formats for dates, according to the various date styles. + * + *

There are two strings for each entry. The first is the string to search + * for in the datestyle message, and the second the format to use. + * + *

To add a new date style, work out the format. Then with psql running + * in the date style you wish to add, type: show datestyle; + * + *

eg: + *

+   * => show datestyle;
+   * NOTICE:  Datestyle is SQL with European conventions
+   *                       ^^^^^^^^^^^^^^^^^
+   * 
The marked part of the string is the first string below. The second + * is your format. If a style (like ISO) ignores the US/European variants, + * then you can ignore the "with" part of the string. + */ + protected static final String dateStyles[] = { + "Postgres with European", "dd-MM-yyyy", + "Postgres with US", "MM-dd-yyyy", + "ISO", "yyyy-MM-dd", + "SQL with European", "dd/MM/yyyy", + "SQL with US", "MM/dd/yyyy", + "German", "dd.MM.yyyy" + }; // Now handle notices as warnings, so things like "show" now work protected SQLWarning firstWarning = null; @@ -67,6 +102,13 @@ public class Connection implements java.sql.Connection /** * Connect to a PostgreSQL database back end. * + *

Important Notice + * + *
Although this will connect to the database, user code should open + * the connection via the DriverManager.getConnection() methods only. + * + *
This should only be called from the postgresql.Driver class. + * * @param host the hostname of the database back end * @param port the port number of the postmaster process * @param info a Properties[] thing of the user and password @@ -108,6 +150,7 @@ public class Connection implements java.sql.Connection STARTUP_CODE=STARTUP_USER; } + // Now make the initial connection try { pg_stream = new PG_Stream(host, port); @@ -148,6 +191,9 @@ public class Connection implements java.sql.Connection clearWarnings(); ExecSQL("show datestyle"); + // Initialise object handling + initObjectTypes(); + // Mark the connection as ok, and cleanup clearWarnings(); PG_STATUS = CONNECTION_OK; @@ -468,10 +514,15 @@ public class Connection implements java.sql.Connection // ********************************************************** /** - * This adds a warning to the warning chain + * This adds a warning to the warning chain. + * @param msg message to add */ public void addWarning(String msg) { + //PrintStream log = DriverManager.getLogStream(); + //if(log!=null) + DriverManager.println(msg); + // Add the warning to the chain if(firstWarning!=null) firstWarning.setNextWarning(new SQLWarning(msg)); @@ -481,15 +532,24 @@ public class Connection implements java.sql.Connection // Now check for some specific messages // This is generated by the SQL "show datestyle" - if(msg.startsWith("NOTICE:DateStyle")) { - if(msg.indexOf("with US")==-1) - europeanDates=true; - else - europeanDates=false; - System.err.println("europeanDates="+europeanDates); + if(msg.startsWith("NOTICE:") && msg.indexOf("DateStyle")>0) { + // 13 is the length off "DateStyle is " + msg = msg.substring(msg.indexOf("DateStyle is ")+13); + + for(int i=0;i 0) { + Object tup=null; // holds rows as they are recieved + int c = pg_stream.ReceiveChar(); switch (c) @@ -536,7 +598,10 @@ public class Connection implements java.sql.Connection case 'B': // Binary Data Transfer if (fields == null) throw new SQLException("Tuple received before MetaData"); - tuples.addElement(pg_stream.ReceiveTuple(fields.length, true)); + tup = pg_stream.ReceiveTuple(fields.length, true); + // This implements Statement.setMaxRows() + if(maxrows==0 || tuples.size()NOTE: This is not part of JDBC, but allows access to + * functions on the postgresql backend itself. + * + *

It is primarily used by the LargeObject API + * + *

The best way to use this is as follows: + * + *

+   * import postgresql.fastpath.*;
+   * ...
+   * Fastpath fp = ((postgresql.Connection)myconn).getFastpathAPI();
+   * 
+ * + *

where myconn is an open Connection to postgresql. + * + * @return Fastpath object allowing access to functions on the postgresql + * backend. + * @exception SQLException by Fastpath when initialising for first time */ - public PGlobj getLargeObjectAPI() throws SQLException + public Fastpath getFastpathAPI() throws SQLException { - return new PGlobj(this); + if(fastpath==null) + fastpath = new Fastpath(this,pg_stream); + return fastpath; + } + + // This holds a reference to the Fastpath API if already open + private Fastpath fastpath = null; + + /** + * This returns the LargeObject API for the current connection. + * + *

NOTE: This is not part of JDBC, but allows access to + * functions on the postgresql backend itself. + * + *

The best way to use this is as follows: + * + *

+   * import postgresql.largeobject.*;
+   * ...
+   * LargeObjectManager lo = ((postgresql.Connection)myconn).getLargeObjectAPI();
+   * 
+ * + *

where myconn is an open Connection to postgresql. + * + * @return LargeObject object that implements the API + * @exception SQLException by LargeObject when initialising for first time + */ + public LargeObjectManager getLargeObjectAPI() throws SQLException + { + if(largeobject==null) + largeobject = new LargeObjectManager(this); + return largeobject; + } + + // This holds a reference to the LargeObject API if already open + private LargeObjectManager largeobject = null; + + /** + * This method is used internally to return an object based around + * postgresql's more unique data types. + * + *

It uses an internal Hashtable to get the handling class. If the + * type is not supported, then an instance of postgresql.util.PGobject + * is returned. + * + * You can use the getValue() or setValue() methods to handle the returned + * object. Custom objects can have their own methods. + * + * @return PGobject for this type, and set to value + * @exception SQLException if value is not correct for this type + */ + protected PGobject 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()); + } catch(Exception ex) { + throw new SQLException("Failed to create object for "+type+": "+ex); + } + if(obj!=null) { + obj.setType(type); + obj.setValue(value); + } + return obj; + } + + /** + * This allows client code to add a handler for one of postgresql's + * more unique data types. + * + *

NOTE: This is not part of JDBC, but an extension. + * + *

The best way to use this is as follows: + * + *

+   * ...
+   * ((postgresql.Connection)myconn).addDataType("mytype","my.class.name");
+   * ...
+   * 
+ * + *

where myconn is an open Connection to postgresql. + * + *

The handling class must extend postgresql.util.PGobject + * + * @see postgresql.util.PGobject + */ + public void addDataType(String type,String name) + { + objectTypes.put(type,name); + } + + // This holds the available types + private Hashtable objectTypes = new Hashtable(); + + // This array contains the types that are supported as standard. + // + // The first entry is the types name on the database, the second + // the full class name of the handling class. + // + private static final String defaultObjectTypes[][] = { + {"box", "postgresql.geometric.PGbox"}, + {"circle", "postgresql.geometric.PGcircle"}, + {"lseg", "postgresql.geometric.PGlseg"}, + {"path", "postgresql.geometric.PGpath"}, + {"point", "postgresql.geometric.PGpoint"}, + {"polygon", "postgresql.geometric.PGpolygon"} + }; + + // This initialises the objectTypes hashtable + private void initObjectTypes() + { + for(int i=0;iAdrian Hall - * * This class provides information about the database as a whole. * - * Many of the methods here return lists of information in ResultSets. You + *

Many of the methods here return lists of information in ResultSets. You * can use the normal ResultSet methods such as getString and getInt to * retrieve the data from these ResultSets. If a given form of metadata is * not available, these methods should throw a SQLException. * - * Some of these methods take arguments that are String patterns. These + *

Some of these methods take arguments that are String patterns. These * arguments all have names such as fooPattern. Within a pattern String, * "%" means match any substring of 0 or more characters, and "_" means * match any one character. Only metadata entries matching the search @@ -22,7 +19,7 @@ import java.util.*; * ref, it means that argument's criteria should be dropped from the * search. * - * A SQLException will be throws if a driver does not support a meta + *

A SQLException will be throws if a driver does not support a meta * data method. In the case of methods that return a ResultSet, either * a ResultSet (which may be empty) is returned or a SQLException is * thrown. @@ -33,6 +30,12 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData { Connection connection; // The connection association + // These define various OID's. Hopefully they will stay constant. + static final int iVarcharOid = 1043; // OID for varchar + static final int iBoolOid = 16; // OID for bool + static final int iInt2Oid = 21; // OID for int2 + static final int iInt4Oid = 23; // OID for int4 + public DatabaseMetaData(Connection conn) { this.connection = conn; @@ -152,19 +155,21 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData } /** - * What is the version of this database product. Note that - * PostgreSQL 6.1 has a system catalog called pg_version - + * What is the version of this database product. + * + *

Note that PostgreSQL 6.3 has a system catalog called pg_version - * however, select * from pg_version on any database retrieves - * no rows. For now, we will return the version 6.1 (in the - * hopes that we change this driver as often as we change the - * database) + * no rows. + * + *

For now, we will return the version 6.3 (in the hope that we change + * this driver as often as we change the database) * * @return the database version * @exception SQLException if a database access error occurs */ public String getDatabaseProductVersion() throws SQLException { - return ("6.2"); + return ("6.3"); } /** @@ -240,7 +245,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * as case sensitive and as a result store them in mixed case? * A JDBC-Compliant driver will always return false. * - * Predicament - what do they mean by "SQL identifiers" - if it + *

Predicament - what do they mean by "SQL identifiers" - if it * means the names of the tables and columns, then the answers * given below are correct - otherwise I don't know. * @@ -290,7 +295,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * case sensitive and as a result store them in mixed case? A * JDBC compliant driver will always return true. * - * Predicament - what do they mean by "SQL identifiers" - if it + *

Predicament - what do they mean by "SQL identifiers" - if it * means the names of the tables and columns, then the answers * given below are correct - otherwise I don't know. * @@ -340,7 +345,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * a space if identifier quoting isn't supported. A JDBC Compliant * driver will always use a double quote character. * - * If an SQL identifier is a table name, column name, etc. then + *

If an SQL identifier is a table name, column name, etc. then * we do not support it. * * @return the quoting string @@ -355,10 +360,12 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * Get a comma separated list of all a database's SQL keywords that * are NOT also SQL92 keywords. * - * Within PostgreSQL, the keywords are found in + *

Within PostgreSQL, the keywords are found in * src/backend/parser/keywords.c - * For SQL Keywords, I took the list provided at - * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt + * + *

For SQL Keywords, I took the list provided at + * + * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt * which is for SQL3, not SQL-92, but it is close enough for * this purpose. * @@ -410,7 +417,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * Get all the "extra" characters that can bew used in unquoted * identifier names (those beyond a-zA-Z0-9 and _) * - * From the file src/backend/parser/scan.l, an identifier is + *

From the file src/backend/parser/scan.l, an identifier is * {letter}{letter_or_digit} which makes it just those listed * above. * @@ -449,14 +456,16 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData /** * Is column aliasing supported? * - * If so, the SQL AS clause can be used to provide names for + *

If so, the SQL AS clause can be used to provide names for * computed columns or to provide alias names for columns as * required. A JDBC Compliant driver always returns true. * - * e.g. + *

e.g. * + *

    * select count(C) as C_COUNT from T group by C;
    *
+   * 

* should return a column named as C_COUNT instead of count(C) * * @return true if so @@ -506,7 +515,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData /** * Are expressions in "ORCER BY" lists supported? * - * e.g. select * from t order by a + b; + *
e.g. select * from t order by a + b; * * @return true if so * @exception SQLException if a database access error occurs @@ -607,7 +616,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * Can columns be defined as non-nullable. A JDBC Compliant driver * always returns true. * - * This changed from false to true in v6.2 of the driver, as this + *

This changed from false to true in v6.2 of the driver, as this * support was added to the backend. * * @return true if so @@ -622,9 +631,9 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * Does this driver support the minimum ODBC SQL grammar. This * grammar is defined at: * - * http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm + *

http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm * - * In Appendix C. From this description, we seem to support the + *

In Appendix C. From this description, we seem to support the * ODBC minimal (Level 0) grammar. * * @return true if so @@ -1142,7 +1151,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * What is the maximum number of columns in a table? From the * create_table(l) manual page... * - * "The new class is created as a heap with no initial data. A + *

"The new class is created as a heap with no initial data. A * class can have no more than 1600 attributes (realistically, * this is limited by the fact that tuple sizes must be less than * 8192 bytes)..." @@ -1393,6 +1402,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * Does a data definition statement within a transaction force * the transaction to commit? I think this means something like: * + *

    * CREATE TABLE T (A INT);
    * INSERT INTO T (A) VALUES (2);
    * BEGIN;
@@ -1400,6 +1410,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
    * CREATE TABLE X (A INT);
    * SELECT A FROM T INTO X;
    * COMMIT;
+   * 

* * does the CREATE TABLE call cause a commit? The answer is no. * @@ -1412,7 +1423,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData } /** - * Is a data definition statement within a transaction ignored? + * Is a data definition statement within a transaction ignored? * It seems to be (from experiment in previous method) * * @return true if so @@ -1426,22 +1437,26 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData /** * Get a description of stored procedures available in a catalog * - * Only procedure descriptions matching the schema and procedure + *

Only procedure descriptions matching the schema and procedure * name criteria are returned. They are ordered by PROCEDURE_SCHEM * and PROCEDURE_NAME * - * Each procedure description has the following columns: - * PROCEDURE_CAT String => procedure catalog (may be null) - * PROCEDURE_SCHEM String => procedure schema (may be null) - * PROCEDURE_NAME String => procedure name - * Field 4 reserved (make it null) - * Field 5 reserved (make it null) - * Field 6 reserved (make it null) - * REMARKS String => explanatory comment on the procedure - * PROCEDURE_TYPE short => kind of procedure - * * procedureResultUnknown - May return a result - * * procedureNoResult - Does not return a result - * * procedureReturnsResult - Returns a result + *

Each procedure description has the following columns: + *

    + *
  1. PROCEDURE_CAT String => procedure catalog (may be null) + *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) + *
  3. PROCEDURE_NAME String => procedure name + *
  4. Field 4 reserved (make it null) + *
  5. Field 5 reserved (make it null) + *
  6. Field 6 reserved (make it null) + *
  7. REMARKS String => explanatory comment on the procedure + *
  8. PROCEDURE_TYPE short => kind of procedure + *
      + *
    • procedureResultUnknown - May return a result + *
    • procedureNoResult - Does not return a result + *
    • procedureReturnsResult - Returns a result + *
    + *
* * @param catalog - a catalog name; "" retrieves those without a * catalog; null means drop catalog name from criteria @@ -1451,8 +1466,6 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * @return ResultSet - each row is a procedure description * @exception SQLException if a database access error occurs */ - static final int iVarcharOid = 1043; // This is the OID for a varchar() - static final int iInt2Oid = 21; // This is the OID for an int2 public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { // the field descriptors for the new ResultSet @@ -1497,17 +1510,183 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData return new ResultSet(connection, f, v, "OK", 1); } + /** + * Get a description of a catalog's stored procedure parameters + * and result columns. + * + *

Only descriptions matching the schema, procedure and parameter + * name criteria are returned. They are ordered by PROCEDURE_SCHEM + * and PROCEDURE_NAME. Within this, the return value, if any, is + * first. Next are the parameter descriptions in call order. The + * column descriptions follow in column number order. + * + *

Each row in the ResultSet is a parameter description or column + * description with the following fields: + *

    + *
  1. PROCEDURE_CAT String => procedure catalog (may be null) + *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) + *
  3. PROCEDURE_NAME String => procedure name + *
  4. COLUMN_NAME String => column/parameter name + *
  5. COLUMN_TYPE Short => kind of column/parameter: + *
    • procedureColumnUnknown - nobody knows + *
    • procedureColumnIn - IN parameter + *
    • procedureColumnInOut - INOUT parameter + *
    • procedureColumnOut - OUT parameter + *
    • procedureColumnReturn - procedure return value + *
    • procedureColumnResult - result column in ResultSet + *
    + *
  6. DATA_TYPE short => SQL type from java.sql.Types + *
  7. TYPE_NAME String => SQL type name + *
  8. PRECISION int => precision + *
  9. LENGTH int => length in bytes of data + *
  10. SCALE short => scale + *
  11. RADIX short => radix + *
  12. NULLABLE short => can it contain NULL? + *
    • procedureNoNulls - does not allow NULL values + *
    • procedureNullable - allows NULL values + *
    • procedureNullableUnknown - nullability unknown + *
    • REMARKS String => comment describing parameter/column + *
+ * @param catalog This is ignored in postgresql, advise this is set to null + * @param schemaPattern This is ignored in postgresql, advise this is set to null + * @param procedureNamePattern a procedure name pattern + * @param columnNamePattern a column name pattern + * @return each row is a stored procedure parameter or column description + * @exception SQLException if a database-access error occurs + * @see #getSearchStringEscape + */ + // Implementation note: This is required for Borland's JBuilder to work public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException { - // XXX-Not Implemented - return null; + // for now, this returns an empty result set. + Field f[] = new Field[13]; + 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("PROCEDURE_CAT"), iVarcharOid, 32); + f[1] = new Field(connection, new String("PROCEDURE_SCHEM"), iVarcharOid, 32); + f[2] = new Field(connection, new String("PROCEDURE_NAME"), iVarcharOid, 32); + f[3] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32); + f[4] = new Field(connection, new String("COLUMN_TYPE"), iInt2Oid, 2); + f[5] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2); + f[6] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32); + f[7] = new Field(connection, new String("PRECISION"), iInt4Oid, 4); + f[8] = new Field(connection, new String("LENGTH"), iInt4Oid, 4); + f[9] = new Field(connection, new String("SCALE"), iInt2Oid, 2); + f[10] = new Field(connection, new String("RADIX"), iInt2Oid, 2); + f[11] = new Field(connection, new String("NULLABLE"), iInt2Oid, 2); + f[12] = new Field(connection, new String("REMARKS"), iVarcharOid, 32); + + return new ResultSet(connection, f, v, "OK", 1); } + /** + * Get a description of tables available in a catalog. + * + *

Only table descriptions matching the catalog, schema, table + * name and type criteria are returned. They are ordered by + * TABLE_TYPE, TABLE_SCHEM and TABLE_NAME. + * + *

Each table description has the following columns: + * + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. TABLE_TYPE String => table type. Typical types are "TABLE", + * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL + * TEMPORARY", "ALIAS", "SYNONYM". + *
  5. REMARKS String => explanatory comment on the table + *
+ * + *

The valid values for the types parameter are: + * "TABLE", "INDEX", "LARGE OBJECT", "SEQUENCE", "SYSTEM TABLE" and + * "SYSTEM INDEX" + * + * @param catalog a catalog name; For postgresql, this is ignored, and + * should be set to null + * @param schemaPattern a schema name pattern; For postgresql, this is ignored, and + * should be set to null + * @param tableNamePattern a table name pattern. For all tables this should be "%" + * @param types a list of table types to include; null returns + * all types + * @return each row is a table description + * @exception SQLException if a database-access error occurs. + */ public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException { - return connection.createStatement().executeQuery("SELECT '' as TABLE_CAT,'' AS TABLE_SCHEM,relname AS TABLE_NAME,'TABLE' AS TABLE_TYPE,'' AS REMARKS FROM pg_class WHERE relkind = 'r' and relname !~ '^pg_' and relname !~ '^Inv' and relname ~ '"+tableNamePattern+"' ORDER BY TABLE_NAME"); + // the field descriptors for the new ResultSet + Field f[] = new Field[5]; + 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("TABLE_CAT"), iVarcharOid, 32); + f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32); + f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32); + f[3] = new Field(connection, new String("TABLE_TYPE"), iVarcharOid, 32); + f[4] = new Field(connection, new String("REMARKS"), iVarcharOid, 32); + + // Now form the query + StringBuffer sql = new StringBuffer("select relname,oid from pg_class where "); + boolean notFirst=false; + for(int i=0;i0 order by c.relname,a.attnum"); + + while(r.next()) { + byte[][] tuple = new byte[18][0]; + + String name = r.getString(1); + String remarks = new String("no remarks"); + + // Fetch the description for the table (if any) + ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(1)); + if(dr.getTupleCount()==1) { + dr.next(); + remarks=dr.getString(1); + } + dr.close(); + + tuple[0] = "".getBytes(); // Catalog name + tuple[1] = "".getBytes(); // Schema name + tuple[2] = r.getString(2).getBytes(); // Table name + tuple[3] = r.getString(3).getBytes(); // Column name + + dr = connection.ExecSQL("select typname from pg_type where oid = "+r.getString(4)); + dr.next(); + String typname=dr.getString(1); + dr.close(); + tuple[4] = Integer.toString(Field.getSQLType(typname)).getBytes(); // Data type + tuple[5] = typname.getBytes(); // Type name + + tuple[6] = r.getString(7).getBytes(); // Column size + + tuple[7] = null; // Buffer length + + tuple[8] = "0".getBytes(); // Decimal Digits - how to get this? + tuple[9] = "10".getBytes(); // Num Prec Radix - assume decimal + + // tuple[10] is below + + tuple[11] = remarks.getBytes(); // Remarks + + tuple[12] = null; // column default + + tuple[13] = null; // sql data type (unused) + tuple[14] = null; // sql datetime sub (unused) + + tuple[15] = tuple[6]; // char octet length + + tuple[16] = r.getString(5).getBytes(); // ordinal position + + String nullFlag = r.getString(6); + tuple[10] = Integer.toString(nullFlag.equals("f")?java.sql.DatabaseMetaData.columnNullable:java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable + tuple[17] = (nullFlag.equals("f")?"YES":"NO").getBytes(); // is nullable + + v.addElement(tuple); + } + r.close(); + return new ResultSet(connection, f, v, "OK", 1); } /** @@ -1649,8 +1925,30 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData */ public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException { - // XXX-Not Implemented + // XXX-Not Implemented as grant is broken return null; + //Field f[] = new Field[8]; + //Vector v = new Vector(); + // + //f[0] = new Field(connection,new String("TABLE_CAT"),iVarcharOid,32); + //f[1] = new Field(connection,new String("TABLE_SCHEM"),iVarcharOid,32); + //f[2] = new Field(connection,new String("TABLE_NAME"),iVarcharOid,32); + //f[3] = new Field(connection,new String("COLUMN_NAME"),iVarcharOid,32); + //f[4] = new Field(connection,new String("GRANTOR"),iVarcharOid,32); + //f[5] = new Field(connection,new String("GRANTEE"),iVarcharOid,32); + //f[6] = new Field(connection,new String("PRIVILEGE"),iVarcharOid,32); + //f[7] = new Field(connection,new String("IS_GRANTABLE"),iVarcharOid,32); + // + //// This is taken direct from the psql source + //ResultSet r = connection.ExecSQL("SELECT relname, relacl FROM pg_class, pg_user WHERE ( relkind = 'r' OR relkind = 'i') and relname !~ '^pg_' and relname !~ '^xin[vx][0-9]+' and usesysid = relowner ORDER BY relname"); + //while(r.next()) { + //byte[][] tuple = new byte[8][0]; + //tuple[0] = tuple[1]= "default".getBytes(); + // + //v.addElement(tuple); + //} + // + //return new ResultSet(connection,f,v,"OK",1); } /** @@ -1722,10 +2020,24 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * @param nullable include columns that are nullable? * @return ResultSet each row is a column description */ + // Implementation note: This is required for Borland's JBuilder to work public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException { - // XXX-Not Implemented - return null; + // for now, this returns an empty result set. + Field f[] = new Field[8]; + 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("SCOPE"), iInt2Oid, 2); + f[1] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32); + f[2] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2); + f[3] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32); + f[4] = new Field(connection, new String("COLUMN_SIZE"), iInt4Oid, 4); + f[5] = new Field(connection, new String("BUFFER_LENGTH"), iInt4Oid, 4); + f[6] = new Field(connection, new String("DECIMAL_DIGITS"), iInt2Oid, 2); + f[7] = new Field(connection, new String("PSEUDO_COLUMN"), iInt2Oid, 2); + + return new ResultSet(connection, f, v, "OK", 1); } /** @@ -2078,9 +2390,29 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * accurate * @return ResultSet each row is an index column description */ + // Implementation note: This is required for Borland's JBuilder to work public java.sql.ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException { - // XXX-Not Implemented - return null; + // for now, this returns an empty result set. + Field f[] = new Field[13]; + 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("TABLE_CAT"), iVarcharOid, 32); + f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32); + f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32); + f[3] = new Field(connection, new String("NON_UNIQUE"), iBoolOid, 1); + f[4] = new Field(connection, new String("INDEX_QUALIFIER"), iVarcharOid, 32); + f[5] = new Field(connection, new String("INDEX_NAME"), iVarcharOid, 32); + f[6] = new Field(connection, new String("TYPE"), iInt2Oid, 2); + f[7] = new Field(connection, new String("ORDINAL_POSITION"), iInt2Oid, 2); + f[8] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32); + f[9] = new Field(connection, new String("ASC_OR_DESC"), iVarcharOid, 32); + f[10] = new Field(connection, new String("CARDINALITY"), iInt4Oid, 4); + f[11] = new Field(connection, new String("PAGES"), iInt4Oid, 4); + f[12] = new Field(connection, new String("FILTER_CONDITION"), iVarcharOid, 32); + + return new ResultSet(connection, f, v, "OK", 1); } } + diff --git a/src/interfaces/jdbc/postgresql/Driver.java b/src/interfaces/jdbc/postgresql/Driver.java index 7b711455ad..4b1772c88e 100644 --- a/src/interfaces/jdbc/postgresql/Driver.java +++ b/src/interfaces/jdbc/postgresql/Driver.java @@ -7,17 +7,17 @@ import java.util.*; * The Java SQL framework allows for multiple database drivers. Each * driver should supply a class that implements the Driver interface * - * The DriverManager will try to load as many drivers as it can find and then - * for any given connection request, it will ask each driver in turn to try - * to connect to the target URL. + *

The DriverManager will try to load as many drivers as it can find and + * then for any given connection request, it will ask each driver in turn + * to try to connect to the target URL. * - * It is strongly recommended that each Driver class should be small and + *

It is strongly recommended that each Driver class should be small and * standalone so that the Driver class can be loaded and queried without * bringing in vast quantities of supporting code. * - * When a Driver class is loaded, it should create an instance of itself and - * register it with the DriverManager. This means that a user can load and - * register a driver by doing Class.forName("foo.bah.Driver") + *

When a Driver class is loaded, it should create an instance of itself + * and register it with the DriverManager. This means that a user can load + * and register a driver by doing Class.forName("foo.bah.Driver") * * @see postgresql.Connection * @see java.sql.Driver @@ -58,18 +58,18 @@ public class Driver implements java.sql.Driver * when the JDBC driverManager is asked to connect to a given URL, * it passes the URL to each loaded driver in turn. * - * The driver should raise an SQLException if it is the right driver + *

The driver should raise an SQLException if it is the right driver * to connect to the given URL, but has trouble connecting to the * database. * - * The java.util.Properties argument can be used to pass arbitrary + *

The java.util.Properties argument can be used to pass arbitrary * string tag/value pairs as connection arguments. Normally, at least * "user" and "password" properties should be included in the * properties. * - * Our protocol takes the form: + * Our protocol takes the forms: *

-   *	jdbc:postgresql://host:port/database
+   *	jdbc:postgresql://host:port/database?param1=val1&...
    * 
* * @param url the URL of the database to connect to @@ -110,7 +110,8 @@ public class Driver implements java.sql.Driver * The getPropertyInfo method is intended to allow a generic GUI * tool to discover what properties it should prompt a human for * in order to get enough information to connect to a database. - * Note that depending on the values the human has supplied so + * + *

Note that depending on the values the human has supplied so * far, additional values may become necessary, so it may be necessary * to iterate through several calls to getPropertyInfo * @@ -169,6 +170,9 @@ public class Driver implements java.sql.Driver * tests, otherwise it is required to return false. JDBC compliance * requires full support for the JDBC API and full support for SQL 92 * Entry Level. + * + *

For PostgreSQL, this is not yet possible, as we are not SQL92 + * compliant (yet). */ public boolean jdbcCompliant() { @@ -185,7 +189,7 @@ public class Driver implements java.sql.Driver * @param url JDBC URL to parse * @param defaults Default properties * @return Properties with elements added from the url - * @throws SQLException + * @exception SQLException */ Properties parseURL(String url,Properties defaults) throws SQLException { @@ -280,7 +284,7 @@ public class Driver implements java.sql.Driver } /** - * Returns the hostname portion of the URL + * @return the hostname portion of the URL */ public String host() { @@ -288,8 +292,7 @@ public class Driver implements java.sql.Driver } /** - * Returns the port number portion of the URL - * or -1 if no port was specified + * @return the port number portion of the URL or -1 if no port was specified */ public int port() { @@ -297,7 +300,7 @@ public class Driver implements java.sql.Driver } /** - * Returns the database name of the URL + * @return the database name of the URL */ public String database() { @@ -305,7 +308,8 @@ public class Driver implements java.sql.Driver } /** - * Returns any property + * @return the value of any property specified in the URL or properties + * passed to connect(), or null if not found. */ public String property(String name) { diff --git a/src/interfaces/jdbc/postgresql/Field.java b/src/interfaces/jdbc/postgresql/Field.java index a4cc3c76e7..78553dd32e 100644 --- a/src/interfaces/jdbc/postgresql/Field.java +++ b/src/interfaces/jdbc/postgresql/Field.java @@ -6,10 +6,8 @@ import java.util.*; import postgresql.*; /** - * postgresql.Field is a class used to describe fields in a PostgreSQL ResultSet - * - * @version 1.0 15-APR-1997 - * @author Adrian Hall + * postgresql.Field is a class used to describe fields in a PostgreSQL + * ResultSet */ public class Field { @@ -22,7 +20,7 @@ public class Field String type_name = null;// The sql type name /** - * Construct a field based on the information fed to it. + * Construct a field based on the information fed to it. * * @param conn the connection this field came from * @param name the name of the field @@ -37,6 +35,14 @@ public class Field this.length = length; } + /** + * @return the oid of this Field's data type + */ + public int getOID() + { + return oid; + } + /** * the ResultSet and ResultMetaData both need to handle the SQL * type, which is gained from another query. Note that we cannot @@ -47,47 +53,77 @@ public class Field */ public int getSQLType() throws SQLException { - if (sql_type == -1) - { - ResultSet result = (postgresql.ResultSet)conn.ExecSQL("select typname from pg_type where oid = " + oid); - if (result.getColumnCount() != 1 || result.getTupleCount() != 1) - throw new SQLException("Unexpected return from query for type"); - result.next(); - type_name = result.getString(1); - if (type_name.equals("int2")) - sql_type = Types.SMALLINT; - else if (type_name.equals("int4")) - sql_type = Types.INTEGER; - else if (type_name.equals("int8")) - sql_type = Types.BIGINT; - else if (type_name.equals("cash")) - sql_type = Types.DECIMAL; - else if (type_name.equals("money")) - sql_type = Types.DECIMAL; - else if (type_name.equals("float4")) - sql_type = Types.REAL; - else if (type_name.equals("float8")) - sql_type = Types.DOUBLE; - else if (type_name.equals("bpchar")) - sql_type = Types.CHAR; - else if (type_name.equals("varchar")) - sql_type = Types.VARCHAR; - else if (type_name.equals("bool")) - sql_type = Types.BIT; - else if (type_name.equals("date")) - sql_type = Types.DATE; - else if (type_name.equals("time")) - sql_type = Types.TIME; - else if (type_name.equals("abstime")) - sql_type = Types.TIMESTAMP; - else if (type_name.equals("timestamp")) - sql_type = Types.TIMESTAMP; - else - sql_type = Types.OTHER; - } + if(sql_type == -1) { + ResultSet result = (postgresql.ResultSet)conn.ExecSQL("select typname from pg_type where oid = " + oid); + if (result.getColumnCount() != 1 || result.getTupleCount() != 1) + throw new SQLException("Unexpected return from query for type"); + result.next(); + sql_type = getSQLType(result.getString(1)); + result.close(); + } return sql_type; } + /** + * This returns the SQL type. It is called by the Field and DatabaseMetaData classes + * @param type_name PostgreSQL type name + * @return java.sql.Types value for oid + */ + public static int getSQLType(String type_name) + { + int sql_type = Types.OTHER; // default value + for(int i=0;i 0) + { + buf[p++] = (byte)(val & 0xff); + val >>= 8; + } + Send(buf); + } + /** * Send an array of bytes to the backend * @@ -295,6 +317,20 @@ public class PG_Stream } } + /** + * This flushes any pending output to the backend. It is used primarily + * by the Fastpath code. + * @exception SQLException if an I/O error occurs + */ + public void flush() throws SQLException + { + try { + pg_output.flush(); + } catch (IOException e) { + throw new SQLException("Error flushing output: " + e.toString()); + } + } + /** * Closes the connection * diff --git a/src/interfaces/jdbc/postgresql/PreparedStatement.java b/src/interfaces/jdbc/postgresql/PreparedStatement.java index 479f86eee9..659375064d 100644 --- a/src/interfaces/jdbc/postgresql/PreparedStatement.java +++ b/src/interfaces/jdbc/postgresql/PreparedStatement.java @@ -5,21 +5,20 @@ import java.math.*; import java.sql.*; import java.text.*; import java.util.*; +import postgresql.largeobject.*; +import postgresql.util.*; /** - * @version 6.3 15-APR-1997 - * @author Adrian HallPeter Mount - * * A SQL Statement is pre-compiled and stored in a PreparedStatement object. * This object can then be used to efficiently execute this statement multiple * times. * - * Note: The setXXX methods for setting IN parameter values must + *

Note: The setXXX methods for setting IN parameter values must * specify types that are compatible with the defined SQL type of the input * parameter. For instance, if the IN parameter has SQL type Integer, then * setInt should be used. * - * If arbitrary parameter type conversions are required, then the setObject + *

If arbitrary parameter type conversions are required, then the setObject * method should be used with a target SQL type. * * @see ResultSet @@ -33,10 +32,10 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta Connection connection; /** - * Constructor for the PreparedStatement class. Split the SQL statement - * into segments - separated by the arguments. When we rebuild the - * thing with the arguments, we can substitute the args and join the - * whole thing together. + * Constructor for the PreparedStatement class. + * Split the SQL statement into segments - separated by the arguments. + * When we rebuild the thing with the arguments, we can substitute the + * args and join the whole thing together. * * @param conn the instanatiating connection * @param sql the SQL statement with ? for IN markers @@ -125,7 +124,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta /** * Set a parameter to SQL NULL * - * Note: You must specify the parameters SQL type (although + *

Note: You must specify the parameters SQL type (although * PostgreSQL ignores it) * * @param parameterIndex the first parameter is 1, etc... @@ -254,35 +253,49 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setString(int parameterIndex, String x) throws SQLException { - StringBuffer b = new StringBuffer(); - int i; - - b.append('\''); - for (i = 0 ; i < x.length() ; ++i) - { - char c = x.charAt(i); - if (c == '\\' || c == '\'') - b.append((char)'\\'); - b.append(c); - } - b.append('\''); - set(parameterIndex, b.toString()); + // if the passed string is null, then set this column to null + if(x==null) + set(parameterIndex,"null"); + else { + StringBuffer b = new StringBuffer(); + int i; + + b.append('\''); + for (i = 0 ; i < x.length() ; ++i) + { + char c = x.charAt(i); + if (c == '\\' || c == '\'') + b.append((char)'\\'); + b.append(c); + } + b.append('\''); + set(parameterIndex, b.toString()); + } } - /** - * Set a parameter to a Java array of bytes. The driver converts this - * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's - * size relative to the driver's limits on VARBINARYs) when it sends - * it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setBytes(int parameterIndex, byte x[]) throws SQLException - { - throw new SQLException("Binary Data not supported"); - } + /** + * Set a parameter to a Java array of bytes. The driver converts this + * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's + * size relative to the driver's limits on VARBINARYs) when it sends + * it to the database. + * + *

Implementation note: + *
With postgresql, this creates a large object, and stores the + * objects oid in this column. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBytes(int parameterIndex, byte x[]) throws SQLException + { + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + lob.write(x); + lob.close(); + setInt(parameterIndex,oid); + } /** * Set a parameter to a java.sql.Date value. The driver converts this @@ -294,16 +307,30 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setDate(int parameterIndex, java.sql.Date x) throws SQLException { - SimpleDateFormat df = new SimpleDateFormat(connection.europeanDates?"''dd-MM-yyyy''":"''MM-dd-yyyy''"); - - set(parameterIndex, df.format(x)); + SimpleDateFormat df = new SimpleDateFormat("''"+connection.getDateStyle()+"''"); + + // Ideally the following should work: + // + // set(parameterIndex, df.format(x)); + // + // however, SimpleDateFormat seems to format a date to the previous + // day. So a fix (for now) is to add a day before formatting. + // This needs more people to confirm this is really happening, or + // possibly for us to implement our own formatting code. + // + // I've tested this with the Linux jdk1.1.3 and the Win95 JRE1.1.5 + // + set(parameterIndex, df.format(new java.util.Date(x.getTime()+DAY))); } + + // This equates to 1 day + private static final int DAY = 86400000; /** * Set a parameter to a java.sql.Time value. The driver converts * this to a SQL TIME value when it sends it to the database. * - * @param parameterIndex the first parameter is 1... + * @param parameterIndex the first parameter is 1...)); * @param x the parameter value * @exception SQLException if a database access error occurs */ @@ -332,7 +359,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * end-of-file. The JDBC driver will do any necessary conversion from * ASCII to the database char format. * - * Note: This stream object can either be a standard Java + *

Note: This stream object can either be a standard Java * stream object or your own subclass that implements the standard * interface. * @@ -353,7 +380,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * end-of-file. The JDBC driver will do any necessary conversion from * UNICODE to the database char format. * - * Note: This stream object can either be a standard Java + *

Note: This stream object can either be a standard Java * stream object or your own subclass that implements the standard * interface. * @@ -372,7 +399,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * JDBC will read the data from the stream as needed, until it reaches * end-of-file. * - * Note: This stream object can either be a standard Java + *

Note: This stream object can either be a standard Java * stream object or your own subclass that implements the standard * interface. * @@ -406,10 +433,10 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * Set the value of a parameter using an object; use the java.lang * equivalent objects for integral values. * - * The given Java object will be converted to the targetSqlType before + *

The given Java object will be converted to the targetSqlType before * being sent to the database. * - * note that this method may be used to pass database-specific + *

note that this method may be used to pass database-specific * abstract data types. This is done by using a Driver-specific * Java type and using a targetSqlType of java.sql.Types.OTHER * @@ -450,7 +477,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta case Types.TIMESTAMP: setTimestamp(parameterIndex, (Timestamp)x); case Types.OTHER: - setString(parameterIndex, ((PG_Object)x).value); + setString(parameterIndex, ((PGobject)x).getValue()); default: throw new SQLException("Unknown Types value"); } @@ -485,8 +512,8 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta setTimestamp(parameterIndex, (Timestamp)x); else if (x instanceof Boolean) setBoolean(parameterIndex, ((Boolean)x).booleanValue()); - else if (x instanceof PG_Object) - setString(parameterIndex, ((PG_Object)x).value); + else if (x instanceof PGobject) + setString(parameterIndex, ((PGobject)x).getValue()); else throw new SQLException("Unknown object type"); } diff --git a/src/interfaces/jdbc/postgresql/ResultSet.java b/src/interfaces/jdbc/postgresql/ResultSet.java index 017b0ad292..f8eea22595 100644 --- a/src/interfaces/jdbc/postgresql/ResultSet.java +++ b/src/interfaces/jdbc/postgresql/ResultSet.java @@ -6,31 +6,32 @@ import java.math.*; import java.text.*; import java.util.*; import java.sql.*; -import postgresql.*; +import postgresql.largeobject.*; +import postgresql.util.*; /** * A ResultSet provides access to a table of data generated by executing a * Statement. The table rows are retrieved in sequence. Within a row its * column values can be accessed in any order. * - * A ResultSet maintains a cursor pointing to its current row of data. + *

A ResultSet maintains a cursor pointing to its current row of data. * Initially the cursor is positioned before the first row. The 'next' * method moves the cursor to the next row. * - * The getXXX methods retrieve column values for the current row. You can + *

The getXXX methods retrieve column values for the current row. You can * retrieve values either using the index number of the column, or by using * the name of the column. In general using the column index will be more * efficient. Columns are numbered from 1. * - * For maximum portability, ResultSet columns within each row should be read + *

For maximum portability, ResultSet columns within each row should be read * in left-to-right order and each column should be read only once. * - * For the getXXX methods, the JDBC driver attempts to convert the underlying - * data to the specified Java type and returns a suitable Java value. See the - * JDBC specification for allowable mappings from SQL types to Java types with - * the ResultSet getXXX methods. + *

For the getXXX methods, the JDBC driver attempts to convert the + * underlying data to the specified Java type and returns a suitable Java + * value. See the JDBC specification for allowable mappings from SQL types + * to Java types with the ResultSet getXXX methods. * - * Column names used as input to getXXX methods are case insenstive. When + *

Column names used as input to getXXX methods are case insenstive. When * performing a getXXX using a column name, if several columns have the same * name, then the value of the first matching column will be returned. The * column name option is designed to be used when column names are used in the @@ -39,11 +40,11 @@ import postgresql.*; * the programmer to guarentee that they actually refer to the intended * columns. * - * A ResultSet is automatically closed by the Statement that generated it + *

A ResultSet is automatically closed by the Statement that generated it * when that Statement is closed, re-executed, or is used to retrieve the * next result from a sequence of multiple results. * - * The number, types and properties of a ResultSet's columns are provided by + *

The number, types and properties of a ResultSet's columns are provided by * the ResultSetMetaData object returned by the getMetaData method. * * @see ResultSetMetaData @@ -92,7 +93,7 @@ public class ResultSet implements java.sql.ResultSet * the first call to next makes the first row the current row; * the second call makes the second row the current row, etc. * - * If an input stream from the previous row is open, it is + *

If an input stream from the previous row is open, it is * implicitly closed. The ResultSet's warning chain is cleared * when a new row is read * @@ -114,7 +115,7 @@ public class ResultSet implements java.sql.ResultSet * when it is automatically closed. The close method provides this * immediate release. * - * Note: A ResultSet is automatically closed by the Statement + *

Note: A ResultSet is automatically closed by the Statement * the Statement that generated it when that Statement is closed, * re-executed, or is used to retrieve the next result from a sequence * of multiple results. A ResultSet is also automatically closed @@ -150,11 +151,17 @@ public class ResultSet implements java.sql.ResultSet */ public String getString(int columnIndex) throws SQLException { - byte[] bytes = getBytes(columnIndex); - - if (bytes == null) + //byte[] bytes = getBytes(columnIndex); + // + //if (bytes == null) + //return null; + //return new String(bytes); + if (columnIndex < 1 || columnIndex > fields.length) + throw new SQLException("Column Index out of range"); + wasNullFlag = (this_row[columnIndex - 1] == null); + if(wasNullFlag) return null; - return new String(bytes); + return new String(this_row[columnIndex - 1]); } /** @@ -347,8 +354,14 @@ public class ResultSet implements java.sql.ResultSet } /** - * Get the value of a column in the current row as a Java byte array - * The bytes represent the raw values returned by the driver. + * Get the value of a column in the current row as a Java byte array. + * + *

In normal use, the bytes represent the raw values returned by the + * backend. However, if the column is an OID, then it is assumed to + * refer to a Large Object, and that object is returned as a byte array. + * + *

Be warned If the large object is huge, then you may run out + * of memory. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the result @@ -360,6 +373,17 @@ public class ResultSet implements java.sql.ResultSet if (columnIndex < 1 || columnIndex > fields.length) throw new SQLException("Column Index out of range"); wasNullFlag = (this_row[columnIndex - 1] == null); + + // Handle OID's as BLOBS + if(!wasNullFlag) + if( fields[columnIndex - 1].getOID() == 26) { + LargeObjectManager lom = connection.getLargeObjectAPI(); + LargeObject lob = lom.open(getInt(columnIndex)); + byte buf[] = lob.read(lob.size()); + lob.close(); + return buf; + } + return this_row[columnIndex - 1]; } @@ -374,7 +398,7 @@ public class ResultSet implements java.sql.ResultSet public java.sql.Date getDate(int columnIndex) throws SQLException { String s = getString(columnIndex); - SimpleDateFormat df = new SimpleDateFormat(connection.europeanDates?"dd-MM-yyyy":"MM-dd-yyyy"); + SimpleDateFormat df = new SimpleDateFormat(connection.getDateStyle()); try { return new java.sql.Date(df.parse(s).getTime()); } catch (ParseException e) { @@ -449,13 +473,13 @@ public class ResultSet implements java.sql.ResultSet * The JDBC driver will do any necessary conversion from the * database format into ASCII. * - * Note: All the data in the returned stream must be read + *

Note: All the data in the returned stream must be read * prior to getting the value of any other column. The next call * to a get method implicitly closes the stream. Also, a stream * may return 0 for available() whether there is data available * or not. * - * We implement an ASCII stream as a Binary stream - we should really + *

We implement an ASCII stream as a Binary stream - we should really * do the data conversion, but I cannot be bothered to implement this * right now. * @@ -494,8 +518,8 @@ public class ResultSet implements java.sql.ResultSet * * @param columnIndex the first column is 1, the second is 2... * @return a Java InputStream that delivers the database column value - * as a stream of two byte Unicode characters. If the value is - * SQL NULL, then the result is null + * as a stream of bytes. If the value is SQL NULL, then the result + * is null * @exception SQLException if a database access error occurs * @see getAsciiStream * @see getUnicodeStream @@ -603,10 +627,10 @@ public class ResultSet implements java.sql.ResultSet * returned. Subsequent ResultSet warnings will be chained * to this SQLWarning. * - * The warning chain is automatically cleared each time a new + *

The warning chain is automatically cleared each time a new * row is read. * - * Note: This warning chain only covers warnings caused by + *

Note: This warning chain only covers warnings caused by * ResultSet methods. Any warnings caused by statement methods * (such as reading OUT parameters) will be chained on the * Statement object. @@ -633,16 +657,16 @@ public class ResultSet implements java.sql.ResultSet /** * Get the name of the SQL cursor used by this ResultSet * - * In SQL, a result table is retrieved though a cursor that is + *

In SQL, a result table is retrieved though a cursor that is * named. The current row of a result can be updated or deleted * using a positioned update/delete statement that references * the cursor name. * - * JDBC supports this SQL feature by providing the name of the + *

JDBC supports this SQL feature by providing the name of the * SQL cursor used by a ResultSet. The current row of a ResulSet * is also the current row of this SQL cursor. * - * Note: If positioned update is not supported, a SQLException + *

Note: If positioned update is not supported, a SQLException * is thrown. * * @return the ResultSet's SQL cursor name. @@ -668,12 +692,12 @@ public class ResultSet implements java.sql.ResultSet /** * Get the value of a column in the current row as a Java object * - * This method will return the value of the given column as a + *

This method will return the value of the given column as a * Java object. The type of the Java object will be the default * Java Object type corresponding to the column's SQL type, following * the mapping specified in the JDBC specification. * - * This method may also be used to read database specific abstract + *

This method may also be used to read database specific abstract * data types. * * @param columnIndex the first column is 1, the second is 2... @@ -714,19 +738,19 @@ public class ResultSet implements java.sql.ResultSet case Types.TIMESTAMP: return getTimestamp(columnIndex); default: - return new PG_Object(field.getTypeName(), getString(columnIndex)); + return connection.getObject(field.getTypeName(), getString(columnIndex)); } } /** * Get the value of a column in the current row as a Java object * - * This method will return the value of the given column as a + *

This method will return the value of the given column as a * Java object. The type of the Java object will be the default * Java Object type corresponding to the column's SQL type, following * the mapping specified in the JDBC specification. * - * This method may also be used to read database specific abstract + *

This method may also be used to read database specific abstract * data types. * * @param columnName is the SQL name of the column @@ -816,8 +840,6 @@ public class ResultSet implements java.sql.ResultSet * particular, we need to know the number of rows and the * number of columns. Rows are also known as Tuples * - * getTupleCount returns the number of rows - * * @return the number of rows */ public int getTupleCount() diff --git a/src/interfaces/jdbc/postgresql/ResultSetMetaData.java b/src/interfaces/jdbc/postgresql/ResultSetMetaData.java index 7d2c1ceaf3..c4e54dbefa 100644 --- a/src/interfaces/jdbc/postgresql/ResultSetMetaData.java +++ b/src/interfaces/jdbc/postgresql/ResultSetMetaData.java @@ -6,9 +6,6 @@ import java.util.*; import postgresql.*; /** - * @version 1.0 15-APR-1997 - * @author Adrian Hall - * * A ResultSetMetaData object can be used to find out about the types and * properties of the columns in a ResultSet * diff --git a/src/interfaces/jdbc/postgresql/Statement.java b/src/interfaces/jdbc/postgresql/Statement.java index 464a263621..177b8189f6 100644 --- a/src/interfaces/jdbc/postgresql/Statement.java +++ b/src/interfaces/jdbc/postgresql/Statement.java @@ -3,13 +3,10 @@ package postgresql; import java.sql.*; /** - * @version 1.0 15-APR-1997 - * @author Adrian Hall - * * A Statement object is used for executing a static SQL statement and * obtaining the results produced by it. * - * Only one ResultSet per Statement can be open at any point in time. + *

Only one ResultSet per Statement can be open at any point in time. * Therefore, if the reading of one ResultSet is interleaved with the * reading of another, each must have been generated by different * Statements. All statement execute methods implicitly close a @@ -23,7 +20,6 @@ public class Statement implements java.sql.Statement Connection connection; // The connection who created us ResultSet result = null; // The current results SQLWarning warnings = null; // The warnings chain. - int maxrows = 0; // maximum no. of rows; 0 = unlimited int timeout = 0; // The timeout for a query (not used) boolean escapeProcessing = true;// escape processing flag @@ -78,7 +74,7 @@ public class Statement implements java.sql.Statement * for this to happen when it is automatically closed. The * close method provides this immediate release. * - * Note: A Statement is automatically closed when it is + *

Note: A Statement is automatically closed when it is * garbage collected. When a Statement is closed, its current * ResultSet, if one exists, is also closed. * @@ -126,7 +122,7 @@ public class Statement implements java.sql.Statement */ public int getMaxRows() throws SQLException { - return maxrows; + return connection.maxrows; } /** @@ -138,7 +134,7 @@ public class Statement implements java.sql.Statement */ public void setMaxRows(int max) throws SQLException { - maxrows = max; + connection.maxrows = max; } /** @@ -197,10 +193,10 @@ public class Statement implements java.sql.Statement * chain. Subsequent Statement warnings will be chained to this * SQLWarning. * - * The Warning chain is automatically cleared each time a statement + *

The Warning chain is automatically cleared each time a statement * is (re)executed. * - * Note: If you are processing a ResultSet then any warnings + *

Note: If you are processing a ResultSet then any warnings * associated with ResultSet reads will be chained on the ResultSet * object. * @@ -231,12 +227,12 @@ public class Statement implements java.sql.Statement * doesn't support positioned update/delete, this method is a * no-op. * - * Note: By definition, positioned update/delete execution + *

Note: By definition, positioned update/delete execution * must be done by a different Statement than the one which * generated the ResultSet being used for positioning. Also, cursor * names must be unique within a Connection. * - * We throw an additional constriction. There can only be one + *

We throw an additional constriction. There can only be one * cursor active at any one time. * * @param name the new cursor name