From 45b5d792afa60ae24d57cdceaaec0e03cf6abb2a Mon Sep 17 00:00:00 2001 From: Peter Mount Date: Thu, 18 Jan 2001 14:50:15 +0000 Subject: [PATCH] Thu Jan 18 12:24:00 GMT 2001 peter@retep.org.uk - These methods in org.postgresql.jdbc2.ResultSet are now implemented: getBigDecimal(int) ie: without a scale (why did this get missed?) getBlob(int) getCharacterStream(int) getConcurrency() getDate(int,Calendar) getFetchDirection() getFetchSize() getTime(int,Calendar) getTimestamp(int,Calendar) getType() NB: Where int represents the column name, the associated version taking a String were already implemented by calling the int version. - These methods no longer throw the not implemented but the new noupdate error. This is in preparation for the Updateable ResultSet support which will overide these methods by extending the existing class to implement that functionality, but needed to show something other than notimplemented: cancelRowUpdates() deleteRow() - Added new error message into errors.properties "postgresql.noupdate" This is used by jdbc2.ResultSet when an update method is called and the ResultSet is not updateable. A new method notUpdateable() has been added to that class to throw this exception, keeping the binary size down. - Added new error message into errors.properties "postgresql.psqlnotimp" This is used instead of unimplemented when it's a feature in the backend that is preventing this method from being implemented. - Removed getKeysetSize() as its not part of the ResultSet API Thu Jan 18 09:46:00 GMT 2001 peter@retep.org.uk - Applied modified patch from Richard Bullington-McGuire . I had to modify it as some of the code patched now exists in different classes, and some of it actually patched obsolete code. Wed Jan 17 10:19:00 GMT 2001 peter@retep.org.uk - Updated Implementation to include both ANT & JBuilder - Updated README to reflect the changes since 7.0 - Created jdbc.jpr file which allows JBuilder to be used to edit the source. JBuilder _CAN_NOT_ be used to compile. You must use ANT for that. It's only to allow JBuilders syntax checking to improve the drivers source. Refer to Implementation for more details --- build.xml | 94 ++++ contrib/retep/CHANGELOG | 0 contrib/retep/Implementation | 0 contrib/retep/README | 0 contrib/retep/build.xml | 60 +++ contrib/retep/retep.jpx | 44 ++ .../uk/org/retep/xml/parser/TagHandler.java | 202 +++++++++ .../uk/org/retep/xml/parser/TagListener.java | 30 ++ src/interfaces/jdbc/CHANGELOG | 72 ++- src/interfaces/jdbc/Implementation | 129 ++++-- src/interfaces/jdbc/Makefile | 29 +- src/interfaces/jdbc/README | 56 ++- src/interfaces/jdbc/build.xml | 54 ++- src/interfaces/jdbc/jdbc.jpx | 31 ++ .../jdbc/org/postgresql/Connection.java | 185 ++++---- .../jdbc/org/postgresql/PG_Stream.java | 225 ++------- .../org/postgresql/core/BytePoolDim1.java | 95 ++++ .../org/postgresql/core/BytePoolDim2.java | 62 +++ .../jdbc/org/postgresql/core/MemoryPool.java | 18 + .../jdbc/org/postgresql/core/ObjectPool.java | 48 ++ .../org/postgresql/core/SimpleObjectPool.java | 97 ++++ .../jdbc/org/postgresql/errors.properties | 2 + .../jdbc/org/postgresql/jdbc1/Statement.java | 22 +- .../jdbc/org/postgresql/jdbc2/ResultSet.java | 426 ++++++++++-------- .../org/postgresql/util/PSQLException.java | 28 +- 25 files changed, 1459 insertions(+), 550 deletions(-) create mode 100644 build.xml create mode 100644 contrib/retep/CHANGELOG create mode 100644 contrib/retep/Implementation create mode 100644 contrib/retep/README create mode 100644 contrib/retep/build.xml create mode 100644 contrib/retep/retep.jpx create mode 100644 contrib/retep/uk/org/retep/xml/parser/TagHandler.java create mode 100644 contrib/retep/uk/org/retep/xml/parser/TagListener.java create mode 100644 src/interfaces/jdbc/jdbc.jpx create mode 100644 src/interfaces/jdbc/org/postgresql/core/BytePoolDim1.java create mode 100644 src/interfaces/jdbc/org/postgresql/core/BytePoolDim2.java create mode 100644 src/interfaces/jdbc/org/postgresql/core/MemoryPool.java create mode 100644 src/interfaces/jdbc/org/postgresql/core/ObjectPool.java create mode 100644 src/interfaces/jdbc/org/postgresql/core/SimpleObjectPool.java diff --git a/build.xml b/build.xml new file mode 100644 index 0000000000..c359e97ee4 --- /dev/null +++ b/build.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/retep/CHANGELOG b/contrib/retep/CHANGELOG new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contrib/retep/Implementation b/contrib/retep/Implementation new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contrib/retep/README b/contrib/retep/README new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contrib/retep/build.xml b/contrib/retep/build.xml new file mode 100644 index 0000000000..cf377d3bad --- /dev/null +++ b/contrib/retep/build.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/retep/retep.jpx b/contrib/retep/retep.jpx new file mode 100644 index 0000000000..47f5429624 --- /dev/null +++ b/contrib/retep/retep.jpx @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/retep/uk/org/retep/xml/parser/TagHandler.java b/contrib/retep/uk/org/retep/xml/parser/TagHandler.java new file mode 100644 index 0000000000..add0a61274 --- /dev/null +++ b/contrib/retep/uk/org/retep/xml/parser/TagHandler.java @@ -0,0 +1,202 @@ +package uk.org.retep.xml.parser; + +import java.io.CharArrayWriter; +import java.io.IOException; +import java.util.List; +import java.util.Iterator; +import java.util.Map; +import java.util.HashSet; +import java.util.ArrayList; +import java.util.HashMap; +import org.xml.sax.AttributeList; +import org.xml.sax.HandlerBase; +import org.xml.sax.InputSource; +import org.xml.sax.Parser; +import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +/** + * This class implements the base of the XML handler. You create an instance, + * register classes (who implement TagListener) that are interested in the tags + * and pass it to SAX. + * + *

Or you create an instance, register the TagListeners and use the getParser() + * method to create a Parser. Then start parsing by calling it's parse() method. + */ + +public class TagHandler extends HandlerBase { + + /** + * The current active level + */ + private int level; + + /** + * cache used to handle nesting of tags + */ + private List contents; + + /** + * cache used to handle nesting of tags + */ + private List tags; + + /** + * cache used to handle nesting of tags + */ + private List args; + + // Current active content writer + private CharArrayWriter content; + + // List of TagListener's who want to be fed data + private HashSet tagListeners; + + /** + * default constructor + */ + public TagHandler() { + level=0; + contents = new ArrayList(); + tags = new ArrayList(); + args = new ArrayList(); + tagListeners = new HashSet(); + } + + /** + * Called by SAX when a tag is begun. This simply creates a new level in the + * cache and stores the parameters and tag name in there. + */ + public void startElement(String p0, AttributeList p1) throws SAXException { + + // Now move up and fetch a CharArrayWriter from the cache + // creating if this is the first time at this level + if(contents.size()<=level) { + contents.add(new CharArrayWriter()); + tags.add(p0); + args.add(new HashMap()); + } + + content=(CharArrayWriter) contents.get(level); + content.reset(); + + // Also cache the tag's text and argument list + tags.set(level,p0); + + HashMap h = (HashMap) args.get(level); + h.clear(); + for(int i=p1.getLength()-1;i>-1;i--) { + h.put(p1.getName(i),p1.getValue(i)); + } + + // Now notify any TagListeners + Iterator it = tagListeners.iterator(); + while(it.hasNext()) + ( (TagListener) it.next() ).tagStart(level,p0,h); + + // Now move up a level + level++; + } + + /** + * This is called by SAX at the end of a tag. This calls handleTag() and then + * raises the level, so that the previous parent tag may continue. + */ + public void endElement(String p0) throws SAXException { + // move up a level retrieving that level's current content + // Now this exception should never occur as the underlying parser should + // actually trap it. + if(level<1) + throw new SAXException("Already at top level?"); + level--; + + // Now notify any TagListeners + Iterator it = tagListeners.iterator(); + while(it.hasNext()) + ( (TagListener) it.next() ).tagContent(content); + + // allows large content to be released early + content.reset(); + + // Now reset content to the previous level + content=(CharArrayWriter) contents.get(level); + } + + /** + * Called by SAX so that content between the start and end tags are captured. + */ + public void characters(char[] p0, int p1, int p2) throws SAXException { + content.write(p0,p1,p2); + } + + /** + * Adds a TagListener so that it is notified of tags as they are processed. + * @param handler TagListener to add + */ + public void addTagListener(TagListener h) { + tagListeners.add(h); + } + + /** + * Removes the TagListener so it no longer receives notifications of tags + */ + public void removeTagListener(TagListener h) { + tagListeners.remove(h); + } + + /** + * This method returns a org.xml.sax.Parser object that will parse the + * contents of a URI. + * + *

Normally you would call this method, then call the parse(uri) method of + * the returned object. + * @return org.xml.sax.Parser object + */ + public Parser getParser() + throws SAXException + { + try { + SAXParserFactory spf = SAXParserFactory.newInstance(); + + String validation = System.getProperty ("javax.xml.parsers.validation", "false"); + if (validation.equalsIgnoreCase("true")) + spf.setValidating (true); + + SAXParser sp = spf.newSAXParser(); + Parser parser = sp.getParser (); + + parser.setDocumentHandler(this); + + return(parser); + } catch(ParserConfigurationException pce) { + throw new SAXException(pce.toString()); + } + } + + /** + * This method will parse the specified URI. + * + *

Internally this is the same as getParser().parse(uri); + * @param uri The URI to parse + */ + public void parse(String uri) + throws IOException, SAXException + { + getParser().parse(uri); + } + + /** + * This method will parse the specified InputSource. + * + *

Internally this is the same as getParser().parse(is); + * @param is The InputSource to parse + */ + public void parse(InputSource is) + throws IOException, SAXException + { + getParser().parse(is); + } + +} \ No newline at end of file diff --git a/contrib/retep/uk/org/retep/xml/parser/TagListener.java b/contrib/retep/uk/org/retep/xml/parser/TagListener.java new file mode 100644 index 0000000000..24146b3e24 --- /dev/null +++ b/contrib/retep/uk/org/retep/xml/parser/TagListener.java @@ -0,0 +1,30 @@ +package uk.org.retep.xml.parser; + +import java.util.HashMap; +import java.io.CharArrayWriter; + +/** + * This interface defines the methods a class needs to implement if it wants the + * xml parser to notify it of any xml tags. + */ + +public interface TagListener { + /** + * This is called when a tag has just been started. + *

NB: args is volatile, so if you use it beyond the lifetime of + * this call, then you must make a copy of the HashMap (and not use simply + * store this HashMap). + * @param level The number of tags above this + * @param tag The tag name + * @param args A HashMap of any arguments + */ + public void tagStart(int level,String tag,HashMap args); + /** + * This method is called by ContHandler to process a tag once it has been + * fully processed. + *

NB: content is volatile, so you must copy its contents if you use + * it beyond the lifetime of this call. + * @param content CharArrayWriter containing the content of the tag. + */ + public void tagContent(CharArrayWriter content); +} \ No newline at end of file diff --git a/src/interfaces/jdbc/CHANGELOG b/src/interfaces/jdbc/CHANGELOG index 6afd3c6f25..e9242a0b57 100644 --- a/src/interfaces/jdbc/CHANGELOG +++ b/src/interfaces/jdbc/CHANGELOG @@ -1,3 +1,49 @@ +Thu Jan 18 12:24:00 GMT 2001 peter@retep.org.uk + - These methods in org.postgresql.jdbc2.ResultSet are now implemented: + getBigDecimal(int) ie: without a scale (why did this get missed?) + getBlob(int) + getCharacterStream(int) + getConcurrency() + getDate(int,Calendar) + getFetchDirection() + getFetchSize() + getTime(int,Calendar) + getTimestamp(int,Calendar) + getType() + NB: Where int represents the column name, the associated version + taking a String were already implemented by calling the int + version. + - These methods no longer throw the not implemented but the new noupdate + error. This is in preparation for the Updateable ResultSet support + which will overide these methods by extending the existing class to + implement that functionality, but needed to show something other than + notimplemented: + cancelRowUpdates() + deleteRow() + - Added new error message into errors.properties "postgresql.noupdate" + This is used by jdbc2.ResultSet when an update method is called and + the ResultSet is not updateable. A new method notUpdateable() has been + added to that class to throw this exception, keeping the binary size + down. + - Added new error message into errors.properties "postgresql.psqlnotimp" + This is used instead of unimplemented when it's a feature in the + backend that is preventing this method from being implemented. + - Removed getKeysetSize() as its not part of the ResultSet API + +Thu Jan 18 09:46:00 GMT 2001 peter@retep.org.uk + - Applied modified patch from Richard Bullington-McGuire + . I had to modify it as some of the code + patched now exists in different classes, and some of it actually + patched obsolete code. + +Wed Jan 17 10:19:00 GMT 2001 peter@retep.org.uk + - Updated Implementation to include both ANT & JBuilder + - Updated README to reflect the changes since 7.0 + - Created jdbc.jpr file which allows JBuilder to be used to edit the + source. JBuilder _CAN_NOT_ be used to compile. You must use ANT for + that. It's only to allow JBuilders syntax checking to improve the + drivers source. Refer to Implementation for more details + Wed Dec 20 16:19:00 GMT 2000 peter@retep.org.uk - Finished build.xml and updated Driver.java.in and buildDriver to match how Makefile and ANT operate. @@ -12,23 +58,23 @@ Tue Oct 17 15:35:00 BST 2000 petermount@maidstone.gov.uk - Changed getTimestamp() again. This time Michael Stephenson's solution looked far better than the original solution put in June. - + Tue Oct 10 13:12:00 BST 2000 peter@retep.org.uk - DatabaseMetaData.supportsAlterTableWithDropColumn() as psql doesn't support dropping of individual columns - Merged in some last patches. Only 1 left, which may not be compatible with jdbc1 - Merged in my old retepsql project. Makefile now includes it. - + Mon Oct 02 12:30:00 BST 2000 peter@retep.org.uk - Merged in byte[] array allocation changes submitted by Gunnar R|nning - + Mon Sep 25 14:22:00 BST 2000 peter@retep.org.uk - Removed the DriverClass kludge. Now the org.postgresql.Driver class is compiled from a template file, and now has both the connection class (ie jdbc1/jdbc2) and the current version's from Makefile.global - + Thu Jul 20 16:30:00 BST 2000 petermount@it.maidstone.gov.uk - Fixed DatabaseMetaData.getTableTypes() @@ -56,24 +102,24 @@ Mon May 15 22:30:00 BST 2000 peter@retep.org.uk Thu May 04 11:38:00 BST 2000 petermount@it.maidstone.gov.uk - Corrected incorrect date in CHANGELOG - Fixed the ImageViewer example - + Wed May 03 16:47:00 BST 2000 petermount@it.maidstone.gov.uk - Fixed the Makefile so that postgresql.jar is built everytime the jdbc1 or jdbc2 rules are called. - Fixed the threadsafe example. It had problems with autocommit - + Wed May 03 14:32:00 BST 2000 petermount@it.maidstone.gov.uk - Rewrote the README file (the old one was 18 months old!) - Added @deprecated tags to org.postgresql.jdbc2.ResultSet to clear some warnings issued during compilation. - + Wed Apr 12 22:14:00 BST 2000 peter@retep.org.uk - Implemented the JDBC2 Blob interface, and ResultSet.getBlob(). - + Wed Apr 12 20:20:00 BST 2000 peter@retep.org.uk - Fixed bug in ResultSet.absolute(). Negative rows are now supported. - Implemented ResultSet.relative(), afterLast(). - + Tue Feb 1 21:40:00 GMT 2000 peter@retep.org.uk - Finally imported the contributed javax extensions by Assaf Arkin arkin@exoffice.com @@ -109,7 +155,7 @@ Wed Aug 4 00:25:18 CEST 1999 jens@jens.de Mon Aug 2 03:29:35 CEST 1999 jens@jens.de - fixed bug in DatabaseMetaData.getPrimaryKeys() - + Sun Aug 1 18:05:42 CEST 1999 jens@jens.de - added support for getTransactionIsolation and setTransactionIsolation @@ -212,7 +258,7 @@ Web Oct 7 22:00:00 BST 1998 FastPath.fastpath() methods. They now lock against the PG_Steam object for the connection, which now provides full Thread Safety. - Reposted ChangeLog as it's missing from CVS. - + Modifications done since 6.3.2 was released and Sun Aug 30 11:33:06 BST 1998 - Fixed PreparedStatement.setObject as it didn't handle shorts @@ -228,7 +274,7 @@ Modifications done since 6.3.2 was released and Sun Aug 30 11:33:06 BST 1998 properties are missing, as they are required for JDBC to work. This occasionally occurs when the client uses the properties version of getConnection(), and is a common question on the email lists. - + Sun Aug 30 11:33:06 BST 1998 - Created ChangeLog file, and entered stuff done since 6.3.2 and today @@ -290,4 +336,4 @@ Sun Aug 30 11:33:06 BST 1998 and getSchemaName(). - Created new class postgresql.util.PGmoney to map the money type - Created new class postgresql.geometric.PGline to map the line type - + diff --git a/src/interfaces/jdbc/Implementation b/src/interfaces/jdbc/Implementation index 05ceee2a3c..28ab6c1719 100644 --- a/src/interfaces/jdbc/Implementation +++ b/src/interfaces/jdbc/Implementation @@ -1,28 +1,87 @@ This short document is provided to help programmers through the internals of the PostgreSQL JDBC driver. +Last update: January 17 2001 peter@retep.org.uk + +build.xml +--------- + +As of 7.1, we now use the ANT build tool to build the driver. ANT is part of +the Apache/Jakarta project, and provides far superior build capabilities. You +can find ANT from http://jakarta.apache.org/ant/index.html and being pure java +it will run on any java platform. + +So far I've tested it under JDK1.2.x & JDK1.3 (both Linux & NT) but not yet with +JDK1.1.8. Because of the latter the Makefile still works for now, but should be +gone for 7.2. + +Anyhow, to build, simply type ant and the .jar file will be created and put into +the jars directory. + +Tip: If you run ant from the sources root directory (ie: where the configure +script is located) you will find another build.xml file. It is advised to run +ant from that directory as it will then compile some auxilary Java/JDBC +utilities that are located under the /contrib/retep directory. + Makefile -------- -All compilation must be done by using Make. This is because there are two -versions of the driver, one for JDBC1 (for JDK 1.1.x) and the other for JDBC2 -(for JDK 1.2 or later). The makefile determines which version to compile by -using a helper class makeVersion. This class is only used by make, and is not -stored in the Jar file. +Prior to 7.1, all compilation must be done by using Make. This is because there +are three versions of the driver, one for JDBC1 (for JDK 1.1.x) and the others +for JDBC2 (for JDK 1.2 or later, one standard and one enterprise). -Note: It is not sufficient to simply call javac on postgresql/Driver.java as - some classes are dynamically loaded, so javac will not compile them. +As of 7.1, ANT is the build tool of choice. Just compare Makefile and build.xml +to see why! Make just isn't suited to Java. + +Building with just the JDK +-------------------------- + +This is not advised, simply because you have to make sure you include the +correct classes, and the fact that org.postgresql.Driver is built on the fly. +Also, javac won't pick up all the classes because some (org.postgresql.geometric +for example) are loaded dynamically. + +org/postgresql/Driver.java.in +----------------------------- + +Because there are three versions of the driver, the org.postgresql.Driver class +is built dynamically. To build correctly ANT copies the Driver.java.in file to +Driver.java replacing certain values according to the required driver. + +The replaced values are of the format %VALUE%, ie: %MAJORVERSION% is replaced +with 7 in the 7.1 version of the driver. postgresql.jar -------------- -This jar file is produced by make, and contains the driver for your JDK -platform. +This jar file is produced by ANT, and contains the driver for your JDK platform. -Note: It is possible to compile the driver under say JDK1.1.7, then under - JDK 1.2. Because make doesn't remove the old classes before compiling, - jar will simply package both sets together. When the driver is loaded, - the postgresql.Driver class will sort out which set of classes to use. +If you downloaded a precompiled binary from the web, you may find that the +jar file will be named differently. These are identical to this file but are +named according to the backend and jdk versions. + +The naming convention is of the form: jdbc-#.#-#.##.jar + +ie: for 7.1 + jdbc-7.1-1.1.jar JDBC Driver for JDK1.1.8 + jdbc-7.1-1.2.jar JDBC Driver for JDK1.2 & JDK1.3 + jdbc-7.1-1.2ent.jar JDBC Driver for JDK1.2 & JDK1.3 Enterprise Editions + +If in the future there are any 1.3 specific classes then there will be two new +jar files. + +Note: All the precompiled binaries are built under Linux. + +jdbc.jpx +-------- + +This is a JBuilder4 project file. It's here to allow JBuilder to be used to +develop the driver. Mainly for it's Editor's features like syntax checking and +auto-completion etc. + +IMPORTANT: You CAN NOT build the driver from within JBuilder. You must use ANT. + This is because of the three versions of the JDK. If you try to use + JBuilder, it will try to build everything, and it will just not work. Importing packages ------------------ @@ -50,16 +109,17 @@ Package Layout The driver is split into several packages: -postgresql core classes, common to both JDBC 1 & 2 -postgresql.jdbc1 classes used only in implementing JDBC 1 -postgresql.jdbc2 classes used only in implementing JDBC 2 -postgresql.fastpath FastPath to backend functions -postgresql.geometric 2D Geometric types mapped to Java Objects -postgresql.largeobject Low level Large Object access -postgresql.util Utility classes +org.postgresql core classes that can be accessed by user code +org.postgresql.core core classes not normally used externally +org.postgresql.jdbc1 classes used only in implementing JDBC 1 +org.postgresql.jdbc2 classes used only in implementing JDBC 2 +org.postgresql.fastpath FastPath to backend functions +org.postgresql.geometric 2D Geometric types mapped to Java Objects +org.postgresql.largeobject Low level Large Object access +org.postgresql.util Utility classes -Package postgresql +Package org.postgresql ------------------ This package holds the core classes. @@ -70,6 +130,9 @@ Driver registers the driver when it's loaded, and determines which Field Used internally to represent a Field PG_Stream Used internally to manage the network stream. +PostgresqlDataSource + Exists in the Java2 Enterprise edition driver only and is the + enterprise equivalent to Driver These classes contains common code that is not dependent to the two JDBC specifications. @@ -77,13 +140,25 @@ PG_Stream Used internally to manage the network stream. Connection Common code used in Connections, mainly Network Protocol stuff. ResultSet Common code used in ResultSet's -Package postgresql.fastpath +Package org.postgresql.core +----------------------- + +New in 7.1, this is where core classes (common to all versions) will exist. Any +new class that would have gone into org.postgresql must go in here instead. + +BytePoolDim1 Handles a pool of byte[] arrays. +BytePoolDim2 Handles a pool of byte[][] arrays +MemoryPool Interface for managing MemoryPools. Not used (yet). +ObjectPool Interface for an Object Pool +SimpleObjectPool Class that implements ObjectPool and used by BytePoolDim# + +Package org.postgresql.fastpath --------------------------- Fastpath Handles executing a function on the PostgreSQL Backend FastpathArg Defines an argument for a function call -Package postgresql.geometric +Package org.postgresql.geometric ---------------------------- PGbox Maps to postgresql type box @@ -94,25 +169,25 @@ PGpath Maps to postgresql type path PGpoint Maps to postgresql type point PGpolygon Maps to postgresql type polygon -Package postgresql.jdbc1 +Package org.postgresql.jdbc1 ------------------------ The classes in this package handle the JDBC 1 Specification, for JDK 1.1.x All interfaces in the java.sql package are present here. -Package postgresql.jdbc2 +Package org.postgresql.jdbc2 ------------------------ The classes in this package handle the JDBC 2 Specification, for JDK 1.2 All interfaces in the java.sql, and javax.sql packages are present here. -Package postgresql.largeobject +Package org.postgresql.largeobject ------------------------------ LargeObject Represents an open LargeObject LargeObjectManager Handles the opening and deleting of LargeObjects -Package postgresql.util +Package org.postgresql.util ----------------------- PGmoney Maps to postgresql type money diff --git a/src/interfaces/jdbc/Makefile b/src/interfaces/jdbc/Makefile index 651a995f56..abe0d375c5 100644 --- a/src/interfaces/jdbc/Makefile +++ b/src/interfaces/jdbc/Makefile @@ -4,7 +4,7 @@ # Makefile for Java JDBC interface # # IDENTIFICATION -# $Id: Makefile,v 1.25 2000/10/12 08:55:23 peter Exp $ +# $Id: Makefile,v 1.26 2001/01/18 14:50:14 peter Exp $ # #------------------------------------------------------------------------- @@ -39,10 +39,30 @@ PGBASE = org/postgresql # New for 7.1: The jar filename JARFILE = postgresql.jar +all: + @echo ------------------------------------------------------------ + @echo The use of Make to build the driver is now depreciated in + @echo version 7.1 and later. To build the driver you now need to + @echo use ANT. This is the build tool of the Jakarta project, and + @echo you can obtain this from http://jakarta.apache.org/ant/ + @echo + @echo Once you have this installed, change directory to the root + @echo directory of the postgresql source and type ant. + @echo + @echo ie: if you are currently in the pgsql/src/interfaces/jdbc + @echo directory then cd into the pgsql one \(cd ../../.. should + @echo do\). + @echo + @echo The file Implementation contains more info on this, as does + @echo the homepage http://jdbc.postgresql.org/ + @echo ------------------------------------------------------------ + # Yet another attempt to check the version. In theory, any JVM that fails # this is breaking the versioning specifications released by Javasoft. # -all: utils/CheckVersion.class +# In 7.1 this is renamed oldall so that the depreciated message is shown. +# +oldall: utils/CheckVersion.class @$(MAKE) `$(JAVA) utils.CheckVersion` # For 6.5.3 and 7.0+, we need to select the correct JDBC API, so prompt @@ -72,7 +92,10 @@ huho: @echo @echo ------------------------------------------------------------ -msg: +# In 7.1 replaced msg so that the depreciated message is shown. +msg: all + +oldmsg: @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 diff --git a/src/interfaces/jdbc/README b/src/interfaces/jdbc/README index aaaa20ac5e..e29d08acda 100644 --- a/src/interfaces/jdbc/README +++ b/src/interfaces/jdbc/README @@ -1,34 +1,49 @@ This is a simple readme describing how to compile and use the jdbc driver. -This file was amended on May 2 2000 to document the changes made in the 7.0 +This file was amended on January 17 2001 to reflect the changes made in the 7.1 release. --------------------------------------------------------------------------- This isn't a guide on how to use JDBC - for that refer to Javasoft's web site: - http://www.javasoft.com + http://www.javasoft.com/ For problems with this driver, then refer to the postgres-interfaces email list: - http://www.postgresql.org + http://www.postgresql.org/ + +The Driver's home page is: + + http://jdbc.postgresql.org/ +or http://www.retep.org.uk/postgresql/ + +NB: They are both the same physical directory so both will always be in sync +(unless the laws of physics break down ;-) ) --------------------------------------------------------------------------- COMPILING -There are actually two versions of the driver. One for the JDBC1.2 specification, and one for the JDBC2 specification. To compile the driver, you need to select the correct one. +To compile you will need to have ANT installed. To obtain ant go to +http://jakarta.apache.org/ant/index.html and download the binary. Being pure +java it will run on virtually all java platforms. If you have any problems +please email the INTERFACES list. -If you have JDK1.1.x you need to type: make jdbc1 - -If you have JDK1.2 or JDK1.3, you need to type: make jdbc2 - -This will compile the driver, and build a .jar file (Java ARchive) called +Once you have ANT, cd to the src directory and type "ant". This will compile +the correct driver for your JVM, and build a .jar file (Java ARchive) called postgresql.jar That jar file will contain the driver for _your_ version of the JDK. +Note: As of 7.1, you build from pgsql/src and not pgsql/src/interfaces/jdbc. +Well you can, but building from the top will also build some extra utilities +located under /contrib at the same time. Also later on (either 7.1.1 or 7.2) +it's intended to have the main configure script to build the driver +automatically if it finds both a JDK & ANT installed, so this is the first step +towards that. + REMEMBER: Once you have compiled the driver, it will work on ALL platforms that support that version of the API. You don't need to build it for each platform. @@ -37,11 +52,8 @@ That means you don't have to compile it on every platform. Believe me, I still hear from people who ask me "I've compiled it ok under Solaris, but it won't compile under Linux" - there's no difference. -PS: When you run make, don't worry if you see more than one or two calls to - javac. This is normal, because the driver dynamically loads classes, and - the Makefile ensures everything gets compiled. - -I advise you don't try running javac outside of make. You may miss something. +I advise you don't try running javac outside of ANT as it builds some classes +on the fly. Possible problems @@ -51,10 +63,13 @@ postgresql/Driver.java:87: interface java.sql.Connection is an interface. It can return new Connection (host(), port(), props, database(), url, this); This is caused by not having the current directory in your CLASSPATH. Under -Linux/Solaris, unset the CLASSPATH environment variable, and rerun make. +Linux/Solaris, unset the CLASSPATH environment variable, and rerun ant. If you are still having problems, I keep a copy of the driver (for different versions of the backend) on my web site http://www.retep.org.uk/postgres/ +or http://jdbc.postgresql.org/ + +More details are in the Implementation file src/interfaces/jdbc/Implementation --------------------------------------------------------------------------- @@ -67,6 +82,9 @@ ie: under LINUX/SOLARIS (the example here is my linux box): export CLASSPATH=.:/usr/local/lib/postgresql.jar +Please don't be tempted to extract the files from the .jar file. There are a +lot of files in there, and you may break the Exception handling. + --------------------------------------------------------------------------- USING THE DRIVER @@ -122,6 +140,14 @@ them to the URL. eg: jdbc:postgresql:database?user=me jdbc:postgresql:database?user=me&password=mypass +Notes: + +1) If you are connecting to localhost or 127.0.0.1 you can leave it out of the + URL. ie: jdbc:postgresql://localhost/mydb can be replaced with + jdbc:postgresql:mydb + +2) The port defaults to 5432 if it's left out. + --------------------------------------------------------------------------- That's the basics related to this driver. You'll need to read the JDBC Docs diff --git a/src/interfaces/jdbc/build.xml b/src/interfaces/jdbc/build.xml index 1e15b38520..fb59c28304 100644 --- a/src/interfaces/jdbc/build.xml +++ b/src/interfaces/jdbc/build.xml @@ -3,25 +3,26 @@ build file to allow ant (http://jakarta.apache.org/ant/) to be used to build the PostgreSQL JDBC Driver. - $Id: build.xml,v 1.2 2000/12/20 16:22:48 peter Exp $ + $Id: build.xml,v 1.3 2001/01/18 14:50:14 peter Exp $ --> - + + - + @@ -29,51 +30,53 @@ - + - + - + - + - + - + - + - + - + + - + + - + @@ -87,12 +90,27 @@ + - + + - + - + + + + + + + + + + \ No newline at end of file diff --git a/src/interfaces/jdbc/jdbc.jpx b/src/interfaces/jdbc/jdbc.jpx new file mode 100644 index 0000000000..a8735cc5c0 --- /dev/null +++ b/src/interfaces/jdbc/jdbc.jpx @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/interfaces/jdbc/org/postgresql/Connection.java b/src/interfaces/jdbc/org/postgresql/Connection.java index b828a90b6d..3ca464d3ad 100644 --- a/src/interfaces/jdbc/org/postgresql/Connection.java +++ b/src/interfaces/jdbc/org/postgresql/Connection.java @@ -10,7 +10,7 @@ import org.postgresql.largeobject.*; import org.postgresql.util.*; /** - * $Id: Connection.java,v 1.11 2000/12/22 03:08:52 momjian Exp $ + * $Id: Connection.java,v 1.12 2001/01/18 14:50:14 peter Exp $ * * This abstract class is used by org.postgresql.Driver to open either the JDBC1 or * JDBC2 versions of the Connection class. @@ -20,10 +20,10 @@ public abstract class Connection { // This is the network stream associated with this connection public PG_Stream pg_stream; - + // This is set by org.postgresql.Statement.setMaxRows() public int maxrows = 0; // maximum no. of rows; 0 = unlimited - + private String PG_HOST; private int PG_PORT; private String PG_USER; @@ -38,17 +38,17 @@ public abstract class Connection * used. */ private String encoding; - + public boolean CONNECTION_OK = true; public boolean CONNECTION_BAD = false; - + public boolean autoCommit = true; public boolean readOnly = false; - + public Driver this_driver; private String this_url; private String cursor = null; // The positioned update cursor name - + // These are new for v6.3, they determine the current protocol versions // supported by this version of the driver. They are defined in // src/include/libpq/pqcomm.h @@ -59,41 +59,41 @@ public abstract class Connection private static final int SM_OPTIONS = 64; private static final int SM_UNUSED = 64; private static final int SM_TTY = 64; - + private static final int AUTH_REQ_OK = 0; private static final int AUTH_REQ_KRB4 = 1; private static final int AUTH_REQ_KRB5 = 2; private static final int AUTH_REQ_PASSWORD = 3; private static final int AUTH_REQ_CRYPT = 4; - + // New for 6.3, salt value for crypt authorisation private String salt; - + // This is used by Field to cache oid -> names. // It's here, because it's shared across this connection only. // Hence it cannot be static within the Field class, because it would then // be across all connections, which could be to different backends. public Hashtable fieldCache = new Hashtable(); - + // Now handle notices as warnings, so things like "show" now work public SQLWarning firstWarning = null; - + // The PID an cancellation key we get from the backend process public int pid; public int ckey; // This receive_sbuf should be used by the different methods - // that call pg_stream.ReceiveString() in this Connection, so - // so we avoid uneccesary new allocations. + // that call pg_stream.ReceiveString() in this Connection, so + // so we avoid uneccesary new allocations. byte receive_sbuf[] = new byte[8192]; - + /** * This is called by Class.forName() from within org.postgresql.Driver */ public Connection() { } - + /** * This method actually opens the connection. It is called by Driver. * @@ -115,7 +115,7 @@ public abstract class Connection throw new PSQLException("postgresql.con.user"); if(info.getProperty("password")==null) throw new PSQLException("postgresql.con.pass"); - + this_driver = d; this_url = url; PG_DATABASE = database; @@ -137,7 +137,7 @@ public abstract class Connection } catch (IOException e) { throw new PSQLException ("postgresql.con.failed",e); } - + // Now we need to construct and send a startup packet try { @@ -146,13 +146,13 @@ public abstract class Connection pg_stream.SendInteger(PG_PROTOCOL_LATEST_MAJOR,2); pg_stream.SendInteger(PG_PROTOCOL_LATEST_MINOR,2); pg_stream.Send(database.getBytes(),SM_DATABASE); - + // This last send includes the unused fields pg_stream.Send(PG_USER.getBytes(),SM_USER+SM_OPTIONS+SM_UNUSED+SM_TTY); - + // now flush the startup packets to the backend pg_stream.flush(); - + // Now get the response from the backend, either an error message // or an authentication request int areq = -1; // must have a value here @@ -169,11 +169,11 @@ public abstract class Connection // throw new SQLException(pg_stream.ReceiveString (receive_sbuf, 4096, getEncoding())); - + case 'R': // Get the type of request areq = pg_stream.ReceiveIntegerR(4); - + // Get the password salt if there is one if(areq == AUTH_REQ_CRYPT) { byte[] rst = new byte[2]; @@ -182,21 +182,21 @@ public abstract class Connection salt = new String(rst,0,2); DriverManager.println("Salt="+salt); } - + // now send the auth packet switch(areq) { case AUTH_REQ_OK: break; - + case AUTH_REQ_KRB4: DriverManager.println("postgresql: KRB4"); throw new PSQLException("postgresql.con.kerb4"); - + case AUTH_REQ_KRB5: DriverManager.println("postgresql: KRB5"); throw new PSQLException("postgresql.con.kerb5"); - + case AUTH_REQ_PASSWORD: DriverManager.println("postgresql: PASSWORD"); pg_stream.SendInteger(5+PG_PASSWORD.length(),4); @@ -204,7 +204,7 @@ public abstract class Connection pg_stream.SendInteger(0,1); pg_stream.flush(); break; - + case AUTH_REQ_CRYPT: DriverManager.println("postgresql: CRYPT"); String crypted = UnixCrypt.crypt(salt,PG_PASSWORD); @@ -213,21 +213,21 @@ public abstract class Connection pg_stream.SendInteger(0,1); pg_stream.flush(); break; - + default: throw new PSQLException("postgresql.con.auth",new Integer(areq)); } break; - + default: throw new PSQLException("postgresql.con.authfail"); } } while(areq != AUTH_REQ_OK); - + } catch (IOException e) { throw new PSQLException("postgresql.con.failed",e); } - + // As of protocol version 2.0, we should now receive the cancellation key and the pid int beresp = pg_stream.ReceiveChar(); @@ -266,7 +266,7 @@ public abstract class Connection // We also ask the DB for certain properties (i.e. DatabaseEncoding at this time) // firstWarning = null; - + java.sql.ResultSet initrset = ExecSQL("set datestyle to 'ISO'; select getdatabaseencoding()"); String dbEncoding = null; @@ -341,19 +341,19 @@ public abstract class Connection encoding = null; } } - + // Initialise object handling initObjectTypes(); - + // Mark the connection as ok, and cleanup firstWarning = null; PG_STATUS = CONNECTION_OK; } - + // These methods used to be in the main Connection implementation. As they // are common to all implementations (JDBC1 or 2), they are placed here. // This should make it easy to maintain the two specifications. - + /** * This adds a warning to the warning chain. * @param msg message to add @@ -361,15 +361,15 @@ public abstract class Connection public void addWarning(String msg) { DriverManager.println(msg); - + // Add the warning to the chain if(firstWarning!=null) firstWarning.setNextWarning(new SQLWarning(msg)); else firstWarning = new SQLWarning(msg); - + // Now check for some specific messages - + // This is obsolete in 6.5, but I've left it in here so if we need to use this // technique again, we'll know where to place it. // @@ -377,13 +377,13 @@ public abstract class Connection //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 8192) @@ -441,13 +443,13 @@ public abstract class Connection } catch (IOException e) { throw new PSQLException("postgresql.con.ioerror",e); } - + while (!hfr || fqp > 0) { Object tup=null; // holds rows as they are recieved - + int c = pg_stream.ReceiveChar(); - + switch (c) { case 'A': // Asynchronous Notify @@ -464,7 +466,7 @@ public abstract class Connection break; case 'C': // Command Status recv_status = pg_stream.ReceiveString(receive_sbuf,8192,getEncoding()); - + // Now handle the update count correctly. if(recv_status.startsWith("INSERT") || recv_status.startsWith("UPDATE") || recv_status.startsWith("DELETE")) { try { @@ -511,7 +513,7 @@ public abstract class Connection break; case 'I': // Empty Query int t = pg_stream.ReceiveChar(); - + if (t != 0) throw new PSQLException("postgresql.con.garbled"); if (fqp > 0) @@ -538,7 +540,7 @@ public abstract class Connection } if (final_error != null) throw final_error; - + return getResultSet(this, fields, tuples, recv_status, update_count, insert_oid); } } @@ -553,7 +555,7 @@ public abstract class Connection { int nf = pg_stream.ReceiveIntegerR(2), i; Field[] fields = new Field[nf]; - + for (i = 0 ; i < nf ; ++i) { String typname = pg_stream.ReceiveString(receive_sbuf,8192,getEncoding()); @@ -564,7 +566,7 @@ public abstract class Connection } return fields; } - + /** * In SQL, a result table can be retrieved through a cursor that * is named. The current row of a result can be updated or deleted @@ -582,7 +584,7 @@ public abstract class Connection { this.cursor = cursor; } - + /** * getCursorName gets the cursor name. * @@ -593,7 +595,7 @@ public abstract class Connection { return cursor; } - + /** * We are required to bring back certain information by * the DatabaseMetaData class. These functions do that. @@ -607,7 +609,7 @@ public abstract class Connection { return this_url; } - + /** * Method getUserName() brings back the User Name (again, we * saved it) @@ -622,13 +624,13 @@ public abstract class Connection /** * Get the character encoding to use for this connection. - * @return the encoding to use, or null for the + * @return the encoding to use, or null for the * default encoding. */ public String getEncoding() throws SQLException { return encoding; } - + /** * This returns the Fastpath API for the current connection. * @@ -657,10 +659,10 @@ public abstract class Connection 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. * @@ -686,10 +688,10 @@ public abstract class Connection 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 * org.postgresql's more unique data types. @@ -713,7 +715,7 @@ public abstract class Connection { try { Object o = objectTypes.get(type); - + // If o is null, then the type is unknown, so check to see if type // is an actual table name. If it does, see if a Class is known that // can handle it @@ -722,7 +724,7 @@ public abstract class Connection objectTypes.put(type,ser); return ser.fetch(Integer.parseInt(value)); } - + // If o is not null, and it is a String, then its a class name that // extends PGobject. // @@ -748,11 +750,11 @@ public abstract class Connection } catch(Exception ex) { throw new PSQLException("postgresql.con.creobj",type,ex); } - + // should never be reached return null; } - + /** * This stores an object into the database. * @param o Object to store @@ -765,7 +767,7 @@ public abstract class Connection try { String type = o.getClass().getName(); Object x = objectTypes.get(type); - + // If x is null, then the type is unknown, so check to see if type // is an actual table name. If it does, see if a Class is known that // can handle it @@ -774,15 +776,15 @@ public abstract class Connection objectTypes.put(type,ser); return ser.store(o); } - + // If it's an object, it should be an instance of our Serialize class // If so, then call it's fetch method. if(x instanceof Serialize) return ((Serialize)x).store(o); - + // Thow an exception because the type is unknown throw new PSQLException("postgresql.con.strobj"); - + } catch(SQLException sx) { // rethrow the exception. Done because we capture any others next sx.fillInStackTrace(); @@ -791,7 +793,7 @@ public abstract class Connection throw new PSQLException("postgresql.con.strobjex",ex); } } - + /** * This allows client code to add a handler for one of org.postgresql's * more unique data types. @@ -816,10 +818,10 @@ public abstract class Connection { 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 @@ -835,25 +837,25 @@ public abstract class Connection {"polygon", "org.postgresql.geometric.PGpolygon"}, {"money", "org.postgresql.util.PGmoney"} }; - + // This initialises the objectTypes hashtable private void initObjectTypes() { for(int i=0;i adds a 10x speed // improvement on FreeBSD machines (caused by a bug in their TCP Stack) connection.setTcpNoDelay(true); - + // Buffer sizes submitted by Sverre H Huseby pg_input = new BufferedInputStream(connection.getInputStream(), 8192); pg_output = new BufferedOutputStream(connection.getOutputStream(), 8192); } - + /** * Sends a single character to the back end * @@ -59,11 +60,11 @@ public class PG_Stream //byte b[] = new byte[1]; //b[0] = (byte)val; //pg_output.write(b); - + // Optimised version by Sverre H. Huseby Aug 22 1999 Applied Sep 13 1999 pg_output.write((byte)val); } - + /** * Sends an integer to the back end * @@ -74,7 +75,7 @@ public class PG_Stream public void SendInteger(int val, int siz) throws IOException { byte[] buf = bytePoolDim1.allocByte(siz); - + while (siz-- > 0) { buf[siz] = (byte)(val & 0xff); @@ -82,7 +83,7 @@ public class PG_Stream } Send(buf); } - + /** * Sends an integer to the back end in reverse order. * @@ -106,7 +107,7 @@ public class PG_Stream } Send(buf); } - + /** * Send an array of bytes to the backend * @@ -117,7 +118,7 @@ public class PG_Stream { pg_output.write(buf); } - + /** * Send an exact array of bytes to the backend - if the length * has not been reached, send nulls until it has. @@ -130,7 +131,7 @@ public class PG_Stream { Send(buf,0,siz); } - + /** * Send an exact array of bytes to the backend - if the length * has not been reached, send nulls until it has. @@ -143,7 +144,7 @@ public class PG_Stream public void Send(byte buf[], int off, int siz) throws IOException { int i; - + pg_output.write(buf, off, ((buf.length-off) < siz ? (buf.length-off) : siz)); if((buf.length-off) < siz) { @@ -153,7 +154,7 @@ public class PG_Stream } } } - + /** * Sends a packet, prefixed with the packet's length * @param buf buffer to send @@ -164,7 +165,7 @@ public class PG_Stream SendInteger(buf.length+4,4); Send(buf); } - + /** * Receives a single character from the backend * @@ -174,7 +175,7 @@ public class PG_Stream public int ReceiveChar() throws SQLException { int c = 0; - + try { c = pg_input.read(); @@ -184,7 +185,7 @@ public class PG_Stream } return c; } - + /** * Receives an integer from the backend * @@ -195,13 +196,13 @@ public class PG_Stream public int ReceiveInteger(int siz) throws SQLException { int n = 0; - + try { for (int i = 0 ; i < siz ; i++) { int b = pg_input.read(); - + if (b < 0) throw new PSQLException("postgresql.stream.eof"); n = n | (b << (8 * i)) ; @@ -211,7 +212,7 @@ public class PG_Stream } return n; } - + /** * Receives an integer from the backend * @@ -222,13 +223,13 @@ public class PG_Stream public int ReceiveIntegerR(int siz) throws SQLException { int n = 0; - + try { for (int i = 0 ; i < siz ; i++) { int b = pg_input.read(); - + if (b < 0) throw new PSQLException("postgresql.stream.eof"); n = b | (n << 8); @@ -270,24 +271,24 @@ public class PG_Stream byte[] rst = bytePoolDim1.allocByte(maxsiz); return ReceiveString(rst, maxsiz, encoding); } - + /** * Receives a null-terminated string from the backend. Maximum of * maxsiz bytes - if we don't see a null, then we assume something * has gone wrong. * - * @param rst byte array to read the String into. rst.length must - * equal to or greater than maxsize. + * @param rst byte array to read the String into. rst.length must + * equal to or greater than maxsize. * @param maxsiz maximum length of string in bytes * @param encoding the charset encoding to use. * @return string from back end * @exception SQLException if an I/O error occurs */ - public String ReceiveString(byte rst[], int maxsiz, String encoding) + public String ReceiveString(byte rst[], int maxsiz, String encoding) throws SQLException { int s = 0; - + try { while (s < maxsiz) @@ -318,7 +319,7 @@ public class PG_Stream } return v; } - + /** * Read a tuple from the back end. A tuple is a two dimensional * array of bytes @@ -334,10 +335,10 @@ public class PG_Stream int i, bim = (nf + 7)/8; byte[] bitmask = Receive(bim); byte[][] answer = bytePoolDim2.allocByte(nf); - + int whichbit = 0x80; int whichbyte = 0; - + for (i = 0 ; i < nf ; ++i) { boolean isNull = ((bitmask[whichbyte] & whichbit) == 0); @@ -347,21 +348,21 @@ public class PG_Stream ++whichbyte; whichbit = 0x80; } - if (isNull) + if (isNull) answer[i] = null; else { int len = ReceiveIntegerR(4); - if (!bin) + if (!bin) len -= 4; - if (len < 0) + if (len < 0) len = 0; answer[i] = Receive(len); } } return answer; } - + /** * Reads in a given number of bytes from the backend * @@ -375,7 +376,7 @@ public class PG_Stream Receive(answer,0,siz); return answer; } - + /** * Reads in a given number of bytes from the backend * @@ -387,8 +388,8 @@ public class PG_Stream public void Receive(byte[] b,int off,int siz) throws SQLException { int s = 0; - - try + + try { while (s < siz) { @@ -401,7 +402,7 @@ public class PG_Stream throw new PSQLException("postgresql.stream.ioerror",e); } } - + /** * This flushes any pending output to the backend. It is used primarily * by the Fastpath code. @@ -415,7 +416,7 @@ public class PG_Stream throw new PSQLException("postgresql.stream.flush",e); } } - + /** * Closes the connection * @@ -430,151 +431,5 @@ public class PG_Stream connection.close(); } - /** - * Deallocate all resources that has been associated with any previous - * query. - */ - public void deallocate(){ - bytePoolDim1.deallocate(); - bytePoolDim2.deallocate(); - } -} - -/** - * A simple and fast object pool implementation that can pool objects - * of any type. This implementation is not thread safe, it is up to the users - * of this class to assure thread safety. - */ -class ObjectPool { - int cursize = 0; - int maxsize = 16; - Object arr[] = new Object[maxsize]; - - public void add(Object o){ - if(cursize >= maxsize){ - Object newarr[] = new Object[maxsize*2]; - System.arraycopy(arr, 0, newarr, 0, maxsize); - maxsize = maxsize * 2; - arr = newarr; - } - arr[cursize++] = o; - } - - public Object remove(){ - return arr[--cursize]; - } - public boolean isEmpty(){ - return cursize == 0; - } - public int size(){ - return cursize; - } - public void addAll(ObjectPool pool){ - int srcsize = pool.size(); - if(srcsize == 0) - return; - int totalsize = srcsize + cursize; - if(totalsize > maxsize){ - Object newarr[] = new Object[totalsize*2]; - System.arraycopy(arr, 0, newarr, 0, cursize); - maxsize = maxsize = totalsize * 2; - arr = newarr; - } - System.arraycopy(pool.arr, 0, arr, cursize, srcsize); - cursize = totalsize; - } - public void clear(){ - cursize = 0; - } -} - -/** - * A simple and efficient class to pool one dimensional byte arrays - * of different sizes. - */ -class BytePoolDim1 { - int maxsize = 256; - ObjectPool notusemap[] = new ObjectPool[maxsize]; - ObjectPool inusemap[] = new ObjectPool[maxsize]; - byte binit[][] = new byte[maxsize][0]; - - public BytePoolDim1(){ - for(int i = 0; i < maxsize; i++){ - binit[i] = new byte[i]; - inusemap[i] = new ObjectPool(); - notusemap[i] = new ObjectPool(); - } - } - - public byte[] allocByte(int size){ - if(size > maxsize){ - return new byte[size]; - } - - ObjectPool not_usel = notusemap[size]; - ObjectPool in_usel = inusemap[size]; - byte b[] = null; - - if(!not_usel.isEmpty()) { - Object o = not_usel.remove(); - b = (byte[]) o; - } else - b = new byte[size]; - in_usel.add(b); - - return b; - } - - public void deallocate(){ - for(int i = 0; i < maxsize; i++){ - notusemap[i].addAll(inusemap[i]); - inusemap[i].clear(); - } - - } -} - - - -/** - * A simple and efficient class to pool two dimensional byte arrays - * of different sizes. - */ -class BytePoolDim2 { - int maxsize = 32; - ObjectPool notusemap[] = new ObjectPool[maxsize]; - ObjectPool inusemap[] = new ObjectPool[maxsize]; - - public BytePoolDim2(){ - for(int i = 0; i < maxsize; i++){ - inusemap[i] = new ObjectPool(); - notusemap[i] = new ObjectPool(); - } - } - - public byte[][] allocByte(int size){ - if(size > maxsize){ - return new byte[size][0]; - } - ObjectPool not_usel = notusemap[size]; - ObjectPool in_usel = inusemap[size]; - - byte b[][] = null; - - if(!not_usel.isEmpty()) { - Object o = not_usel.remove(); - b = (byte[][]) o; - } else - b = new byte[size][0]; - in_usel.add(b); - return b; - } - - public void deallocate(){ - for(int i = 0; i < maxsize; i++){ - notusemap[i].addAll(inusemap[i]); - inusemap[i].clear(); - } - } } diff --git a/src/interfaces/jdbc/org/postgresql/core/BytePoolDim1.java b/src/interfaces/jdbc/org/postgresql/core/BytePoolDim1.java new file mode 100644 index 0000000000..61b58f2ec5 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/core/BytePoolDim1.java @@ -0,0 +1,95 @@ +package org.postgresql.core; + +/** + * A simple and efficient class to pool one dimensional byte arrays + * of different sizes. + */ +public class BytePoolDim1 { + + /** + * The maximum size of the array we manage. + */ + int maxsize = 256; + /** + * The pools not currently in use + */ + ObjectPool notusemap[] = new ObjectPool[maxsize+1]; + /** + * The pools currently in use + */ + ObjectPool inusemap[] = new ObjectPool[maxsize+1]; + /** + * + */ + byte binit[][] = new byte[maxsize][0]; + + /** + * Construct a new pool + */ + public BytePoolDim1(){ + for(int i = 0; i <= maxsize; i++){ + binit[i] = new byte[i]; + inusemap[i] = new SimpleObjectPool(); + notusemap[i] = new SimpleObjectPool(); + } + } + + /** + * Allocate a byte[] of a specified size and put it in the pool. If it's + * larger than maxsize then it is not pooled. + * @return the byte[] allocated + */ + public byte[] allocByte(int size) { + // for now until the bug can be removed + return new byte[size]; + /* + // Don't pool if >maxsize + if(size > maxsize){ + return new byte[size]; + } + + ObjectPool not_usel = notusemap[size]; + ObjectPool in_usel = inusemap[size]; + byte b[] = null; + + // Fetch from the unused pool if available otherwise allocate a new + // now array + if(!not_usel.isEmpty()) { + Object o = not_usel.remove(); + b = (byte[]) o; + } else + b = new byte[size]; + in_usel.add(b); + + return b; + */ + } + + /** + * Release an array + * @param b byte[] to release + */ + public void release(byte[] b) { + // If it's larger than maxsize then we don't touch it + if(b.length>maxsize) + return; + + ObjectPool not_usel = notusemap[b.length]; + ObjectPool in_usel = inusemap[b.length]; + + in_usel.remove(b); + not_usel.add(b); + } + + /** + * Deallocate all + * @deprecated Real bad things happen if this is called! + */ + public void deallocate() { + //for(int i = 0; i <= maxsize; i++){ + // notusemap[i].addAll(inusemap[i]); + // inusemap[i].clear(); + //} + } +} + diff --git a/src/interfaces/jdbc/org/postgresql/core/BytePoolDim2.java b/src/interfaces/jdbc/org/postgresql/core/BytePoolDim2.java new file mode 100644 index 0000000000..0eb508171f --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/core/BytePoolDim2.java @@ -0,0 +1,62 @@ +package org.postgresql.core; + +public class BytePoolDim2 { + int maxsize = 32; + ObjectPool notusemap[] = new ObjectPool[maxsize+1]; + ObjectPool inusemap[] = new ObjectPool[maxsize+1]; + + public BytePoolDim2(){ + for(int i = 0; i <= maxsize; i++){ + inusemap[i] = new SimpleObjectPool(); + notusemap[i] = new SimpleObjectPool(); + } + } + + public byte[][] allocByte(int size){ + // For now until the bug can be removed + return new byte[size][0]; + /* + if(size > maxsize){ + return new byte[size][0]; + } + ObjectPool not_usel = notusemap[size]; + ObjectPool in_usel = inusemap[size]; + + byte b[][] = null; + + if(!not_usel.isEmpty()) { + Object o = not_usel.remove(); + b = (byte[][]) o; + } else + b = new byte[size][0]; + in_usel.add(b); + return b; + */ + } + + public void release(byte[][] b){ + if(b.length > maxsize){ + return; + } + ObjectPool not_usel = notusemap[b.length]; + ObjectPool in_usel = inusemap[b.length]; + + in_usel.remove(b); + not_usel.add(b); + } + + /** + * Deallocate the object cache. + * PM 17/01/01: Commented out this code as it blows away any hope of + * multiple queries on the same connection. I'll redesign the allocation + * code to use some form of Statement context, so the buffers are per + * Statement and not per Connection/PG_Stream as it is now. + */ + public void deallocate(){ + //for(int i = 0; i <= maxsize; i++){ + // notusemap[i].addAll(inusemap[i]); + // inusemap[i].clear(); + //} + } +} + diff --git a/src/interfaces/jdbc/org/postgresql/core/MemoryPool.java b/src/interfaces/jdbc/org/postgresql/core/MemoryPool.java new file mode 100644 index 0000000000..1e83d76be2 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/core/MemoryPool.java @@ -0,0 +1,18 @@ +package org.postgresql.core; + +/** + * This interface defines the methods to access the memory pool classes. + */ +public interface MemoryPool { + /** + * Allocate an array from the pool + * @return byte[] allocated + */ + public byte[] allocByte(int size); + + /** + * Frees an object back to the pool + * @param o Object to release + */ + public void release(Object o); +} \ No newline at end of file diff --git a/src/interfaces/jdbc/org/postgresql/core/ObjectPool.java b/src/interfaces/jdbc/org/postgresql/core/ObjectPool.java new file mode 100644 index 0000000000..5053c85c59 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/core/ObjectPool.java @@ -0,0 +1,48 @@ +package org.postgresql.core; + +/** + * This interface defines methods needed to implement a simple object pool. + * There are two known classes that implement this, one for jdk1.1 and the + * other for jdk1.2+ + */ + +public interface ObjectPool { + /** + * Adds an object to the pool + * @param o Object to add + */ + public void add(Object o); + + /** + * Removes an object from the pool + * @param o Object to remove + */ + public void remove(Object o); + + /** + * Removes the top object from the pool + * @return Object from the top. + */ + public Object remove(); + + /** + * @return true if the pool is empty + */ + public boolean isEmpty(); + + /** + * @return the number of objects in the pool + */ + public int size(); + + /** + * Adds all objects in one pool to this one + * @param pool The pool to take the objects from + */ + public void addAll(ObjectPool pool); + + /** + * Clears the pool of all objects + */ + public void clear(); +} diff --git a/src/interfaces/jdbc/org/postgresql/core/SimpleObjectPool.java b/src/interfaces/jdbc/org/postgresql/core/SimpleObjectPool.java new file mode 100644 index 0000000000..e9d5d9fc6d --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/core/SimpleObjectPool.java @@ -0,0 +1,97 @@ +package org.postgresql.core; + +/** + * A simple and fast object pool implementation that can pool objects + * of any type. This implementation is not thread safe, it is up to the users + * of this class to assure thread safety. + */ + +public class SimpleObjectPool implements ObjectPool +{ + // This was originally in PG_Stream but moved out to fix the major problem + // where more than one query (usually all the time) overwrote the results + // of another query. + int cursize = 0; + int maxsize = 16; + Object arr[] = new Object[maxsize]; + + /** + * Adds an object to the pool + * @param o Object to add + */ + public void add(Object o) + { + if(cursize >= maxsize){ + Object newarr[] = new Object[maxsize*2]; + System.arraycopy(arr, 0, newarr, 0, maxsize); + maxsize = maxsize * 2; + arr = newarr; + } + arr[cursize++] = o; + } + + /** + * Removes the top object from the pool + * @return Object from the top. + */ + public Object remove(){ + return arr[--cursize]; + } + + /** + * Removes the given object from the pool + * @param o Object to remove + */ + public void remove(Object o) { + int p=0; + while(p maxsize){ + Object newarr[] = new Object[totalsize*2]; + System.arraycopy(arr, 0, newarr, 0, cursize); + maxsize = maxsize = totalsize * 2; + arr = newarr; + } + System.arraycopy(pool.arr, 0, arr, cursize, srcsize); + cursize = totalsize; + } + + /** + * Clears the pool of all objects + */ + public void clear(){ + cursize = 0; + } +} diff --git a/src/interfaces/jdbc/org/postgresql/errors.properties b/src/interfaces/jdbc/org/postgresql/errors.properties index c33a34aa7b..b157ec8fed 100644 --- a/src/interfaces/jdbc/org/postgresql/errors.properties +++ b/src/interfaces/jdbc/org/postgresql/errors.properties @@ -35,6 +35,8 @@ postgresql.geo.point:Conversion of point failed - {0} postgresql.jvm.version:The postgresql.jar file does not contain the correct JDBC classes for this JVM. Try rebuilding. If that fails, try forcing the version supplying it to the command line using the argument -Djava.version=1.1 or -Djava.version=1.2\nException thrown was {0} postgresql.lo.init:failed to initialise LargeObject API postgresql.money:conversion of money failed - {0}. +postgresql.noupdate:This ResultSet is not updateable +postgresql.psqlnotimp:The backend currently does not support this feature. postgresql.prep.is:InputStream as parameter not supported postgresql.prep.param:No value specified for parameter {0}. postgresql.prep.range:Parameter index out of range. diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Statement.java index 43e5d38143..fcc6dac360 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Statement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/Statement.java @@ -13,7 +13,7 @@ import org.postgresql.util.PSQLException; * 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 @@ -29,7 +29,7 @@ public class Statement implements java.sql.Statement SQLWarning warnings = null; // The warnings chain. int timeout = 0; // The timeout for a query (not used) boolean escapeProcessing = true;// escape processing flag - + /** * Constructor for a Statement. It simply sets the connection * that created us. @@ -81,8 +81,8 @@ 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 - * garbage collected. When a Statement is closed, its current + *

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. * * @exception SQLException if a database access error occurs (why?) @@ -146,7 +146,7 @@ public class Statement implements java.sql.Statement /** * If escape scanning is on (the default), the driver will do escape - * substitution before sending the SQL to the database. + * substitution before sending the SQL to the database. * * @param enable true to enable; false to disable * @exception SQLException if a database access error occurs @@ -183,7 +183,7 @@ public class Statement implements java.sql.Statement /** * Cancel can be used by one thread to cancel a statement that * is being executed by another thread. However, PostgreSQL is - * a sync. sort of thing, so this really has no meaning - we + * a sync. sort of thing, so this really has no meaning - we * define it as a no-op (i.e. you can't cancel, but there is no * error if you try.) * @@ -256,7 +256,7 @@ public class Statement implements java.sql.Statement /** * Execute a SQL statement that may return multiple results. We * don't have to worry about this since we do not support multiple - * ResultSets. You can use getResultSet or getUpdateCount to + * ResultSets. You can use getResultSet or getUpdateCount to * retrieve the result. * * @param sql any SQL statement @@ -266,8 +266,10 @@ public class Statement implements java.sql.Statement */ public boolean execute(String sql) throws SQLException { - result = connection.ExecSQL(sql); - return (result != null && ((org.postgresql.ResultSet)result).reallyResultSet()); + if(escapeProcessing) + sql=connection.EscapeSQL(sql); + result = connection.ExecSQL(sql); + return (result != null && ((org.postgresql.ResultSet)result).reallyResultSet()); } /** @@ -309,7 +311,7 @@ public class Statement implements java.sql.Statement result = ((org.postgresql.ResultSet)result).getNext(); return (result != null && ((org.postgresql.ResultSet)result).reallyResultSet()); } - + /** * Returns the status message from the current Result.

* This is used internally by the driver. diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java index f1f333d6d2..0bdf0e24c5 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java @@ -21,7 +21,7 @@ import org.postgresql.util.*; * 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. * @@ -47,8 +47,8 @@ import org.postgresql.util.*; * the programmer to guarentee that they actually refer to the intended * columns. * - *

A ResultSet is automatically closed by the Statement that generated it - * when that Statement is closed, re-executed, or is used to retrieve the + *

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 @@ -57,7 +57,7 @@ import org.postgresql.util.*; * @see ResultSetMetaData * @see java.sql.ResultSet */ -public class ResultSet extends org.postgresql.ResultSet implements java.sql.ResultSet +public class ResultSet extends org.postgresql.ResultSet implements java.sql.ResultSet { /** * Create a new ResultSet - Note that we create ResultSets to @@ -74,7 +74,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { super(conn,fields,tuples,status,updateCount,insertOID); } - + /** * Create a new ResultSet - Note that we create ResultSets to * represent the results of everything. @@ -90,7 +90,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { super(conn,fields,tuples,status,updateCount,0); } - + /** * A ResultSet is initially positioned before its first row, * the first call to next makes the first row the current row; @@ -111,7 +111,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu this_row = (byte [][])rows.elementAt(current_row); return true; } - + /** * In some cases, it is desirable to immediately release a ResultSet * database and JDBC resources instead of waiting for this to happen @@ -121,7 +121,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu *

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 + * of multiple results. A ResultSet is also automatically closed * when it is garbage collected. * * @exception SQLException if a database access error occurs @@ -131,7 +131,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu //release resources held (memory for tuples) rows.setSize(0); } - + /** * A column may have the value of SQL NULL; wasNull() reports whether * the last column read had this special value. Note that you must @@ -145,7 +145,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { return wasNullFlag; } - + /** * Get the value of a column in the current row as a Java String * @@ -176,7 +176,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu } } } - + /** * Get the value of a column in the current row as a Java boolean * @@ -187,7 +187,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public boolean getBoolean(int columnIndex) throws SQLException { String s = getString(columnIndex); - + if (s != null) { int c = s.charAt(0); @@ -195,7 +195,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu } return false; // SQL NULL } - + /** * Get the value of a column in the current row as a Java byte. * @@ -206,7 +206,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public byte getByte(int columnIndex) throws SQLException { String s = getString(columnIndex); - + if (s != null) { try @@ -218,7 +218,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu } return 0; // SQL NULL } - + /** * Get the value of a column in the current row as a Java short. * @@ -229,7 +229,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public short getShort(int columnIndex) throws SQLException { String s = getString(columnIndex); - + if (s != null) { try @@ -241,7 +241,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu } return 0; // SQL NULL } - + /** * Get the value of a column in the current row as a Java int. * @@ -252,7 +252,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public int getInt(int columnIndex) throws SQLException { String s = getString(columnIndex); - + if (s != null) { try @@ -264,7 +264,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu } return 0; // SQL NULL } - + /** * Get the value of a column in the current row as a Java long. * @@ -275,7 +275,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public long getLong(int columnIndex) throws SQLException { String s = getString(columnIndex); - + if (s != null) { try @@ -287,7 +287,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu } return 0; // SQL NULL } - + /** * Get the value of a column in the current row as a Java float. * @@ -298,7 +298,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public float getFloat(int columnIndex) throws SQLException { String s = getString(columnIndex); - + if (s != null) { try @@ -310,7 +310,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu } return 0; // SQL NULL } - + /** * Get the value of a column in the current row as a Java double. * @@ -321,7 +321,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public double getDouble(int columnIndex) throws SQLException { String s = getString(columnIndex); - + if (s != null) { try @@ -333,9 +333,9 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu } return 0; // SQL NULL } - + /** - * Get the value of a column in the current row as a + * Get the value of a column in the current row as a * java.math.BigDecimal object * * @param columnIndex the first column is 1, the second is 2... @@ -348,7 +348,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { String s = getString(columnIndex); BigDecimal val; - + if (s != null) { try @@ -367,7 +367,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu } return null; // SQL NULL } - + /** * Get the value of a column in the current row as a Java byte array. * @@ -388,7 +388,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu if (columnIndex < 1 || columnIndex > fields.length) throw new PSQLException("postgresql.res.colrange"); wasNullFlag = (this_row[columnIndex - 1] == null); - + // Handle OID's as BLOBS if(!wasNullFlag) if( fields[columnIndex - 1].getOID() == 26) { @@ -398,10 +398,10 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu lob.close(); return buf; } - + return this_row[columnIndex - 1]; } - + /** * Get the value of a column in the current row as a java.sql.Date * object @@ -422,7 +422,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu throw new PSQLException("postgresql.res.baddate",new Integer(e.getErrorOffset()),s); } } - + /** * Get the value of a column in the current row as a java.sql.Time * object @@ -434,7 +434,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public Time getTime(int columnIndex) throws SQLException { String s = getString(columnIndex); - + if (s != null) { try @@ -451,9 +451,9 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu } return null; // SQL NULL } - + /** - * Get the value of a column in the current row as a + * Get the value of a column in the current row as a * java.sql.Timestamp object * * @param columnIndex the first column is 1, the second is 2... @@ -465,7 +465,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu String s = getString(columnIndex); if(s==null) return null; - + // This works, but it's commented out because Michael Stephenson's // solution is better still: //SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @@ -480,8 +480,8 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu // Modification by Jan Thomae if (s.length()>27) { - df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzzzzzzzz"); - } else + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzzzzzzzz"); + } else // ------- if (s.length()>21 && s.indexOf('.') != -1) { df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSzzz"); @@ -494,17 +494,17 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu } else { df = new SimpleDateFormat("yyyy-MM-dd"); } - + try { return new Timestamp(df.parse(s).getTime()); } catch(ParseException e) { throw new PSQLException("postgresql.res.badtimestamp",new Integer(e.getErrorOffset()),s); } } - + /** * A column value can be retrieved as a stream of ASCII characters - * and then read in chunks from the stream. This method is + * and then read in chunks from the stream. This method is * particular suitable for retrieving large LONGVARCHAR values. * The JDBC driver will do any necessary conversion from the * database format into ASCII. @@ -530,7 +530,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { return getBinaryStream(columnIndex); } - + /** * A column value can also be retrieved as a stream of Unicode * characters. We implement this as a binary stream. @@ -550,7 +550,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { return getBinaryStream(columnIndex); } - + /** * A column value can also be retrieved as a binary strea. This * method is suitable for retrieving LONGVARBINARY values. @@ -566,12 +566,12 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public InputStream getBinaryStream(int columnIndex) throws SQLException { byte b[] = getBytes(columnIndex); - + if (b != null) return new ByteArrayInputStream(b); return null; // SQL NULL } - + /** * The following routines simply convert the columnName into * a columnIndex and then call the appropriate routine above. @@ -584,43 +584,43 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { return getString(findColumn(columnName)); } - + public boolean getBoolean(String columnName) throws SQLException { return getBoolean(findColumn(columnName)); } - + public byte getByte(String columnName) throws SQLException { - + return getByte(findColumn(columnName)); } - + public short getShort(String columnName) throws SQLException { return getShort(findColumn(columnName)); } - + public int getInt(String columnName) throws SQLException { return getInt(findColumn(columnName)); } - + public long getLong(String columnName) throws SQLException { return getLong(findColumn(columnName)); } - + public float getFloat(String columnName) throws SQLException { return getFloat(findColumn(columnName)); } - + public double getDouble(String columnName) throws SQLException { return getDouble(findColumn(columnName)); } - + /** * @deprecated */ @@ -628,32 +628,32 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { return getBigDecimal(findColumn(columnName), scale); } - + public byte[] getBytes(String columnName) throws SQLException { return getBytes(findColumn(columnName)); } - + public java.sql.Date getDate(String columnName) throws SQLException { return getDate(findColumn(columnName)); } - + public Time getTime(String columnName) throws SQLException { return getTime(findColumn(columnName)); } - + public Timestamp getTimestamp(String columnName) throws SQLException { return getTimestamp(findColumn(columnName)); } - + public InputStream getAsciiStream(String columnName) throws SQLException { return getAsciiStream(findColumn(columnName)); } - + /** * * ** DEPRECATED IN JDBC 2 ** @@ -664,12 +664,12 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { return getUnicodeStream(findColumn(columnName)); } - + public InputStream getBinaryStream(String columnName) throws SQLException { return getBinaryStream(findColumn(columnName)); } - + /** * The first warning reported by calls on this ResultSet is * returned. Subsequent ResultSet warnings will be chained @@ -690,7 +690,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { return warnings; } - + /** * After this call, getWarnings returns null until a new warning * is reported for this ResultSet @@ -701,7 +701,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { warnings = null; } - + /** * Get the name of the SQL cursor used by this ResultSet * @@ -724,7 +724,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { return connection.getCursorName(); } - + /** * The numbers, types and properties of a ResultSet's columns are * provided by the getMetaData method @@ -736,7 +736,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { return new ResultSetMetaData(rows, fields); } - + /** * Get the value of a column in the current row as a Java object * @@ -755,22 +755,22 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public Object getObject(int columnIndex) throws SQLException { Field field; - + if (columnIndex < 1 || columnIndex > fields.length) throw new PSQLException("postgresql.res.colrange"); - + wasNullFlag = (this_row[columnIndex - 1] == null); if(wasNullFlag) return null; - + field = fields[columnIndex - 1]; - + // some fields can be null, mainly from those returned by MetaData methods if(field==null) { wasNullFlag=true; return null; } - + switch (field.getSQLType()) { case Types.BIT: @@ -801,7 +801,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu return connection.getObject(field.getTypeName(), getString(columnIndex)); } } - + /** * Get the value of a column in the current row as a Java object * @@ -821,7 +821,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { return getObject(findColumn(columnName)); } - + /** * Map a ResultSet column name to a ResultSet column index * @@ -832,35 +832,35 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public int findColumn(String columnName) throws SQLException { int i; - + for (i = 0 ; i < fields.length; ++i) if (fields[i].name.equalsIgnoreCase(columnName)) return (i+1); throw new PSQLException ("postgresql.res.colname",columnName); } - + // ** JDBC 2 Extensions ** - + public boolean absolute(int index) throws SQLException { // index is 1-based, but internally we use 0-based indices int internalIndex; if (index==0) - throw new SQLException("Cannot move to index of 0"); + throw new SQLException("Cannot move to index of 0"); //if index<0, count from the end of the result set, but check //to be sure that it is not beyond the first index - if (index<0) + if (index<0) if (index>=-rows.size()) internalIndex=rows.size()+index; else { beforeFirst(); return false; } - - //must be the case that index>0, - //find the correct place, assuming that + + //must be the case that index>0, + //find the correct place, assuming that //the index is not too large if (index<=rows.size()) internalIndex = index-1; @@ -873,29 +873,31 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu this_row = (byte [][])rows.elementAt(internalIndex); return true; } - + public void afterLast() throws SQLException { if (rows.size() > 0) current_row = rows.size(); } - + public void beforeFirst() throws SQLException { if (rows.size() > 0) current_row = -1; } - + public void cancelRowUpdates() throws SQLException { - throw org.postgresql.Driver.notImplemented(); + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); } - + public void deleteRow() throws SQLException { - throw org.postgresql.Driver.notImplemented(); + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); } - + public boolean first() throws SQLException { if (rows.size() <= 0) @@ -904,148 +906,189 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu this_row = (byte [][])rows.elementAt(current_row); return true; } - + public Array getArray(String colName) throws SQLException { return getArray(findColumn(colName)); } - + public Array getArray(int i) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException { - throw org.postgresql.Driver.notImplemented(); + try { + return new BigDecimal(getDouble(columnIndex)); + } catch(NumberFormatException nfe) { + throw new PSQLException("postgresql.res.badbigdec",nfe.toString()); + } } - + public java.math.BigDecimal getBigDecimal(String columnName) throws SQLException { return getBigDecimal(findColumn(columnName)); } - + public Blob getBlob(String columnName) throws SQLException { return getBlob(findColumn(columnName)); } - + public Blob getBlob(int i) throws SQLException { return new org.postgresql.largeobject.PGblob(connection,getInt(i)); } - + public java.io.Reader getCharacterStream(String columnName) throws SQLException { return getCharacterStream(findColumn(columnName)); } - + public java.io.Reader getCharacterStream(int i) throws SQLException { - throw org.postgresql.Driver.notImplemented(); + // New in 7.1 + try { + String encoding = connection.getEncoding(); + if(encoding==null) + return new InputStreamReader(getBinaryStream(i)); + return new InputStreamReader(getBinaryStream(i),encoding); + } catch (UnsupportedEncodingException unse) { + throw new PSQLException("postgresql.res.encoding", unse); + } } - + public Clob getClob(String columnName) throws SQLException { return getClob(findColumn(columnName)); } - + public Clob getClob(int i) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public int getConcurrency() throws SQLException { - throw org.postgresql.Driver.notImplemented(); + // New in 7.1 - The standard ResultSet class will now return + // CONCUR_READ_ONLY. A sub-class will overide this if the query was + // updateable. + return CONCUR_READ_ONLY; } - + public java.sql.Date getDate(int i,java.util.Calendar cal) throws SQLException { - throw org.postgresql.Driver.notImplemented(); + // new in 7.1: If I read the specs, this should use cal only if we don't + // store the timezone, and if we do, then act just like getDate()? + // for now... + return getDate(i); } - + public Time getTime(int i,java.util.Calendar cal) throws SQLException { - throw org.postgresql.Driver.notImplemented(); + // new in 7.1: If I read the specs, this should use cal only if we don't + // store the timezone, and if we do, then act just like getTime()? + // for now... + return getTime(i); } - + public Timestamp getTimestamp(int i,java.util.Calendar cal) throws SQLException { - throw org.postgresql.Driver.notImplemented(); + // new in 7.1: If I read the specs, this should use cal only if we don't + // store the timezone, and if we do, then act just like getDate()? + // for now... + return getTimestamp(i); } - + public java.sql.Date getDate(String c,java.util.Calendar cal) throws SQLException { return getDate(findColumn(c),cal); } - + public Time getTime(String c,java.util.Calendar cal) throws SQLException { return getTime(findColumn(c),cal); } - + public Timestamp getTimestamp(String c,java.util.Calendar cal) throws SQLException { return getTimestamp(findColumn(c),cal); } - + public int getFetchDirection() throws SQLException { - throw org.postgresql.Driver.notImplemented(); + // new in 7.1: PostgreSQL normally sends rows first->last + return FETCH_FORWARD; } - + public int getFetchSize() throws SQLException { - throw org.postgresql.Driver.notImplemented(); + // new in 7.1: In this implementation we return the entire result set, so + // here return the number of rows we have. Sub-classes can return a proper + // value + return rows.size(); } - - public int getKeysetSize() throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - + public Object getObject(String columnName,java.util.Map map) throws SQLException { return getObject(findColumn(columnName),map); } - + + /** + * This checks against map for the type of column i, and if found returns + * an object based on that mapping. The class must implement the SQLData + * interface. + */ public Object getObject(int i,java.util.Map map) throws SQLException { - throw org.postgresql.Driver.notImplemented(); + /* In preparation + SQLInput s = new PSQLInput(this,i); + String t = getTypeName(i); + SQLData o = (SQLData) map.get(t); + // If the type is not in the map, then pass to the existing code + if(o==null) + return getObject(i); + o.readSQL(s,t); + return o; + */throw org.postgresql.Driver.notImplemented(); } - + public Ref getRef(String columnName) throws SQLException { return getRef(findColumn(columnName)); } - + public Ref getRef(int i) throws SQLException { - throw org.postgresql.Driver.notImplemented(); + // new in 7.1: The backend doesn't yet have SQL3 REF types + throw new PSQLException("postgresql.psqlnotimp"); } - + public int getRow() throws SQLException { return current_row + 1; } - + // This one needs some thought, as not all ResultSets come from a statement public java.sql.Statement getStatement() throws SQLException { - throw org.postgresql.Driver.notImplemented(); + return statement; } - + public int getType() throws SQLException { - throw org.postgresql.Driver.notImplemented(); + // New in 7.1. This implementation allows scrolling but is not able to + // see any changes. Sub-classes may overide this to return a more + // meaningful result. + return TYPE_SCROLL_INSENSITIVE; } - + public void insertRow() throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public boolean isAfterLast() throws SQLException { return (current_row >= rows.size() && rows.size() > 0); @@ -1074,17 +1117,17 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu this_row = (byte [][])rows.elementAt(current_row); return true; } - + public void moveToCurrentRow() throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void moveToInsertRow() throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public boolean previous() throws SQLException { if (--current_row < 0) @@ -1092,49 +1135,49 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu this_row = (byte [][])rows.elementAt(current_row); return true; } - + public void refreshRow() throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + // Peter: Implemented in 7.0 public boolean relative(int rows) throws SQLException { //have to add 1 since absolute expects a 1-based index return absolute(current_row+1+rows); } - + public boolean rowDeleted() throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public boolean rowInserted() throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public boolean rowUpdated() throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void setFetchDirection(int direction) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void setFetchSize(int rows) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void setKeysetSize(int keys) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateAsciiStream(int columnIndex, java.io.InputStream x, int length @@ -1142,7 +1185,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { throw org.postgresql.Driver.notImplemented(); } - + public void updateAsciiStream(String columnName, java.io.InputStream x, int length @@ -1150,21 +1193,21 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { updateAsciiStream(findColumn(columnName),x,length); } - + public void updateBigDecimal(int columnIndex, java.math.BigDecimal x ) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateBigDecimal(String columnName, java.math.BigDecimal x ) throws SQLException { updateBigDecimal(findColumn(columnName),x); } - + public void updateBinaryStream(int columnIndex, java.io.InputStream x, int length @@ -1172,7 +1215,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { throw org.postgresql.Driver.notImplemented(); } - + public void updateBinaryStream(String columnName, java.io.InputStream x, int length @@ -1180,37 +1223,37 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { updateBinaryStream(findColumn(columnName),x,length); } - + public void updateBoolean(int columnIndex,boolean x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateBoolean(String columnName,boolean x) throws SQLException { updateBoolean(findColumn(columnName),x); } - + public void updateByte(int columnIndex,byte x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateByte(String columnName,byte x) throws SQLException { updateByte(findColumn(columnName),x); } - + public void updateBytes(String columnName,byte[] x) throws SQLException { updateBytes(findColumn(columnName),x); } - + public void updateBytes(int columnIndex,byte[] x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateCharacterStream(int columnIndex, java.io.Reader x, int length @@ -1218,7 +1261,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { throw org.postgresql.Driver.notImplemented(); } - + public void updateCharacterStream(String columnName, java.io.Reader x, int length @@ -1226,131 +1269,146 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu { updateCharacterStream(findColumn(columnName),x,length); } - + public void updateDate(int columnIndex,java.sql.Date x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateDate(String columnName,java.sql.Date x) throws SQLException { updateDate(findColumn(columnName),x); } - + public void updateDouble(int columnIndex,double x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateDouble(String columnName,double x) throws SQLException { updateDouble(findColumn(columnName),x); } - + public void updateFloat(int columnIndex,float x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateFloat(String columnName,float x) throws SQLException { updateFloat(findColumn(columnName),x); } - + public void updateInt(int columnIndex,int x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateInt(String columnName,int x) throws SQLException { updateInt(findColumn(columnName),x); } - + public void updateLong(int columnIndex,long x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateLong(String columnName,long x) throws SQLException { updateLong(findColumn(columnName),x); } - + public void updateNull(int columnIndex) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateNull(String columnName) throws SQLException { updateNull(findColumn(columnName)); } - + public void updateObject(int columnIndex,Object x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateObject(String columnName,Object x) throws SQLException { updateObject(findColumn(columnName),x); } - + public void updateObject(int columnIndex,Object x,int scale) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateObject(String columnName,Object x,int scale) throws SQLException { updateObject(findColumn(columnName),x,scale); } - + public void updateRow() throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateShort(int columnIndex,short x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateShort(String columnName,short x) throws SQLException { updateShort(findColumn(columnName),x); } - + public void updateString(int columnIndex,String x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateString(String columnName,String x) throws SQLException { updateString(findColumn(columnName),x); } - + public void updateTime(int columnIndex,Time x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateTime(String columnName,Time x) throws SQLException { updateTime(findColumn(columnName),x); } - + public void updateTimestamp(int columnIndex,Timestamp x) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - + public void updateTimestamp(String columnName,Timestamp x) throws SQLException { updateTimestamp(findColumn(columnName),x); } - + + // helper method. Throws an SQLException when an update is not possible + public void notUpdateable() throws SQLException + { + throw new PSQLException("postgresql.noupdate"); + } + + /** + * This is called by Statement to register itself with this statement. + * It's used currently by getStatement() but may also with the new core + * package. + */ + public void setStatement(org.postgresql.Statement statement) { + this.statement=statement; + } + } diff --git a/src/interfaces/jdbc/org/postgresql/util/PSQLException.java b/src/interfaces/jdbc/org/postgresql/util/PSQLException.java index 1f206addce..fbfca8e228 100644 --- a/src/interfaces/jdbc/org/postgresql/util/PSQLException.java +++ b/src/interfaces/jdbc/org/postgresql/util/PSQLException.java @@ -10,10 +10,10 @@ import java.util.*; public class PSQLException extends SQLException { private String message; - + // Cache for future errors static ResourceBundle bundle; - + /** * This provides the same functionality to SQLException * @param error Error string @@ -22,7 +22,7 @@ public class PSQLException extends SQLException super(); translate(error,null); } - + /** * A more generic entry point. * @param error Error string or standard message id @@ -33,7 +33,7 @@ public class PSQLException extends SQLException //super(); translate(error,args); } - + /** * Helper version for 1 arg */ @@ -44,7 +44,7 @@ public class PSQLException extends SQLException argv[0] = arg; translate(error,argv); } - + /** * Helper version for 2 args */ @@ -56,7 +56,7 @@ public class PSQLException extends SQLException argv[1] = arg2; translate(error,argv); } - + /** * This does the actual translation */ @@ -70,7 +70,7 @@ public class PSQLException extends SQLException message = id; } } - + if (bundle != null) { // Now look up a localized message. If one is not found, then use // the supplied message instead. @@ -81,13 +81,13 @@ public class PSQLException extends SQLException message = id; } } - + // Expand any arguments - if(args!=null) + if(args!=null && message != null) message = MessageFormat.format(message,args); - + } - + /** * Overides Throwable */ @@ -95,7 +95,7 @@ public class PSQLException extends SQLException { return message; } - + /** * Overides Throwable */ @@ -103,7 +103,7 @@ public class PSQLException extends SQLException { return message; } - + /** * Overides Object */ @@ -111,5 +111,5 @@ public class PSQLException extends SQLException { return message; } - + }