diff --git a/src/interfaces/jdbc/CHANGELOG b/src/interfaces/jdbc/CHANGELOG index a11df412b9..2f3dfe2eb2 100644 --- a/src/interfaces/jdbc/CHANGELOG +++ b/src/interfaces/jdbc/CHANGELOG @@ -1,3 +1,42 @@ +Tue Dec 29 15:45:00 GMT 1998 + - Refreshed the README (which was way out of date) + +Tue Dec 29 15:45:00 GMT 1998 + - Finished adding the additional methods into the JDBC2 driver. + - Had to add some explicit package references for the JDK1.2 Javac to + cope with the driver + +Tue Dec 29 12:40:00 GMT 1998 + - Fixed package imports and some references to java.sql.ResultSet in + various files. Compiled and tested the JDBC1 driver. + +Mon Dec 28 19:01:37 GMT 1998 + - created a new package postgresql.jdbc2 which will contain the JDBC 2 + specific classes. A similar new package (postgresql.jdbc1) has been + created to hold the JDBC 1 specific classes. + - modified Makefile to allow compilation of the JDBC 1 & 2 drivers, + with the possibility of building a dual-spec driver. + - changed the version number in postgresql.Driver to 6.5 + - modified postgresql.Driver class to initiate the correct driver when + used under a 1.1 or 1.2+ JVM. + - postgresql.Connection and postgresql.jdbc2.Connection now extends the + new class postgresql.ConnectionStub, which allows us to dynamically + open the JDBC1 or JDBC2 drivers. + - enabled compilation of the driver under Win32 when using the Make + from the CygWin package (Cygnus B20.1 was used). + - To make future development easier (now we have 2 specifications to + work with) the following classes have moved from the postgresql to + the postgresql.jdbc1 package: + CallableStatement Connection + DatabaseMetaData PreparedStatement + ResultSet ResultSetMetaData + Statement + Some of these classes have common code that is not dependent on + either JDBC specification. These common code are still in the + postgresql package. + Ie: postgresql.jdbc1.Connection extends postgresql.Connection + and postgresql.jdbc2.Connection extends postgresql.Connection + Web Oct 7 22:00:00 BST 1998 - removed syncronised from Connection.ExecSQL(). See next entry. - added new syncronised locking in the Connection.ExecSQL() and @@ -82,4 +121,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 - \ No newline at end of file + diff --git a/src/interfaces/jdbc/Implementation b/src/interfaces/jdbc/Implementation new file mode 100644 index 0000000000..05ceee2a3c --- /dev/null +++ b/src/interfaces/jdbc/Implementation @@ -0,0 +1,123 @@ +This short document is provided to help programmers through the internals of +the PostgreSQL JDBC driver. + +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. + +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. + +postgresql.jar +-------------- + +This jar file is produced by make, 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. + +Importing packages +------------------ + +In user code, you may have to import one or more packages, if and only if you +are using the non jdbc extensions (like FastPath, or LargeObject). + +DO NOT import the postgresql, postgresql.jdbc1 or postgresql.jdbc2 packages! + +Internally, some classes will import the packages when there is a link between +them and the other packages. However, the above rule still applies. It's there +because Javac becomes confused between the different places that similar class +names are present. + +However, there are places where they need to refer to classes in the postgresql +package. In this case, import the individual classes, and not the entire +package. + +ie: import postgresql.Field + + NOT import postgresql.* + +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 + + +Package postgresql +------------------ + +This package holds the core classes. + +Driver registers the driver when it's loaded, and determines which + Connection class (in jdbc1 or jdbc2 packages) to use when + connecting to a database. + +Field Used internally to represent a Field +PG_Stream Used internally to manage the network stream. + + These classes contains common code that is not dependent to the + two JDBC specifications. + +Connection Common code used in Connections, mainly Network Protocol stuff. +ResultSet Common code used in ResultSet's + +Package postgresql.fastpath +--------------------------- + +Fastpath Handles executing a function on the PostgreSQL Backend +FastpathArg Defines an argument for a function call + +Package postgresql.geometric +---------------------------- + +PGbox Maps to postgresql type box +PGcircle Maps to postgresql type circle +PGline Maps to postgresql type line +PGlseg Maps to postgresql type lseg +PGpath Maps to postgresql type path +PGpoint Maps to postgresql type point +PGpolygon Maps to postgresql type polygon + +Package 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 +------------------------ + +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 +------------------------------ + +LargeObject Represents an open LargeObject +LargeObjectManager Handles the opening and deleting of LargeObjects + +Package postgresql.util +----------------------- + +PGmoney Maps to postgresql type money +PGobject Used to represent postgresql types that have no Java equivalent +PGtokenizer Helper class for the geometric types +Serialize Used to serialise Java objects into tabes, rather than Blobs +UnixCrypt Used to handle crypt authentication + diff --git a/src/interfaces/jdbc/Makefile b/src/interfaces/jdbc/Makefile index bda001e8b0..611687dc9a 100644 --- a/src/interfaces/jdbc/Makefile +++ b/src/interfaces/jdbc/Makefile @@ -4,12 +4,10 @@ # Makefile for Java JDBC interface # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/Makefile,v 1.10 1998/10/08 00:38:18 momjian Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/Makefile,v 1.11 1999/01/17 04:51:49 momjian Exp $ # #------------------------------------------------------------------------- -# These are commented out, but would be included in the postgresql source - FIND = find JAR = jar JAVA = java @@ -24,7 +22,10 @@ RM = rm -f .SUFFIXES: .class .java .PHONY: all clean doc examples -all: postgresql.jar +# In 6.5, the all rule builds the makeVersion class which then calls make using +# the jdbc1 or jdbc2 rules +all: makeVersion.class + make $$($(JAVA) makeVersion) @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 @@ -37,7 +38,9 @@ all: postgresql.jar @echo "under unix for HotJava), and add a line containing" @echo jdbc.drivers=postgresql.Driver @echo - @echo More details are in the README file. + @echo More details are in the README file and in the main postgresql + @echo documentation. + @echo @echo ------------------------------------------------------------ @echo To build the examples, type: @echo " make examples" @@ -56,39 +59,83 @@ doc: # These classes form the driver. These, and only these are placed into # the jar file. -OBJS= postgresql/CallableStatement.class \ - postgresql/Connection.class \ - postgresql/DatabaseMetaData.class \ - postgresql/Driver.class \ - postgresql/Field.class \ - postgresql/PG_Stream.class \ - postgresql/PreparedStatement.class \ - postgresql/ResultSet.class \ - postgresql/ResultSetMetaData.class \ - postgresql/Statement.class \ - postgresql/fastpath/Fastpath.class \ - postgresql/fastpath/FastpathArg.class \ - postgresql/geometric/PGbox.class \ - postgresql/geometric/PGcircle.class \ - postgresql/geometric/PGline.class \ - postgresql/geometric/PGlseg.class \ - postgresql/geometric/PGpath.class \ - postgresql/geometric/PGpoint.class \ - postgresql/geometric/PGpolygon.class \ - postgresql/largeobject/LargeObject.class \ - postgresql/largeobject/LargeObjectManager.class \ - postgresql/util/PGmoney.class \ - postgresql/util/PGobject.class \ - postgresql/util/PGtokenizer.class \ - postgresql/util/Serialize.class \ - postgresql/util/UnixCrypt.class +OBJ_COMMON= postgresql/Connection.class \ + postgresql/Driver.class \ + postgresql/Field.class \ + postgresql/PG_Stream.class \ + postgresql/ResultSet.class \ + postgresql/fastpath/Fastpath.class \ + postgresql/fastpath/FastpathArg.class \ + postgresql/geometric/PGbox.class \ + postgresql/geometric/PGcircle.class \ + postgresql/geometric/PGline.class \ + postgresql/geometric/PGlseg.class \ + postgresql/geometric/PGpath.class \ + postgresql/geometric/PGpoint.class \ + postgresql/geometric/PGpolygon.class \ + postgresql/largeobject/LargeObject.class \ + postgresql/largeobject/LargeObjectManager.class \ + postgresql/util/PGmoney.class \ + postgresql/util/PGobject.class \ + postgresql/util/PGtokenizer.class \ + postgresql/util/Serialize.class \ + postgresql/util/UnixCrypt.class -# If you have problems with the first line, try the second one. -# This is needed when compiling under Solaris, as the solaris sh doesn't -# recognise $( ) -postgresql.jar: $(OBJS) +# These files are unique to the JDBC 1 (JDK 1.1) driver +OBJ_JDBC1= postgresql/jdbc1/CallableStatement.class \ + postgresql/jdbc1/Connection.class \ + postgresql/jdbc1/DatabaseMetaData.class \ + postgresql/jdbc1/PreparedStatement.class \ + postgresql/jdbc1/ResultSet.class \ + postgresql/jdbc1/ResultSetMetaData.class \ + postgresql/jdbc1/Statement.class + +# These files are unique to the JDBC 2 (JDK 2 nee 1.2) driver +OBJ_JDBC2= postgresql/jdbc2/ResultSet.class \ + postgresql/jdbc2/PreparedStatement.class \ + postgresql/jdbc2/CallableStatement.class \ + postgresql/jdbc2/Connection.class \ + postgresql/jdbc2/DatabaseMetaData.class \ + postgresql/jdbc2/ResultSetMetaData.class \ + postgresql/jdbc2/Statement.class + +# This rule should never occur, but will be called when makeVersion fails to +# understand the java.version property correctly. +jdbc0: + @echo + @echo FATAL ERROR! + @echo + @echo makeVersion has not been able to determine what version of + @echo the JDK you are using, and hence what version of the driver + @echo to compile. + @echo + @echo There are two versions available, one that conforms to the + @echo JDBC 1 specification, and one to the JDBC 2 specification. + @echo + @echo To build the driver for JDBC 1 (usually for JDK 1.1 thru 1.1.7) + @echo then type: make jdbc1 + @echo + @echo To build the driver for JDBC 2 (usually for JDK 1.2 and later) + @echo then type: make jdbc2 + @echo + @echo If you still have problems, then please email the interfaces + @echo or bugs lists, or better still to me direct (peter@retep.org.uk) + @echo + +# This rule builds the JDBC1 compliant driver +jdbc1: $(OBJ_COMMON) $(OBJ_JDBC1) postgresql.jar + +# This rule builds the JDBC2 compliant driver +jdbc2: $(OBJ_COMMON) $(OBJ_JDBC2) postgresql.jar + +# If you have problems with this rule, replace the $( ) with ` ` as some +# shells (mainly sh under Solaris) doesn't recognise $( ) +# +# Note: This works by storing all compiled classes under the postgresql +# directory. We use this later for compiling the dual-mode driver. +# +postgresql.jar: $(OBJ) $(OBJ_COMMON) $(JAR) -c0f $@ $$($(FIND) postgresql -name "*.class" -print) -# $(JAR) -c0f $@ `$(FIND) postgresql -name "*.class" -print` # This rule removes any temporary and compiled files from the source tree. clean: diff --git a/src/interfaces/jdbc/README b/src/interfaces/jdbc/README index 035ad008bf..f1e3fb8c2a 100644 --- a/src/interfaces/jdbc/README +++ b/src/interfaces/jdbc/README @@ -15,8 +15,11 @@ list: http://www.postgresql.org -By the time V6.3 is released, full documentation will be on the web, and in -the distribution. +When PostgreSQL V6.4 was released, full documentation for the driver was +included in the main documentation tree (under the doc directory). + +This file was finally amended on December 29 1998 to account for the major +changes made to the driver since V6.4 was released. --------------------------------------------------------------------------- @@ -28,14 +31,35 @@ This will compile the driver, and build a .jar file (Java ARchive). REMEMBER: once you have compiled the driver, it will work on ALL platforms that support the JDK 1.1 api or later. +The V6.5 driver introduced support for the JDBC2 specification (which is used +with JDK 1.2 api and later). This caused us some problems because classes +written for JDBC1 and JDBC2 are not compatible, so a large chunk of the +driver had to be re-written to accomodate this. + +Running make will build a .jar file (postgresql.jar) which contains the driver. +That jar file will contain the driver for _your_ version of the JDK. That is, +if you run make using JDK 1.1.7, then you will get the JDBC1 driver. If you +run using 1.2 then you will get the JDBC2 driver. + +Tip: If you want the driver to run on both JDBC1 or JDBC2, first compile under +JDK 1.1.x, then recompile under JDK 1.2. + +In testing, I've done this using 1.1.6 (running under linux), and running make +on my Win95 based Laptop (CygWin B20.1 was used to get a GNUMake - and a +decent shell {bash}). + +When the .jar file is built, it includes all the classes under postgresql, and +the driver automatically selects the correct classes. + 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 just one or two calls to javac. - If, while compiling a class, javac needs another class that's not compiled, - it will compile it automatically. This reduces the numer of calls to javac - that make has to do. +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. Possible problems @@ -47,6 +71,9 @@ postgresql/Driver.java:87: interface java.sql.Connection is an interface. It can This is caused by not having the current directory in your CLASSPATH. Under Linux/Solaris, unset the CLASSPATH environment variable, and rerun make. +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/ + --------------------------------------------------------------------------- INSTALLING THE DRIVER @@ -120,23 +147,11 @@ them to the URL. eg: jdbc:postgresql:database?user=me jdbc:postgresql:database?user=me&password=mypass -By default, the driver doesn't use password authentication. You can enable -this by adding the argument auth. ie: +Previous versions you had to use an auth argument to tell the driver what +authentication scheme to use when connecting to the database. - jdbc:postgresql:database?user=me&password=mypass&auth=password - -or if passing the user & password directly via DriverManager.getConnection(): - - jdbc:postgresql:database?auth=password - -PS: Password authentication is enabled if the value of auth starts with 'p'. - It is case insensitive. - -As of postgresql 6.3, Ident (RFC 1413) authentication is also supported. -Simply use auth=ident in the url. - -Also, as of 6.3, a system property of postgresql.auth is supported. This -defines the default authentication to use. The auth property overides this. +However, this is no longer supported because the database tells the driver +what scheme it's expecting. --------------------------------------------------------------------------- @@ -148,15 +163,6 @@ POSTGRESQL SPECIFICS Date datatype: -The driver now supports US and European date styles (although it is currently -limited to postgres format). - -Basically the US like to format their dates as mm-dd-yyyy, while in Europe, -we like to use dd-mm-yyyy. Postgres supports this by the DateStyle variable. -From psql, you can issue "set datestyle='european';" to set european style, -and "set datestyle='us';" to set the US format. You can see what the current -value for this with "show datestyle;". - The driver now issues the "show datestyle;" query when it first connects, so any call to ResultSet.getDate() how returns the correct date. @@ -171,13 +177,16 @@ ie: s.executeUpdate("show datestyle"); .. s.close(); - + +Please note: This may change later, so that the driver uses the same format +internally (similar to how the ODBC driver works). + ------------------ JDBC supports database specific data types using the getObject() call. The following types have their own Java equivalents supplied by the driver: - box, circle, lseg, path, point, polygon + box, circle, line, lseg, path, point, polygon When using the getObject() method on a resultset, it returns a PG_Object, which holds the postgres type, and its value. This object also supports @@ -194,10 +203,9 @@ syntax for writing these to the database. --------------------------------------------------------------------------- -Peter T Mount, January 11 1998 -home email: pmount@maidast.demon.co.uk http://www.demon.co.uk/finder -work email: peter@maidstone.gov.uk http://www.maidstone.gov.uk - -Adrian Hall - email: adrian@hottub.org +Peter T Mount, December 29 1998 +home email: pmount@retep.org.uk http://www.retep.org.uk +work email: petermount@it.maidstone.gov.uk or peter@taer.maidstone.gov.uk +PS: Please use the home email whenever possible. If you must contact me at work +then please cc my home one at the same time. diff --git a/src/interfaces/jdbc/makeVersion.java b/src/interfaces/jdbc/makeVersion.java new file mode 100644 index 0000000000..3badfa9354 --- /dev/null +++ b/src/interfaces/jdbc/makeVersion.java @@ -0,0 +1,30 @@ +/** + * This class is used by the makefile to determine which version of the + * JDK is currently in use, and if it's using JDK1.1.x then it returns JDBC1 + * and if later, it returns JDBC2 + * + * $Id: makeVersion.java,v 1.1 1999/01/17 04:51:49 momjian Exp $ + */ +public class makeVersion +{ + public static void main(String[] args) { + String key = "java.version"; + String version = System.getProperty(key); + + //System.out.println(key+" = \""+version+"\""); + + // Tip: use print not println here as println breaks the make that + // comes with CygWin-B20.1 + + if(version.startsWith("1.0")) { + // This will trigger the unknown rule in the makefile + System.out.print("jdbc0"); + } else if(version.startsWith("1.1")) { + // This will trigger the building of the JDBC 1 driver + System.out.print("jdbc1"); + } else { + // This will trigger the building of the JDBC 2 driver + System.out.print("jdbc2"); + } + } +} diff --git a/src/interfaces/jdbc/postgresql/Connection.java b/src/interfaces/jdbc/postgresql/Connection.java index be15b38abe..df354776f7 100644 --- a/src/interfaces/jdbc/postgresql/Connection.java +++ b/src/interfaces/jdbc/postgresql/Connection.java @@ -1,41 +1,28 @@ package postgresql; import java.io.*; -import java.lang.*; -import java.lang.reflect.*; import java.net.*; -import java.util.*; import java.sql.*; +import java.util.*; +import postgresql.Field; import postgresql.fastpath.*; import postgresql.largeobject.*; import postgresql.util.*; /** - * A Connection represents a session with a specific database. Within the - * context of a Connection, SQL statements are executed and results are - * returned. + * $Id: Connection.java,v 1.14 1999/01/17 04:51:50 momjian Exp $ * - *

A Connection's database is able to provide information describing - * its tables, its supported SQL grammar, its stored procedures, the - * capabilities of this connection, etc. This information is obtained - * with the getMetaData method. + * This abstract class is used by postgresql.Driver to open either the JDBC1 or + * JDBC2 versions of the Connection class. * - *

Note: By default, the Connection automatically commits changes - * after executing each statement. If auto-commit has been disabled, an - * explicit commit must be done or database changes will not be saved. - * - * @see java.sql.Connection */ -public class Connection implements java.sql.Connection +public abstract class Connection { // This is the network stream associated with this connection - protected PG_Stream pg_stream; + public PG_Stream pg_stream; // This is set by postgresql.Statement.setMaxRows() - protected int maxrows = 0; // maximum no. of rows; 0 = unlimited - - // This is a cache of the DatabaseMetaData instance for this connection - protected DatabaseMetaData metadata; + public int maxrows = 0; // maximum no. of rows; 0 = unlimited private String PG_HOST; private int PG_PORT; @@ -47,10 +34,10 @@ public class Connection implements java.sql.Connection public boolean CONNECTION_OK = true; public boolean CONNECTION_BAD = false; - private boolean autoCommit = true; - private boolean readOnly = false; + public boolean autoCommit = true; + public boolean readOnly = false; - protected Driver this_driver; + public Driver this_driver; private String this_url; private String cursor = null; // The positioned update cursor name @@ -78,12 +65,12 @@ public class Connection implements java.sql.Connection // 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. - protected Hashtable fieldCache = new Hashtable(); + public Hashtable fieldCache = new Hashtable(); /** * This is the current date style of the backend */ - protected int currentDateStyle; + public int currentDateStyle; /** * This defines the formats for dates, according to the various date styles. @@ -113,29 +100,29 @@ public class Connection implements java.sql.Connection }; // Now handle notices as warnings, so things like "show" now work - protected SQLWarning firstWarning = null; + public SQLWarning firstWarning = null; - /** - * Connect to a PostgreSQL database back end. - * - *

Important Notice - * - *
Although this will connect to the database, user code should open - * the connection via the DriverManager.getConnection() methods only. - * - *
This should only be called from the postgresql.Driver class. - * - * @param host the hostname of the database back end - * @param port the port number of the postmaster process - * @param info a Properties[] thing of the user and password - * @param database the database to connect to - * @param u the URL of the connection - * @param d the Driver instantation of the connection - * @return a valid connection profile - * @exception SQLException if a database access error occurs - */ - public Connection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException - { + /** + * This is called by Class.forName() from within postgresql.Driver + */ + public Connection() + { + } + + /** + * This method actually opens the connection. It is called by Driver. + * + * @param host the hostname of the database back end + * @param port the port number of the postmaster process + * @param info a Properties[] thing of the user and password + * @param database the database to connect to + * @param u the URL of the connection + * @param d the Driver instantation of the connection + * @return a valid connection profile + * @exception SQLException if a database access error occurs + */ + protected void openConnection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException + { // Throw an exception if the user or password properties are missing // This occasionally occurs when the client uses the properties version // of getConnection(), and is a common question on the email lists @@ -262,777 +249,472 @@ public class Connection implements java.sql.Connection // Also, this query replaced the NULL query issued to test the // connection. // - clearWarnings(); + firstWarning = null; ExecSQL("show datestyle"); // Initialise object handling initObjectTypes(); // Mark the connection as ok, and cleanup - clearWarnings(); + firstWarning = null; PG_STATUS = CONNECTION_OK; - } - - /** - * SQL statements without parameters are normally executed using - * Statement objects. If the same SQL statement is executed many - * times, it is more efficient to use a PreparedStatement - * - * @return a new Statement object - * @exception SQLException passed through from the constructor - */ - public java.sql.Statement createStatement() throws SQLException - { - return new Statement(this); - } - - /** - * A SQL statement with or without IN parameters can be pre-compiled - * and stored in a PreparedStatement object. This object can then - * be used to efficiently execute this statement multiple times. - * - * Note: This method is optimized for handling parametric - * SQL statements that benefit from precompilation if the drivers - * supports precompilation. PostgreSQL does not support precompilation. - * In this case, the statement is not sent to the database until the - * PreparedStatement is executed. This has no direct effect on users; - * however it does affect which method throws certain SQLExceptions - * - * @param sql a SQL statement that may contain one or more '?' IN - * parameter placeholders - * @return a new PreparedStatement object containing the pre-compiled - * statement. - * @exception SQLException if a database access error occurs. - */ - public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException - { - return new PreparedStatement(this, sql); - } - - /** - * A SQL stored procedure call statement is handled by creating a - * CallableStatement for it. The CallableStatement provides methods - * for setting up its IN and OUT parameters and methods for executing - * it. - * - * Note: This method is optimised for handling stored procedure - * call statements. Some drivers may send the call statement to the - * database when the prepareCall is done; others may wait until the - * CallableStatement is executed. This has no direct effect on users; - * however, it does affect which method throws certain SQLExceptions - * - * @param sql a SQL statement that may contain one or more '?' parameter - * placeholders. Typically this statement is a JDBC function call - * escape string. - * @return a new CallableStatement object containing the pre-compiled - * SQL statement - * @exception SQLException if a database access error occurs - */ - public java.sql.CallableStatement prepareCall(String sql) throws SQLException - { - throw new SQLException("Callable Statements are not supported at this time"); - // return new CallableStatement(this, sql); - } - - /** - * A driver may convert the JDBC sql grammar into its system's - * native SQL grammar prior to sending it; nativeSQL returns the - * native form of the statement that the driver would have sent. - * - * @param sql a SQL statement that may contain one or more '?' - * parameter placeholders - * @return the native form of this statement - * @exception SQLException if a database access error occurs - */ - public String nativeSQL(String sql) throws SQLException - { - return sql; - } - - /** - * If a connection is in auto-commit mode, than all its SQL - * statements will be executed and committed as individual - * transactions. Otherwise, its SQL statements are grouped - * into transactions that are terminated by either commit() - * or rollback(). By default, new connections are in auto- - * commit mode. The commit occurs when the statement completes - * or the next execute occurs, whichever comes first. In the - * case of statements returning a ResultSet, the statement - * completes when the last row of the ResultSet has been retrieved - * or the ResultSet has been closed. In advanced cases, a single - * statement may return multiple results as well as output parameter - * values. Here the commit occurs when all results and output param - * values have been retrieved. - * - * @param autoCommit - true enables auto-commit; false disables it - * @exception SQLException if a database access error occurs - */ - public void setAutoCommit(boolean autoCommit) throws SQLException - { - if (this.autoCommit == autoCommit) - return; - if (autoCommit) - ExecSQL("end"); - else - ExecSQL("begin"); - this.autoCommit = autoCommit; - } - - /** - * gets the current auto-commit state - * - * @return Current state of the auto-commit mode - * @exception SQLException (why?) - * @see setAutoCommit - */ - public boolean getAutoCommit() throws SQLException - { - return this.autoCommit; - } - - /** - * The method commit() makes all changes made since the previous - * commit/rollback permanent and releases any database locks currently - * held by the Connection. This method should only be used when - * auto-commit has been disabled. (If autoCommit == true, then we - * just return anyhow) - * - * @exception SQLException if a database access error occurs - * @see setAutoCommit - */ - public void commit() throws SQLException - { - if (autoCommit) - return; - ExecSQL("commit"); - autoCommit = true; - ExecSQL("begin"); - autoCommit = false; - } - - /** - * The method rollback() drops all changes made since the previous - * commit/rollback and releases any database locks currently held by - * the Connection. - * - * @exception SQLException if a database access error occurs - * @see commit - */ - public void rollback() throws SQLException - { - if (autoCommit) - return; - ExecSQL("rollback"); - autoCommit = true; - ExecSQL("begin"); - autoCommit = false; - } - - /** - * In some cases, it is desirable to immediately release a Connection's - * database and JDBC resources instead of waiting for them to be - * automatically released (cant think why off the top of my head) - * - * Note: A Connection is automatically closed when it is - * garbage collected. Certain fatal errors also result in a closed - * connection. - * - * @exception SQLException if a database access error occurs - */ - public void close() throws SQLException - { - if (pg_stream != null) - { - try - { - pg_stream.close(); - } catch (IOException e) {} - pg_stream = null; - } - } - - /** - * Tests to see if a Connection is closed - * - * @return the status of the connection - * @exception SQLException (why?) - */ - public boolean isClosed() throws SQLException - { - return (pg_stream == null); - } - - /** - * A connection's database is able to provide information describing - * its tables, its supported SQL grammar, its stored procedures, the - * capabilities of this connection, etc. This information is made - * available through a DatabaseMetaData object. - * - * @return a DatabaseMetaData object for this connection - * @exception SQLException if a database access error occurs - */ - public java.sql.DatabaseMetaData getMetaData() throws SQLException - { - if(metadata==null) - metadata = new DatabaseMetaData(this); - return metadata; - } - - /** - * You can put a connection in read-only mode as a hunt to enable - * database optimizations - * - * Note: setReadOnly cannot be called while in the middle - * of a transaction - * - * @param readOnly - true enables read-only mode; false disables it - * @exception SQLException if a database access error occurs - */ - public void setReadOnly (boolean readOnly) throws SQLException - { - this.readOnly = readOnly; - } - - /** - * Tests to see if the connection is in Read Only Mode. Note that - * we cannot really put the database in read only mode, but we pretend - * we can by returning the value of the readOnly flag - * - * @return true if the connection is read only - * @exception SQLException if a database access error occurs - */ - public boolean isReadOnly() throws SQLException - { - return readOnly; - } - - /** - * A sub-space of this Connection's database may be selected by - * setting a catalog name. If the driver does not support catalogs, - * it will silently ignore this request - * - * @exception SQLException if a database access error occurs - */ - public void setCatalog(String catalog) throws SQLException - { - // No-op - } - - /** - * Return the connections current catalog name, or null if no - * catalog name is set, or we dont support catalogs. - * - * @return the current catalog name or null - * @exception SQLException if a database access error occurs - */ - public String getCatalog() throws SQLException - { - return null; - } - - /** - * You can call this method to try to change the transaction - * isolation level using one of the TRANSACTION_* values. - * - * Note: setTransactionIsolation cannot be called while - * in the middle of a transaction - * - * @param level one of the TRANSACTION_* isolation values with - * the exception of TRANSACTION_NONE; some databases may - * not support other values - * @exception SQLException if a database access error occurs - * @see java.sql.DatabaseMetaData#supportsTransactionIsolationLevel - */ - public void setTransactionIsolation(int level) throws SQLException - { - throw new SQLException("Transaction Isolation Levels are not implemented"); - } - - /** - * Get this Connection's current transaction isolation mode. - * - * @return the current TRANSACTION_* mode value - * @exception SQLException if a database access error occurs - */ - public int getTransactionIsolation() throws SQLException - { - return java.sql.Connection.TRANSACTION_SERIALIZABLE; - } - - /** - * The first warning reported by calls on this Connection is - * returned. - * - * Note: Sebsequent warnings will be changed to this - * SQLWarning - * - * @return the first SQLWarning or null - * @exception SQLException if a database access error occurs - */ - public SQLWarning getWarnings() throws SQLException - { - return firstWarning; - } - - /** - * After this call, getWarnings returns null until a new warning - * is reported for this connection. - * - * @exception SQLException if a database access error occurs - */ - public void clearWarnings() throws SQLException - { - firstWarning = null; - } - - // ********************************************************** - // END OF PUBLIC INTERFACE - // ********************************************************** - - /** - * This adds a warning to the warning chain. - * @param msg message to add - */ - 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 generated by the SQL "show datestyle" - 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;iNote: there does not seem to be any method currently - * in existance to return the update count. - * - * @param sql the SQL statement to be executed - * @return a ResultSet holding the results - * @exception SQLException if a database error occurs - */ - public ResultSet ExecSQL(String sql) throws SQLException - { - // added Oct 7 1998 to give us thread safety. - synchronized(pg_stream) { - - Field[] fields = null; - Vector tuples = new Vector(); - byte[] buf = new byte[sql.length()]; - int fqp = 0; - boolean hfr = false; - String recv_status = null, msg; - SQLException final_error = null; - if (sql.length() > 8192) - throw new SQLException("SQL Statement too long: " + sql); - try - { - pg_stream.SendChar('Q'); - buf = sql.getBytes(); - pg_stream.Send(buf); - pg_stream.SendChar(0); - pg_stream.flush(); - } catch (IOException e) { - throw new SQLException("I/O Error: " + e.toString()); - } - - while (!hfr || fqp > 0) - { - Object tup=null; // holds rows as they are recieved - - int c = pg_stream.ReceiveChar(); - - switch (c) - { - case 'A': // Asynchronous Notify - int pid = pg_stream.ReceiveInteger(4); - msg = pg_stream.ReceiveString(8192); - break; - case 'B': // Binary Data Transfer - if (fields == null) - throw new SQLException("Tuple received before MetaData"); - tup = pg_stream.ReceiveTuple(fields.length, true); - // This implements Statement.setMaxRows() - if(maxrows==0 || tuples.size() 0) - fqp--; - if (fqp == 0) - hfr = true; - break; - case 'N': // Error Notification - addWarning(pg_stream.ReceiveString(4096)); - break; - case 'P': // Portal Name - String pname = pg_stream.ReceiveString(8192); - break; - case 'T': // MetaData Field Description - if (fields != null) - throw new SQLException("Cannot handle multiple result groups"); - fields = ReceiveFields(); - break; - default: - throw new SQLException("Unknown Response Type: " + (char)c); - } + // 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 + */ + 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 generated by the SQL "show datestyle" + 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;iNOTE: This is not part of JDBC, but allows access to - * functions on the postgresql backend itself. - * - *

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

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

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

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

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

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

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

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

It uses an internal Hashtable to get the handling class. If the - * type is not supported, then an instance of postgresql.util.PGobject - * is returned. - * - * You can use the getValue() or setValue() methods to handle the returned - * object. Custom objects can have their own methods. - * - * In 6.4, this is extended to use the postgresql.util.Serialize class to - * allow the Serialization of Java Objects into the database without using - * Blobs. Refer to that class for details on how this new feature works. - * - * @return PGobject for this type, and set to value - * @exception SQLException if value is not correct for this type - * @see postgresql.util.Serialize - */ - protected Object getObject(String type,String value) throws SQLException - { - 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 - if(o == null) { - Serialize ser = new Serialize(this,type); - 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. - // - // This is used to implement the postgresql unique types (like lseg, - // point, etc). - if(o instanceof String) { - // 6.3 style extending PG_Object - PGobject obj = null; - obj = (PGobject)(Class.forName((String)o).newInstance()); - obj.setType(type); - obj.setValue(value); - return (Object)obj; - } else { - // If it's an object, it should be an instance of our Serialize class - // If so, then call it's fetch method. - if(o instanceof Serialize) - return ((Serialize)o).fetch(Integer.parseInt(value)); - } - } catch(SQLException sx) { - // rethrow the exception. Done because we capture any others next - sx.fillInStackTrace(); - throw sx; - } catch(Exception ex) { - throw new SQLException("Failed to create object for "+type+": "+ex); } - // should never be reached - return null; - } - - /** - * This stores an object into the database. - * @param o Object to store - * @return OID of the new rectord - * @exception SQLException if value is not correct for this type - * @see postgresql.util.Serialize - */ - protected int putObject(Object o) throws SQLException - { - 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 - if(x == null) { - Serialize ser = new Serialize(this,type); - 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 SQLException("The object could not be stored. Check that any tables required have already been created in the database."); - - } catch(SQLException sx) { - // rethrow the exception. Done because we capture any others next - sx.fillInStackTrace(); - throw sx; - } catch(Exception ex) { - throw new SQLException("Failed to store object: "+ex); + /** + * @return the date format for the current date style of the backend + */ + public String getDateStyle() + { + return dateStyles[currentDateStyle]; } - } - - /** - * This allows client code to add a handler for one of postgresql's - * more unique data types. - * - *

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

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

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

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

The handling class must extend postgresql.util.PGobject - * - * @see postgresql.util.PGobject - */ - public void addDataType(String type,String name) - { - objectTypes.put(type,name); - } - - // This holds the available types - private Hashtable objectTypes = new Hashtable(); - - // This array contains the types that are supported as standard. - // - // The first entry is the types name on the database, the second - // the full class name of the handling class. - // - private static final String defaultObjectTypes[][] = { - {"box", "postgresql.geometric.PGbox"}, - {"circle", "postgresql.geometric.PGcircle"}, - {"line", "postgresql.geometric.PGline"}, - {"lseg", "postgresql.geometric.PGlseg"}, - {"path", "postgresql.geometric.PGpath"}, - {"point", "postgresql.geometric.PGpoint"}, - {"polygon", "postgresql.geometric.PGpolygon"}, - {"money", "postgresql.util.PGmoney"} - }; - - // This initialises the objectTypes hashtable - private void initObjectTypes() - { - for(int i=0;iNote: there does not seem to be any method currently + * in existance to return the update count. + * + * @param sql the SQL statement to be executed + * @return a ResultSet holding the results + * @exception SQLException if a database error occurs + */ + public java.sql.ResultSet ExecSQL(String sql) throws SQLException + { + // added Oct 7 1998 to give us thread safety. + synchronized(pg_stream) { + + Field[] fields = null; + Vector tuples = new Vector(); + byte[] buf = new byte[sql.length()]; + int fqp = 0; + boolean hfr = false; + String recv_status = null, msg; + SQLException final_error = null; + + if (sql.length() > 8192) + throw new SQLException("SQL Statement too long: " + sql); + try + { + pg_stream.SendChar('Q'); + buf = sql.getBytes(); + pg_stream.Send(buf); + pg_stream.SendChar(0); + pg_stream.flush(); + } catch (IOException e) { + throw new SQLException("I/O Error: " + e.toString()); + } + + while (!hfr || fqp > 0) + { + Object tup=null; // holds rows as they are recieved + + int c = pg_stream.ReceiveChar(); + + switch (c) + { + case 'A': // Asynchronous Notify + int pid = pg_stream.ReceiveInteger(4); + msg = pg_stream.ReceiveString(8192); + break; + case 'B': // Binary Data Transfer + if (fields == null) + throw new SQLException("Tuple received before MetaData"); + tup = pg_stream.ReceiveTuple(fields.length, true); + // This implements Statement.setMaxRows() + if(maxrows==0 || tuples.size() 0) + fqp--; + if (fqp == 0) + hfr = true; + break; + case 'N': // Error Notification + addWarning(pg_stream.ReceiveString(4096)); + break; + case 'P': // Portal Name + String pname = pg_stream.ReceiveString(8192); + break; + case 'T': // MetaData Field Description + if (fields != null) + throw new SQLException("Cannot handle multiple result groups"); + fields = ReceiveFields(); + break; + default: + throw new SQLException("Unknown Response Type: " + (char)c); + } + } + if (final_error != null) + throw final_error; + return getResultSet(this, fields, tuples, recv_status, 1); + //return new ResultSet(this, fields, tuples, recv_status, 1); + } + } + + /** + * Receive the field descriptions from the back end + * + * @return an array of the Field object describing the fields + * @exception SQLException if a database error occurs + */ + private Field[] ReceiveFields() throws SQLException + { + int nf = pg_stream.ReceiveIntegerR(2), i; + Field[] fields = new Field[nf]; + + for (i = 0 ; i < nf ; ++i) + { + String typname = pg_stream.ReceiveString(8192); + int typid = pg_stream.ReceiveIntegerR(4); + int typlen = pg_stream.ReceiveIntegerR(2); + fields[i] = new Field(this, typname, typid, typlen); + } + 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 + * using a positioned update/delete statement that references the + * cursor name. + * + * We support one cursor per connection. + * + * setCursorName sets the cursor name. + * + * @param cursor the cursor name + * @exception SQLException if a database access error occurs + */ + public void setCursorName(String cursor) throws SQLException + { + this.cursor = cursor; + } + + /** + * getCursorName gets the cursor name. + * + * @return the current cursor name + * @exception SQLException if a database access error occurs + */ + public String getCursorName() throws SQLException + { + return cursor; + } + + /** + * We are required to bring back certain information by + * the DatabaseMetaData class. These functions do that. + * + * Method getURL() brings back the URL (good job we saved it) + * + * @return the url + * @exception SQLException just in case... + */ + public String getURL() throws SQLException + { + return this_url; + } + + /** + * Method getUserName() brings back the User Name (again, we + * saved it) + * + * @return the user name + * @exception SQLException just in case... + */ + public String getUserName() throws SQLException + { + return PG_USER; + } + + /** + * This returns the Fastpath API for the current connection. + * + *

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

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

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

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

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

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

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

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

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

It uses an internal Hashtable to get the handling class. If the + * type is not supported, then an instance of postgresql.util.PGobject + * is returned. + * + * You can use the getValue() or setValue() methods to handle the returned + * object. Custom objects can have their own methods. + * + * In 6.4, this is extended to use the postgresql.util.Serialize class to + * allow the Serialization of Java Objects into the database without using + * Blobs. Refer to that class for details on how this new feature works. + * + * @return PGobject for this type, and set to value + * @exception SQLException if value is not correct for this type + * @see postgresql.util.Serialize + */ + public Object getObject(String type,String value) throws SQLException + { + 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 + if(o == null) { + Serialize ser = new Serialize(this,type); + 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. + // + // This is used to implement the postgresql unique types (like lseg, + // point, etc). + if(o instanceof String) { + // 6.3 style extending PG_Object + PGobject obj = null; + obj = (PGobject)(Class.forName((String)o).newInstance()); + obj.setType(type); + obj.setValue(value); + return (Object)obj; + } else { + // If it's an object, it should be an instance of our Serialize class + // If so, then call it's fetch method. + if(o instanceof Serialize) + return ((Serialize)o).fetch(Integer.parseInt(value)); + } + } catch(SQLException sx) { + // rethrow the exception. Done because we capture any others next + sx.fillInStackTrace(); + throw sx; + } catch(Exception ex) { + throw new SQLException("Failed to create object for "+type+": "+ex); + } + + // should never be reached + return null; + } + + /** + * This stores an object into the database. + * @param o Object to store + * @return OID of the new rectord + * @exception SQLException if value is not correct for this type + * @see postgresql.util.Serialize + */ + public int putObject(Object o) throws SQLException + { + 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 + if(x == null) { + Serialize ser = new Serialize(this,type); + 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 SQLException("The object could not be stored. Check that any tables required have already been created in the database."); + + } catch(SQLException sx) { + // rethrow the exception. Done because we capture any others next + sx.fillInStackTrace(); + throw sx; + } catch(Exception ex) { + throw new SQLException("Failed to store object: "+ex); + } + } + + /** + * This allows client code to add a handler for one of postgresql's + * more unique data types. + * + *

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

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

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

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

The handling class must extend postgresql.util.PGobject + * + * @see postgresql.util.PGobject + */ + public void addDataType(String type,String name) + { + objectTypes.put(type,name); + } + + // This holds the available types + private Hashtable objectTypes = new Hashtable(); + + // This array contains the types that are supported as standard. + // + // The first entry is the types name on the database, the second + // the full class name of the handling class. + // + private static final String defaultObjectTypes[][] = { + {"box", "postgresql.geometric.PGbox"}, + {"circle", "postgresql.geometric.PGcircle"}, + {"line", "postgresql.geometric.PGline"}, + {"lseg", "postgresql.geometric.PGlseg"}, + {"path", "postgresql.geometric.PGpath"}, + {"point", "postgresql.geometric.PGpoint"}, + {"polygon", "postgresql.geometric.PGpolygon"}, + {"money", "postgresql.util.PGmoney"} + }; + + // This initialises the objectTypes hashtable + private void initObjectTypes() + { + for(int i=0;iA ResultSet maintains a cursor pointing to its current row of data. - * Initially the cursor is positioned before the first row. The 'next' - * method moves the cursor to the next row. - * - *

The getXXX methods retrieve column values for the current row. You can - * retrieve values either using the index number of the column, or by using - * the name of the column. In general using the column index will be more - * efficient. Columns are numbered from 1. - * - *

For maximum portability, ResultSet columns within each row should be read - * in left-to-right order and each column should be read only once. - * - *

For the getXXX methods, the JDBC driver attempts to convert the - * underlying data to the specified Java type and returns a suitable Java - * value. See the JDBC specification for allowable mappings from SQL types - * to Java types with the ResultSet getXXX methods. - * - *

Column names used as input to getXXX methods are case insenstive. When - * performing a getXXX using a column name, if several columns have the same - * name, then the value of the first matching column will be returned. The - * column name option is designed to be used when column names are used in the - * SQL Query. For columns that are NOT explicitly named in the query, it is - * best to use column numbers. If column names were used there is no way for - * 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 - * next result from a sequence of multiple results. - * - *

The number, types and properties of a ResultSet's columns are provided by - * the ResultSetMetaData object returned by the getMetaData method. - * - * @see ResultSetMetaData - * @see java.sql.ResultSet + * This class implements the common internal methods used by both JDBC 1 and + * JDBC 2 specifications. */ -public class ResultSet implements java.sql.ResultSet +public abstract class ResultSet { - Vector rows; // The results - Field fields[]; // The field descriptions - String status; // Status of the result - int updateCount; // How many rows did we get back? - int current_row; // Our pointer to where we are at - byte[][] this_row; // the current row result - Connection connection; // the connection which we returned from - SQLWarning warnings = null; // The warning chain - boolean wasNullFlag = false; // the flag for wasNull() + protected Vector rows; // The results + protected Field fields[]; // The field descriptions + protected String status; // Status of the result + protected int updateCount; // How many rows did we get back? + protected int current_row; // Our pointer to where we are at + protected byte[][] this_row; // the current row result + protected Connection connection; // the connection which we returned from + protected SQLWarning warnings = null; // The warning chain + protected boolean wasNullFlag = false; // the flag for wasNull() // We can chain multiple resultSets together - this points to // next resultSet in the chain. - private ResultSet next = null; + protected ResultSet next = null; /** * Create a new ResultSet - Note that we create ResultSets to @@ -87,710 +50,7 @@ public class ResultSet implements java.sql.ResultSet this.this_row = null; this.current_row = -1; } - - /** - * A ResultSet is initially positioned before its first row, - * the first call to next makes the first row the current row; - * the second call makes the second row the current row, etc. - * - *

If an input stream from the previous row is open, it is - * implicitly closed. The ResultSet's warning chain is cleared - * when a new row is read - * - * @return true if the new current is valid; false if there are no - * more rows - * @exception SQLException if a database access error occurs - */ - public boolean next() throws SQLException - { - if (++current_row >= rows.size()) - return false; - 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 - * when it is automatically closed. The close method provides this - * immediate release. - * - *

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 - * when it is garbage collected. - * - * @exception SQLException if a database access error occurs - */ - public void close() throws SQLException - { - // No-op - } - - /** - * A column may have the value of SQL NULL; wasNull() reports whether - * the last column read had this special value. Note that you must - * first call getXXX on a column to try to read its value and then - * call wasNull() to find if the value was SQL NULL - * - * @return true if the last column read was SQL NULL - * @exception SQLException if a database access error occurred - */ - public boolean wasNull() throws SQLException - { - return wasNullFlag; - } - - /** - * Get the value of a column in the current row as a Java String - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value, null for SQL NULL - * @exception SQLException if a database access error occurs - */ - public String getString(int columnIndex) throws SQLException - { - //byte[] bytes = getBytes(columnIndex); - // - //if (bytes == null) - //return null; - //return new String(bytes); - if (columnIndex < 1 || columnIndex > fields.length) - throw new SQLException("Column Index out of range"); - wasNullFlag = (this_row[columnIndex - 1] == null); - if(wasNullFlag) - return null; - return new String(this_row[columnIndex - 1]); - } - - /** - * Get the value of a column in the current row as a Java boolean - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value, false for SQL NULL - * @exception SQLException if a database access error occurs - */ - public boolean getBoolean(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - if (s != null) - { - int c = s.charAt(0); - return ((c == 't') || (c == 'T')); - } - return false; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java byte. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public byte getByte(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Byte.parseByte(s); - } catch (NumberFormatException e) { - throw new SQLException("Bad Byte Form: " + s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java short. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public short getShort(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Short.parseShort(s); - } catch (NumberFormatException e) { - throw new SQLException("Bad Short Form: " + s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java int. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public int getInt(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Integer.parseInt(s); - } catch (NumberFormatException e) { - throw new SQLException ("Bad Integer Form: " + s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java long. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public long getLong(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Long.parseLong(s); - } catch (NumberFormatException e) { - throw new SQLException ("Bad Long Form: " + s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java float. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public float getFloat(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Float.valueOf(s).floatValue(); - } catch (NumberFormatException e) { - throw new SQLException ("Bad Float Form: " + s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java double. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public double getDouble(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Double.valueOf(s).doubleValue(); - } catch (NumberFormatException e) { - throw new SQLException ("Bad Double Form: " + s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a - * java.lang.BigDecimal object - * - * @param columnIndex the first column is 1, the second is 2... - * @param scale the number of digits to the right of the decimal - * @return the column value; if the value is SQL NULL, null - * @exception SQLException if a database access error occurs - */ - public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException - { - String s = getString(columnIndex); - BigDecimal val; - - if (s != null) - { - try - { - val = new BigDecimal(s); - } catch (NumberFormatException e) { - throw new SQLException ("Bad BigDecimal Form: " + s); - } - try - { - return val.setScale(scale); - } catch (ArithmeticException e) { - throw new SQLException ("Bad BigDecimal Form: " + s); - } - } - return null; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java byte array. - * - *

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

Be warned If the large object is huge, then you may run out - * of memory. - * - * @param columnIndex the first column is 1, the second is 2, ... - * @return the column value; if the value is SQL NULL, the result - * is null - * @exception SQLException if a database access error occurs - */ - public byte[] getBytes(int columnIndex) throws SQLException - { - if (columnIndex < 1 || columnIndex > fields.length) - throw new SQLException("Column Index out of range"); - wasNullFlag = (this_row[columnIndex - 1] == null); - - // Handle OID's as BLOBS - if(!wasNullFlag) - if( fields[columnIndex - 1].getOID() == 26) { - LargeObjectManager lom = connection.getLargeObjectAPI(); - LargeObject lob = lom.open(getInt(columnIndex)); - byte buf[] = lob.read(lob.size()); - lob.close(); - return buf; - } - - return this_row[columnIndex - 1]; - } - - /** - * Get the value of a column in the current row as a java.sql.Date - * object - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value; null if SQL NULL - * @exception SQLException if a database access error occurs - */ - public java.sql.Date getDate(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - if(s==null) - return null; - SimpleDateFormat df = new SimpleDateFormat(connection.getDateStyle()); - try { - return new java.sql.Date(df.parse(s).getTime()); - } catch (ParseException e) { - throw new SQLException("Bad Date Format: at " + e.getErrorOffset() + " in " + s); - } - } - - /** - * Get the value of a column in the current row as a java.sql.Time - * object - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value; null if SQL NULL - * @exception SQLException if a database access error occurs - */ - public Time getTime(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - if (s.length() != 5 && s.length() != 8) - throw new NumberFormatException("Wrong Length!"); - int hr = Integer.parseInt(s.substring(0,2)); - int min = Integer.parseInt(s.substring(3,5)); - int sec = (s.length() == 5) ? 0 : Integer.parseInt(s.substring(6)); - return new Time(hr, min, sec); - } catch (NumberFormatException e) { - throw new SQLException ("Bad Time Form: " + s); - } - } - return null; // SQL NULL - } - - /** - * 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... - * @return the column value; null if SQL NULL - * @exception SQLException if a database access error occurs - */ - public Timestamp getTimestamp(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzz"); - - if (s != null) - { - int TZ = new Float(s.substring(19)).intValue(); - TZ = TZ * 60 * 60 * 1000; - TimeZone zone = TimeZone.getDefault(); - zone.setRawOffset(TZ); - String nm = zone.getID(); - s = s.substring(0,18) + nm; - try { - java.util.Date d = df.parse(s); - return new Timestamp(d.getTime()); - } catch (ParseException e) { - throw new SQLException("Bad Timestamp Format: at " + e.getErrorOffset() + " in " + s); - } - } - return null; // SQL NULL - } - - /** - * A column value can be retrieved as a stream of ASCII characters - * 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. - * - *

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

We implement an ASCII stream as a Binary stream - we should really - * do the data conversion, but I cannot be bothered to implement this - * right now. - * - * @param columnIndex the first column is 1, the second is 2, ... - * @return a Java InputStream that delivers the database column - * value as a stream of one byte ASCII characters. If the - * value is SQL NULL then the result is null - * @exception SQLException if a database access error occurs - * @see getBinaryStream - */ - public InputStream getAsciiStream(int columnIndex) throws SQLException - { - return getBinaryStream(columnIndex); - } - - /** - * A column value can also be retrieved as a stream of Unicode - * characters. We implement this as a binary stream. - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Java InputStream that delivers the database column value - * as a stream of two byte Unicode characters. If the value is - * SQL NULL, then the result is null - * @exception SQLException if a database access error occurs - * @see getAsciiStream - * @see getBinaryStream - */ - public InputStream getUnicodeStream(int columnIndex) throws SQLException - { - return getBinaryStream(columnIndex); - } - - /** - * A column value can also be retrieved as a binary strea. This - * method is suitable for retrieving LONGVARBINARY values. - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Java InputStream that delivers the database column value - * as a stream of bytes. If the value is SQL NULL, then the result - * is null - * @exception SQLException if a database access error occurs - * @see getAsciiStream - * @see getUnicodeStream - */ - 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. - * - * @param columnName is the SQL name of the column - * @return the column value - * @exception SQLException if a database access error occurs - */ - public String getString(String columnName) throws SQLException - { - 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)); - } - - public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException - { - 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)); - } - - public InputStream getUnicodeStream(String columnName) throws SQLException - { - 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 - * to this SQLWarning. - * - *

The warning chain is automatically cleared each time a new - * row is read. - * - *

Note: This warning chain only covers warnings caused by - * ResultSet methods. Any warnings caused by statement methods - * (such as reading OUT parameters) will be chained on the - * Statement object. - * - * @return the first SQLWarning or null; - * @exception SQLException if a database access error occurs. - */ - public SQLWarning getWarnings() throws SQLException - { - return warnings; - } - - /** - * After this call, getWarnings returns null until a new warning - * is reported for this ResultSet - * - * @exception SQLException if a database access error occurs - */ - public void clearWarnings() throws SQLException - { - warnings = null; - } - - /** - * Get the name of the SQL cursor used by this ResultSet - * - *

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

JDBC supports this SQL feature by providing the name of the - * SQL cursor used by a ResultSet. The current row of a ResulSet - * is also the current row of this SQL cursor. - * - *

Note: If positioned update is not supported, a SQLException - * is thrown. - * - * @return the ResultSet's SQL cursor name. - * @exception SQLException if a database access error occurs - */ - public String getCursorName() throws SQLException - { - return connection.getCursorName(); - } - - /** - * The numbers, types and properties of a ResultSet's columns are - * provided by the getMetaData method - * - * @return a description of the ResultSet's columns - * @exception SQLException if a database access error occurs - */ - public java.sql.ResultSetMetaData getMetaData() throws SQLException - { - return new ResultSetMetaData(rows, fields); - } - - /** - * Get the value of a column in the current row as a Java object - * - *

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

This method may also be used to read database specific abstract - * data types. - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Object holding the column value - * @exception SQLException if a database access error occurs - */ - public Object getObject(int columnIndex) throws SQLException - { - Field field; - - if (columnIndex < 1 || columnIndex > fields.length) - throw new SQLException("Column index out of range"); - 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: - return new Boolean(getBoolean(columnIndex)); - case Types.SMALLINT: - return new Integer(getInt(columnIndex)); - case Types.INTEGER: - return new Integer(getInt(columnIndex)); - case Types.BIGINT: - return new Long(getLong(columnIndex)); - case Types.NUMERIC: - return getBigDecimal(columnIndex, 0); - case Types.REAL: - return new Float(getFloat(columnIndex)); - case Types.DOUBLE: - return new Double(getDouble(columnIndex)); - case Types.CHAR: - case Types.VARCHAR: - return getString(columnIndex); - case Types.DATE: - return getDate(columnIndex); - case Types.TIME: - return getTime(columnIndex); - case Types.TIMESTAMP: - return getTimestamp(columnIndex); - default: - return connection.getObject(field.getTypeName(), getString(columnIndex)); - } - } - - /** - * Get the value of a column in the current row as a Java object - * - *

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

This method may also be used to read database specific abstract - * data types. - * - * @param columnName is the SQL name of the column - * @return a Object holding the column value - * @exception SQLException if a database access error occurs - */ - public Object getObject(String columnName) throws SQLException - { - return getObject(findColumn(columnName)); - } - - /** - * Map a ResultSet column name to a ResultSet column index - * - * @param columnName the name of the column - * @return the column index - * @exception SQLException if a database access error occurs - */ - 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 SQLException ("Column name not found"); - } - - // ************************************************************ - // END OF PUBLIC INTERFACE - // ************************************************************ - /** * We at times need to know if the resultSet we are working * with is the result of an UPDATE, DELETE or INSERT (in which @@ -812,9 +72,9 @@ public class ResultSet implements java.sql.ResultSet * * @return the next ResultSet, or null if there are none */ - public ResultSet getNext() + public java.sql.ResultSet getNext() { - return next; + return (java.sql.ResultSet)next; } /** @@ -887,5 +147,12 @@ public class ResultSet implements java.sql.ResultSet { return fields[field-1].getOID(); } + + /** + * This is part of the JDBC API, but is required by postgresql.Field + */ + public abstract void close() throws SQLException; + public abstract boolean next() throws SQLException; + public abstract String getString(int i) throws SQLException; } diff --git a/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java b/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java index ab702f457f..232f8f0248 100644 --- a/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java +++ b/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java @@ -7,6 +7,9 @@ import java.util.*; import java.sql.*; import postgresql.util.*; +// Important: There are a lot of debug code commented out. Please do not +// delete these. + /** * This class implements the Fastpath api. * @@ -54,7 +57,7 @@ public class Fastpath { this.conn=conn; this.stream=stream; - DriverManager.println("Fastpath initialised"); + //DriverManager.println("Fastpath initialised"); } /** @@ -109,7 +112,7 @@ public class Fastpath Object result = null; // our result while(true) { int in = stream.ReceiveChar(); - DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'"); + //DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'"); switch(in) { case 'V': @@ -120,7 +123,7 @@ public class Fastpath // case 'G': int sz = stream.ReceiveIntegerR(4); - DriverManager.println("G: size="+sz); //debug + //DriverManager.println("G: size="+sz); //debug // Return an Integer if if(resulttype) @@ -149,7 +152,7 @@ public class Fastpath // Here we simply return res, which would contain the result // processed earlier. If no result, this already contains null case '0': - DriverManager.println("returning "+result); + //DriverManager.println("returning "+result); return result; default: @@ -181,7 +184,7 @@ public class Fastpath */ public Object fastpath(String name,boolean resulttype,FastpathArg[] args) throws SQLException { - DriverManager.println("Fastpath: calling "+name); + //DriverManager.println("Fastpath: calling "+name); return fastpath(getID(name),resulttype,args); } diff --git a/src/interfaces/jdbc/postgresql/jdbc1/CallableStatement.java b/src/interfaces/jdbc/postgresql/jdbc1/CallableStatement.java new file mode 100644 index 0000000000..2a90157d01 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc1/CallableStatement.java @@ -0,0 +1,308 @@ +package postgresql.jdbc1; + +// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 2 class in the +// postgresql.jdbc2 package. + +import java.sql.*; +import java.math.*; + +/** + * CallableStatement is used to execute SQL stored procedures. + * + *

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

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

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

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

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

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

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

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

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

See the JDBC spec's "Dynamic Programming" chapter for details. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return A java.lang.Object holding the OUT parameter value. + * @exception SQLException if a database-access error occurs. + */ + public Object getObject(int parameterIndex) + throws SQLException { + return null; + } +} + diff --git a/src/interfaces/jdbc/postgresql/jdbc1/Connection.java b/src/interfaces/jdbc/postgresql/jdbc1/Connection.java new file mode 100644 index 0000000000..790dfa6ab0 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc1/Connection.java @@ -0,0 +1,366 @@ +package postgresql.jdbc1; + +// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 2 class in the +// postgresql.jdbc2 package. + +import java.io.*; +import java.lang.*; +import java.lang.reflect.*; +import java.net.*; +import java.util.*; +import java.sql.*; +import postgresql.Field; +import postgresql.fastpath.*; +import postgresql.largeobject.*; +import postgresql.util.*; + +/** + * $Id: Connection.java,v 1.1 1999/01/17 04:51:53 momjian Exp $ + * + * A Connection represents a session with a specific database. Within the + * context of a Connection, SQL statements are executed and results are + * returned. + * + *

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

Note: By default, the Connection automatically commits changes + * after executing each statement. If auto-commit has been disabled, an + * explicit commit must be done or database changes will not be saved. + * + * @see java.sql.Connection + */ +public class Connection extends postgresql.Connection implements java.sql.Connection +{ + // This is a cache of the DatabaseMetaData instance for this connection + protected DatabaseMetaData metadata; + + /** + * SQL statements without parameters are normally executed using + * Statement objects. If the same SQL statement is executed many + * times, it is more efficient to use a PreparedStatement + * + * @return a new Statement object + * @exception SQLException passed through from the constructor + */ + public java.sql.Statement createStatement() throws SQLException + { + return new Statement(this); + } + + /** + * A SQL statement with or without IN parameters can be pre-compiled + * and stored in a PreparedStatement object. This object can then + * be used to efficiently execute this statement multiple times. + * + * Note: This method is optimized for handling parametric + * SQL statements that benefit from precompilation if the drivers + * supports precompilation. PostgreSQL does not support precompilation. + * In this case, the statement is not sent to the database until the + * PreparedStatement is executed. This has no direct effect on users; + * however it does affect which method throws certain SQLExceptions + * + * @param sql a SQL statement that may contain one or more '?' IN + * parameter placeholders + * @return a new PreparedStatement object containing the pre-compiled + * statement. + * @exception SQLException if a database access error occurs. + */ + public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException + { + return new PreparedStatement(this, sql); + } + + /** + * A SQL stored procedure call statement is handled by creating a + * CallableStatement for it. The CallableStatement provides methods + * for setting up its IN and OUT parameters and methods for executing + * it. + * + * Note: This method is optimised for handling stored procedure + * call statements. Some drivers may send the call statement to the + * database when the prepareCall is done; others may wait until the + * CallableStatement is executed. This has no direct effect on users; + * however, it does affect which method throws certain SQLExceptions + * + * @param sql a SQL statement that may contain one or more '?' parameter + * placeholders. Typically this statement is a JDBC function call + * escape string. + * @return a new CallableStatement object containing the pre-compiled + * SQL statement + * @exception SQLException if a database access error occurs + */ + public java.sql.CallableStatement prepareCall(String sql) throws SQLException + { + throw new SQLException("Callable Statements are not supported at this time"); + // return new CallableStatement(this, sql); + } + + /** + * A driver may convert the JDBC sql grammar into its system's + * native SQL grammar prior to sending it; nativeSQL returns the + * native form of the statement that the driver would have sent. + * + * @param sql a SQL statement that may contain one or more '?' + * parameter placeholders + * @return the native form of this statement + * @exception SQLException if a database access error occurs + */ + public String nativeSQL(String sql) throws SQLException + { + return sql; + } + + /** + * If a connection is in auto-commit mode, than all its SQL + * statements will be executed and committed as individual + * transactions. Otherwise, its SQL statements are grouped + * into transactions that are terminated by either commit() + * or rollback(). By default, new connections are in auto- + * commit mode. The commit occurs when the statement completes + * or the next execute occurs, whichever comes first. In the + * case of statements returning a ResultSet, the statement + * completes when the last row of the ResultSet has been retrieved + * or the ResultSet has been closed. In advanced cases, a single + * statement may return multiple results as well as output parameter + * values. Here the commit occurs when all results and output param + * values have been retrieved. + * + * @param autoCommit - true enables auto-commit; false disables it + * @exception SQLException if a database access error occurs + */ + public void setAutoCommit(boolean autoCommit) throws SQLException + { + if (this.autoCommit == autoCommit) + return; + if (autoCommit) + ExecSQL("end"); + else + ExecSQL("begin"); + this.autoCommit = autoCommit; + } + + /** + * gets the current auto-commit state + * + * @return Current state of the auto-commit mode + * @exception SQLException (why?) + * @see setAutoCommit + */ + public boolean getAutoCommit() throws SQLException + { + return this.autoCommit; + } + + /** + * The method commit() makes all changes made since the previous + * commit/rollback permanent and releases any database locks currently + * held by the Connection. This method should only be used when + * auto-commit has been disabled. (If autoCommit == true, then we + * just return anyhow) + * + * @exception SQLException if a database access error occurs + * @see setAutoCommit + */ + public void commit() throws SQLException + { + if (autoCommit) + return; + ExecSQL("commit"); + autoCommit = true; + ExecSQL("begin"); + autoCommit = false; + } + + /** + * The method rollback() drops all changes made since the previous + * commit/rollback and releases any database locks currently held by + * the Connection. + * + * @exception SQLException if a database access error occurs + * @see commit + */ + public void rollback() throws SQLException + { + if (autoCommit) + return; + ExecSQL("rollback"); + autoCommit = true; + ExecSQL("begin"); + autoCommit = false; + } + + /** + * In some cases, it is desirable to immediately release a Connection's + * database and JDBC resources instead of waiting for them to be + * automatically released (cant think why off the top of my head) + * + * Note: A Connection is automatically closed when it is + * garbage collected. Certain fatal errors also result in a closed + * connection. + * + * @exception SQLException if a database access error occurs + */ + public void close() throws SQLException + { + if (pg_stream != null) + { + try + { + pg_stream.close(); + } catch (IOException e) {} + pg_stream = null; + } + } + + /** + * Tests to see if a Connection is closed + * + * @return the status of the connection + * @exception SQLException (why?) + */ + public boolean isClosed() throws SQLException + { + return (pg_stream == null); + } + + /** + * A connection's database is able to provide information describing + * its tables, its supported SQL grammar, its stored procedures, the + * capabilities of this connection, etc. This information is made + * available through a DatabaseMetaData object. + * + * @return a DatabaseMetaData object for this connection + * @exception SQLException if a database access error occurs + */ + public java.sql.DatabaseMetaData getMetaData() throws SQLException + { + if(metadata==null) + metadata = new DatabaseMetaData(this); + return metadata; + } + + /** + * You can put a connection in read-only mode as a hunt to enable + * database optimizations + * + * Note: setReadOnly cannot be called while in the middle + * of a transaction + * + * @param readOnly - true enables read-only mode; false disables it + * @exception SQLException if a database access error occurs + */ + public void setReadOnly (boolean readOnly) throws SQLException + { + this.readOnly = readOnly; + } + + /** + * Tests to see if the connection is in Read Only Mode. Note that + * we cannot really put the database in read only mode, but we pretend + * we can by returning the value of the readOnly flag + * + * @return true if the connection is read only + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly() throws SQLException + { + return readOnly; + } + + /** + * A sub-space of this Connection's database may be selected by + * setting a catalog name. If the driver does not support catalogs, + * it will silently ignore this request + * + * @exception SQLException if a database access error occurs + */ + public void setCatalog(String catalog) throws SQLException + { + // No-op + } + + /** + * Return the connections current catalog name, or null if no + * catalog name is set, or we dont support catalogs. + * + * @return the current catalog name or null + * @exception SQLException if a database access error occurs + */ + public String getCatalog() throws SQLException + { + return null; + } + + /** + * You can call this method to try to change the transaction + * isolation level using one of the TRANSACTION_* values. + * + * Note: setTransactionIsolation cannot be called while + * in the middle of a transaction + * + * @param level one of the TRANSACTION_* isolation values with + * the exception of TRANSACTION_NONE; some databases may + * not support other values + * @exception SQLException if a database access error occurs + * @see java.sql.DatabaseMetaData#supportsTransactionIsolationLevel + */ + public void setTransactionIsolation(int level) throws SQLException + { + throw new SQLException("Transaction Isolation Levels are not implemented"); + } + + /** + * Get this Connection's current transaction isolation mode. + * + * @return the current TRANSACTION_* mode value + * @exception SQLException if a database access error occurs + */ + public int getTransactionIsolation() throws SQLException + { + return java.sql.Connection.TRANSACTION_SERIALIZABLE; + } + + /** + * The first warning reported by calls on this Connection is + * returned. + * + * Note: Sebsequent warnings will be changed to this + * SQLWarning + * + * @return the first SQLWarning or null + * @exception SQLException if a database access error occurs + */ + public SQLWarning getWarnings() throws SQLException + { + return firstWarning; + } + + /** + * After this call, getWarnings returns null until a new warning + * is reported for this connection. + * + * @exception SQLException if a database access error occurs + */ + public void clearWarnings() throws SQLException + { + firstWarning = null; + } + + /** + * This overides the method in postgresql.Connection and returns a + * ResultSet. + */ + protected java.sql.ResultSet getResultSet(postgresql.Connection conn, Field[] fields, Vector tuples, String status, int updateCount) throws SQLException + { + return new postgresql.jdbc1.ResultSet((postgresql.jdbc1.Connection)conn,fields,tuples,status,updateCount); + } + +} + +// *********************************************************************** + diff --git a/src/interfaces/jdbc/postgresql/jdbc1/DatabaseMetaData.java b/src/interfaces/jdbc/postgresql/jdbc1/DatabaseMetaData.java new file mode 100644 index 0000000000..b7e90e8768 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc1/DatabaseMetaData.java @@ -0,0 +1,2526 @@ +package postgresql.jdbc1; + +// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 2 class in the +// postgresql.jdbc2 package. + +import java.sql.*; +import java.util.*; +import postgresql.Field; + +/** + * This class provides information about the database as a whole. + * + *

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

Some of these methods take arguments that are String patterns. These + * arguments all have names such as fooPattern. Within a pattern String, + * "%" means match any substring of 0 or more characters, and "_" means + * match any one character. Only metadata entries matching the search + * pattern are returned. if a search pattern argument is set to a null + * ref, it means that argument's criteria should be dropped from the + * search. + * + *

A SQLException will be throws if a driver does not support a meta + * data method. In the case of methods that return a ResultSet, either + * a ResultSet (which may be empty) is returned or a SQLException is + * thrown. + * + * @see java.sql.DatabaseMetaData + */ +public class DatabaseMetaData implements java.sql.DatabaseMetaData +{ + Connection connection; // The connection association + + // These define various OID's. Hopefully they will stay constant. + static final int iVarcharOid = 1043; // OID for varchar + static final int iBoolOid = 16; // OID for bool + static final int iInt2Oid = 21; // OID for int2 + static final int iInt4Oid = 23; // OID for int4 + static final int VARHDRSZ = 4; // length for int4 + + // This is a default value for remarks + private static final byte defaultRemarks[]="no remarks".getBytes(); + + public DatabaseMetaData(Connection conn) + { + this.connection = conn; + } + + /** + * Can all the procedures returned by getProcedures be called + * by the current user? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean allProceduresAreCallable() throws SQLException + { + return true; // For now... + } + + /** + * Can all the tables returned by getTable be SELECTed by + * the current user? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean allTablesAreSelectable() throws SQLException + { + return true; // For now... + } + + /** + * What is the URL for this database? + * + * @return the url or null if it cannott be generated + * @exception SQLException if a database access error occurs + */ + public String getURL() throws SQLException + { + return connection.getURL(); + } + + /** + * What is our user name as known to the database? + * + * @return our database user name + * @exception SQLException if a database access error occurs + */ + public String getUserName() throws SQLException + { + return connection.getUserName(); + } + + /** + * Is the database in read-only mode? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly() throws SQLException + { + return connection.isReadOnly(); + } + + /** + * Are NULL values sorted high? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedHigh() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted low? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedLow() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted at the start regardless of sort order? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedAtStart() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted at the end regardless of sort order? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedAtEnd() throws SQLException + { + return true; + } + + /** + * What is the name of this database product - we hope that it is + * PostgreSQL, so we return that explicitly. + * + * @return the database product name + * @exception SQLException if a database access error occurs + */ + public String getDatabaseProductName() throws SQLException + { + return new String("PostgreSQL"); + } + + /** + * What is the version of this database product. + * + *

Note that PostgreSQL 6.3 has a system catalog called pg_version - + * however, select * from pg_version on any database retrieves + * no rows. + * + *

For now, we will return the version 6.3 (in the hope that we change + * this driver as often as we change the database) + * + * @return the database version + * @exception SQLException if a database access error occurs + */ + public String getDatabaseProductVersion() throws SQLException + { + return ("6.4"); + } + + /** + * What is the name of this JDBC driver? If we don't know this + * we are doing something wrong! + * + * @return the JDBC driver name + * @exception SQLException why? + */ + public String getDriverName() throws SQLException + { + return new String("PostgreSQL Native Driver"); + } + + /** + * What is the version string of this JDBC driver? Again, this is + * static. + * + * @return the JDBC driver name. + * @exception SQLException why? + */ + public String getDriverVersion() throws SQLException + { + return new String(Integer.toString(connection.this_driver.getMajorVersion())+"."+Integer.toString(connection.this_driver.getMinorVersion())); + } + + /** + * What is this JDBC driver's major version number? + * + * @return the JDBC driver major version + */ + public int getDriverMajorVersion() + { + return connection.this_driver.getMajorVersion(); + } + + /** + * What is this JDBC driver's minor version number? + * + * @return the JDBC driver minor version + */ + public int getDriverMinorVersion() + { + return connection.this_driver.getMinorVersion(); + } + + /** + * Does the database store tables in a local file? No - it + * stores them in a file on the server. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean usesLocalFiles() throws SQLException + { + return false; + } + + /** + * Does the database use a file for each table? Well, not really, + * since it doesnt use local files. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean usesLocalFilePerTable() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers + * as case sensitive and as a result store them in mixed case? + * A JDBC-Compliant driver will always return false. + * + *

Predicament - what do they mean by "SQL identifiers" - if it + * means the names of the tables and columns, then the answers + * given below are correct - otherwise I don't know. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMixedCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in upper case? + * + * @return true if so + */ + public boolean storesUpperCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in lower case? + * + * @return true if so + */ + public boolean storesLowerCaseIdentifiers() throws SQLException + { + return true; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in mixed case? + * + * @return true if so + */ + public boolean storesMixedCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as + * case sensitive and as a result store them in mixed case? A + * JDBC compliant driver will always return true. + * + *

Predicament - what do they mean by "SQL identifiers" - if it + * means the names of the tables and columns, then the answers + * given below are correct - otherwise I don't know. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException + { + return true; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as + * case insensitive and store them in upper case? + * + * @return true if so + */ + public boolean storesUpperCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as case + * insensitive and store them in lower case? + * + * @return true if so + */ + public boolean storesLowerCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as case + * insensitive and store them in mixed case? + * + * @return true if so + */ + public boolean storesMixedCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * What is the string used to quote SQL identifiers? This returns + * a space if identifier quoting isn't supported. A JDBC Compliant + * driver will always use a double quote character. + * + *

If an SQL identifier is a table name, column name, etc. then + * we do not support it. + * + * @return the quoting string + * @exception SQLException if a database access error occurs + */ + public String getIdentifierQuoteString() throws SQLException + { + return null; + } + + /** + * Get a comma separated list of all a database's SQL keywords that + * are NOT also SQL92 keywords. + * + *

Within PostgreSQL, the keywords are found in + * src/backend/parser/keywords.c + * + *

For SQL Keywords, I took the list provided at + * + * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt + * which is for SQL3, not SQL-92, but it is close enough for + * this purpose. + * + * @return a comma separated list of keywords we use + * @exception SQLException if a database access error occurs + */ + public String getSQLKeywords() throws SQLException + { + return new String("abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version"); + } + + public String getNumericFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getStringFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getSystemFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getTimeDateFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + /** + * This is the string that can be used to escape '_' and '%' in + * a search string pattern style catalog search parameters + * + * @return the string used to escape wildcard characters + * @exception SQLException if a database access error occurs + */ + public String getSearchStringEscape() throws SQLException + { + return new String("\\"); + } + + /** + * Get all the "extra" characters that can bew used in unquoted + * identifier names (those beyond a-zA-Z0-9 and _) + * + *

From the file src/backend/parser/scan.l, an identifier is + * {letter}{letter_or_digit} which makes it just those listed + * above. + * + * @return a string containing the extra characters + * @exception SQLException if a database access error occurs + */ + public String getExtraNameCharacters() throws SQLException + { + return new String(""); + } + + /** + * Is "ALTER TABLE" with an add column supported? + * Yes for PostgreSQL 6.1 + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsAlterTableWithAddColumn() throws SQLException + { + return true; + } + + /** + * Is "ALTER TABLE" with a drop column supported? + * Yes for PostgreSQL 6.1 + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsAlterTableWithDropColumn() throws SQLException + { + return true; + } + + /** + * Is column aliasing supported? + * + *

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

e.g. + * + *

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

+ * should return a column named as C_COUNT instead of count(C) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsColumnAliasing() throws SQLException + { + return true; + } + + /** + * Are concatenations between NULL and non-NULL values NULL? A + * JDBC Compliant driver always returns true + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullPlusNonNullIsNull() throws SQLException + { + return true; + } + + public boolean supportsConvert() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsConvert(int fromType, int toType) throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsTableCorrelationNames() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsDifferentTableCorrelationNames() throws SQLException + { + // XXX-Not Implemented + return false; + } + + /** + * Are expressions in "ORCER BY" lists supported? + * + *
e.g. select * from t order by a + b; + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsExpressionsInOrderBy() throws SQLException + { + return true; + } + + /** + * Can an "ORDER BY" clause use columns not in the SELECT? + * I checked it, and you can't. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOrderByUnrelated() throws SQLException + { + return false; + } + + /** + * Is some form of "GROUP BY" clause supported? + * I checked it, and yes it is. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupBy() throws SQLException + { + return true; + } + + /** + * Can a "GROUP BY" clause use columns not in the SELECT? + * I checked it - it seems to allow it + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupByUnrelated() throws SQLException + { + return true; + } + + /** + * Can a "GROUP BY" clause add columns not in the SELECT provided + * it specifies all the columns in the SELECT? Does anyone actually + * understand what they mean here? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupByBeyondSelect() throws SQLException + { + return true; // For now... + } + + /** + * Is the escape character in "LIKE" clauses supported? A + * JDBC compliant driver always returns true. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsLikeEscapeClause() throws SQLException + { + return true; + } + + /** + * Are multiple ResultSets from a single execute supported? + * Well, I implemented it, but I dont think this is possible from + * the back ends point of view. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMultipleResultSets() throws SQLException + { + return false; + } + + /** + * Can we have multiple transactions open at once (on different + * connections?) + * I guess we can have, since Im relying on it. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMultipleTransactions() throws SQLException + { + return true; + } + + /** + * Can columns be defined as non-nullable. A JDBC Compliant driver + * always returns true. + * + *

This changed from false to true in v6.2 of the driver, as this + * support was added to the backend. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsNonNullableColumns() throws SQLException + { + return true; + } + + /** + * Does this driver support the minimum ODBC SQL grammar. This + * grammar is defined at: + * + *

http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm + * + *

In Appendix C. From this description, we seem to support the + * ODBC minimal (Level 0) grammar. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMinimumSQLGrammar() throws SQLException + { + return true; + } + + /** + * Does this driver support the Core ODBC SQL grammar. We need + * SQL-92 conformance for this. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCoreSQLGrammar() throws SQLException + { + return false; + } + + /** + * Does this driver support the Extended (Level 2) ODBC SQL + * grammar. We don't conform to the Core (Level 1), so we can't + * conform to the Extended SQL Grammar. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsExtendedSQLGrammar() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 entry level SQL grammar? + * All JDBC Compliant drivers must return true. I think we have + * to support outer joins for this to be true. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92EntryLevelSQL() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 intermediate level SQL + * grammar? Anyone who does not support Entry level cannot support + * Intermediate level. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92IntermediateSQL() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 full SQL grammar? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92FullSQL() throws SQLException + { + return false; + } + + /** + * Is the SQL Integrity Enhancement Facility supported? + * I haven't seen this mentioned anywhere, so I guess not + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsIntegrityEnhancementFacility() throws SQLException + { + return false; + } + + /** + * Is some form of outer join supported? From my knowledge, nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOuterJoins() throws SQLException + { + return false; + } + + /** + * Are full nexted outer joins supported? Well, we dont support any + * form of outer join, so this is no as well + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsFullOuterJoins() throws SQLException + { + return false; + } + + /** + * Is there limited support for outer joins? (This will be true if + * supportFullOuterJoins is true) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsLimitedOuterJoins() throws SQLException + { + return false; + } + + /** + * What is the database vendor's preferred term for "schema" - well, + * we do not provide support for schemas, so lets just use that + * term. + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getSchemaTerm() throws SQLException + { + return new String("Schema"); + } + + /** + * What is the database vendor's preferred term for "procedure" - + * I kind of like "Procedure" myself. + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getProcedureTerm() throws SQLException + { + return new String("Procedure"); + } + + /** + * What is the database vendor's preferred term for "catalog"? - + * we dont have a preferred term, so just use Catalog + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getCatalogTerm() throws SQLException + { + return new String("Catalog"); + } + + /** + * Does a catalog appear at the start of a qualified table name? + * (Otherwise it appears at the end). + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isCatalogAtStart() throws SQLException + { + return false; + } + + /** + * What is the Catalog separator. Hmmm....well, I kind of like + * a period (so we get catalog.table definitions). - I don't think + * PostgreSQL supports catalogs anyhow, so it makes no difference. + * + * @return the catalog separator string + * @exception SQLException if a database access error occurs + */ + public String getCatalogSeparator() throws SQLException + { + // PM Sep 29 97 - changed from "." as we don't support catalogs. + return new String(""); + } + + /** + * Can a schema name be used in a data manipulation statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInDataManipulation() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in a procedure call statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInProcedureCalls() throws SQLException + { + return false; + } + + /** + * Can a schema be used in a table definition statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInTableDefinitions() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in an index definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInIndexDefinitions() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in a privilege definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a data manipulation statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInDataManipulation() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a procedure call statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInProcedureCalls() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a table definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInTableDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in an index definition? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInIndexDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a privilege definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException + { + return false; + } + + /** + * We support cursors for gets only it seems. I dont see a method + * to get a positioned delete. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsPositionedDelete() throws SQLException + { + return false; // For now... + } + + /** + * Is positioned UPDATE supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsPositionedUpdate() throws SQLException + { + return false; // For now... + } + + public boolean supportsSelectForUpdate() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsStoredProcedures() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsSubqueriesInComparisons() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsSubqueriesInExists() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsSubqueriesInIns() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsSubqueriesInQuantifieds() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsCorrelatedSubqueries() throws SQLException + { + // XXX-Not Implemented + return false; + } + + /** + * Is SQL UNION supported? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsUnion() throws SQLException + { + return false; + } + + /** + * Is SQL UNION ALL supported? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsUnionAll() throws SQLException + { + return false; + } + + /** + * In PostgreSQL, Cursors are only open within transactions. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenCursorsAcrossCommit() throws SQLException + { + return false; + } + + /** + * Do we support open cursors across multiple transactions? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenCursorsAcrossRollback() throws SQLException + { + return false; + } + + /** + * Can statements remain open across commits? They may, but + * this driver cannot guarentee that. In further reflection. + * we are talking a Statement object jere, so the answer is + * yes, since the Statement is only a vehicle to ExecSQL() + * + * @return true if they always remain open; false otherwise + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenStatementsAcrossCommit() throws SQLException + { + return true; + } + + /** + * Can statements remain open across rollbacks? They may, but + * this driver cannot guarentee that. In further contemplation, + * we are talking a Statement object here, so the answer is yes, + * since the Statement is only a vehicle to ExecSQL() in Connection + * + * @return true if they always remain open; false otherwise + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenStatementsAcrossRollback() throws SQLException + { + return true; + } + + /** + * How many hex characters can you have in an inline binary literal + * + * @return the max literal length + * @exception SQLException if a database access error occurs + */ + public int getMaxBinaryLiteralLength() throws SQLException + { + return 0; // For now... + } + + /** + * What is the maximum length for a character literal + * I suppose it is 8190 (8192 - 2 for the quotes) + * + * @return the max literal length + * @exception SQLException if a database access error occurs + */ + public int getMaxCharLiteralLength() throws SQLException + { + return 8190; + } + + /** + * Whats the limit on column name length. The description of + * pg_class would say '32' (length of pg_class.relname) - we + * should probably do a query for this....but.... + * + * @return the maximum column name length + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum number of columns in a "GROUP BY" clause? + * + * @return the max number of columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInGroupBy() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What's the maximum number of columns allowed in an index? + * 6.0 only allowed one column, but 6.1 introduced multi-column + * indices, so, theoretically, its all of them. + * + * @return max number of columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInIndex() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What's the maximum number of columns in an "ORDER BY clause? + * Theoretically, all of them! + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInOrderBy() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What is the maximum number of columns in a "SELECT" list? + * Theoretically, all of them! + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInSelect() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What is the maximum number of columns in a table? From the + * create_table(l) manual page... + * + *

"The new class is created as a heap with no initial data. A + * class can have no more than 1600 attributes (realistically, + * this is limited by the fact that tuple sizes must be less than + * 8192 bytes)..." + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInTable() throws SQLException + { + return 1600; + } + + /** + * How many active connection can we have at a time to this + * database? Well, since it depends on postmaster, which just + * does a listen() followed by an accept() and fork(), its + * basically very high. Unless the system runs out of processes, + * it can be 65535 (the number of aux. ports on a TCP/IP system). + * I will return 8192 since that is what even the largest system + * can realistically handle, + * + * @return the maximum number of connections + * @exception SQLException if a database access error occurs + */ + public int getMaxConnections() throws SQLException + { + return 8192; + } + + /** + * What is the maximum cursor name length (the same as all + * the other F***** identifiers!) + * + * @return max cursor name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxCursorNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum length of an index (in bytes)? Now, does + * the spec. mean name of an index (in which case its 32, the + * same as a table) or does it mean length of an index element + * (in which case its 8192, the size of a row) or does it mean + * the number of rows it can access (in which case it 2^32 - + * a 4 byte OID number)? I think its the length of an index + * element, personally, so Im setting it to 8192. + * + * @return max index length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxIndexLength() throws SQLException + { + return 8192; + } + + public int getMaxSchemaNameLength() throws SQLException + { + // XXX-Not Implemented + return 0; + } + + /** + * What is the maximum length of a procedure name? + * (length of pg_proc.proname used) - again, I really + * should do a query here to get it. + * + * @return the max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxProcedureNameLength() throws SQLException + { + return 32; + } + + public int getMaxCatalogNameLength() throws SQLException + { + // XXX-Not Implemented + return 0; + } + + /** + * What is the maximum length of a single row? (not including + * blobs). 8192 is defined in PostgreSQL. + * + * @return max row size in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxRowSize() throws SQLException + { + return 8192; + } + + /** + * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY + * blobs? We don't handle blobs yet + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean doesMaxRowSizeIncludeBlobs() throws SQLException + { + return false; + } + + /** + * What is the maximum length of a SQL statement? + * + * @return max length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxStatementLength() throws SQLException + { + return 8192; + } + + /** + * How many active statements can we have open at one time to + * this database? Basically, since each Statement downloads + * the results as the query is executed, we can have many. However, + * we can only really have one statement per connection going + * at once (since they are executed serially) - so we return + * one. + * + * @return the maximum + * @exception SQLException if a database access error occurs + */ + public int getMaxStatements() throws SQLException + { + return 1; + } + + /** + * What is the maximum length of a table name? This was found + * from pg_class.relname length + * + * @return max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxTableNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum number of tables that can be specified + * in a SELECT? Theoretically, this is the same number as the + * number of tables allowable. In practice tho, it is much smaller + * since the number of tables is limited by the statement, we + * return 1024 here - this is just a number I came up with (being + * the number of tables roughly of three characters each that you + * can fit inside a 8192 character buffer with comma separators). + * + * @return the maximum + * @exception SQLException if a database access error occurs + */ + public int getMaxTablesInSelect() throws SQLException + { + return 1024; + } + + /** + * What is the maximum length of a user name? Well, we generally + * use UNIX like user names in PostgreSQL, so I think this would + * be 8. However, showing the schema for pg_user shows a length + * for username of 32. + * + * @return the max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxUserNameLength() throws SQLException + { + return 32; + } + + + /** + * What is the database's default transaction isolation level? We + * do not support this, so all transactions are SERIALIZABLE. + * + * @return the default isolation level + * @exception SQLException if a database access error occurs + * @see Connection + */ + public int getDefaultTransactionIsolation() throws SQLException + { + return Connection.TRANSACTION_SERIALIZABLE; + } + + /** + * Are transactions supported? If not, commit and rollback are noops + * and the isolation level is TRANSACTION_NONE. We do support + * transactions. + * + * @return true if transactions are supported + * @exception SQLException if a database access error occurs + */ + public boolean supportsTransactions() throws SQLException + { + return true; + } + + /** + * Does the database support the given transaction isolation level? + * We only support TRANSACTION_SERIALIZABLE + * + * @param level the values are defined in java.sql.Connection + * @return true if so + * @exception SQLException if a database access error occurs + * @see Connection + */ + public boolean supportsTransactionIsolationLevel(int level) throws SQLException + { + if (level == Connection.TRANSACTION_SERIALIZABLE) + return true; + else + return false; + } + + /** + * Are both data definition and data manipulation transactions + * supported? I checked it, and could not do a CREATE TABLE + * within a transaction, so I am assuming that we don't + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException + { + return false; + } + + /** + * Are only data manipulation statements withing a transaction + * supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsDataManipulationTransactionsOnly() throws SQLException + { + return true; + } + + /** + * Does a data definition statement within a transaction force + * the transaction to commit? I think this means something like: + * + *

+   * CREATE TABLE T (A INT);
+   * INSERT INTO T (A) VALUES (2);
+   * BEGIN;
+   * UPDATE T SET A = A + 1;
+   * CREATE TABLE X (A INT);
+   * SELECT A FROM T INTO X;
+   * COMMIT;
+   * 

+ * + * does the CREATE TABLE call cause a commit? The answer is no. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean dataDefinitionCausesTransactionCommit() throws SQLException + { + return false; + } + + /** + * Is a data definition statement within a transaction ignored? + * It seems to be (from experiment in previous method) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean dataDefinitionIgnoredInTransactions() throws SQLException + { + return true; + } + + /** + * Get a description of stored procedures available in a catalog + * + *

Only procedure descriptions matching the schema and procedure + * name criteria are returned. They are ordered by PROCEDURE_SCHEM + * and PROCEDURE_NAME + * + *

Each procedure description has the following columns: + *

    + *
  1. PROCEDURE_CAT String => procedure catalog (may be null) + *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) + *
  3. PROCEDURE_NAME String => procedure name + *
  4. Field 4 reserved (make it null) + *
  5. Field 5 reserved (make it null) + *
  6. Field 6 reserved (make it null) + *
  7. REMARKS String => explanatory comment on the procedure + *
  8. PROCEDURE_TYPE short => kind of procedure + * + *
+ * + * @param catalog - a catalog name; "" retrieves those without a + * catalog; null means drop catalog name from criteria + * @param schemaParrern - a schema name pattern; "" retrieves those + * without a schema - we ignore this parameter + * @param procedureNamePattern - a procedure name pattern + * @return ResultSet - each row is a procedure description + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException + { + // the field descriptors for the new ResultSet + Field f[] = new Field[8]; + java.sql.ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + byte remarks[] = defaultRemarks; + + f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, 32); + f[3] = f[4] = f[5] = null; // reserved, must be null for now + f[6] = new Field(connection, "REMARKS", iVarcharOid, 8192); + f[7] = new Field(connection, "PROCEDURE_TYPE", iInt2Oid, 2); + + // If the pattern is null, then set it to the default + if(procedureNamePattern==null) + procedureNamePattern="%"; + + r = connection.ExecSQL("select proname, proretset from pg_proc where proname like '"+procedureNamePattern.toLowerCase()+"' order by proname"); + + while (r.next()) + { + byte[][] tuple = new byte[8][0]; + + tuple[0] = null; // Catalog name + tuple[1] = null; // Schema name + tuple[2] = r.getBytes(1); // Procedure name + tuple[3] = tuple[4] = tuple[5] = null; // Reserved + tuple[6] = remarks; // Remarks + + if (r.getBoolean(2)) + tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureReturnsResult).getBytes(); + else + tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureNoResult).getBytes(); + + v.addElement(tuple); + } + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of a catalog's stored procedure parameters + * and result columns. + * + *

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

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

    + *
  1. PROCEDURE_CAT String => procedure catalog (may be null) + *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) + *
  3. PROCEDURE_NAME String => procedure name + *
  4. COLUMN_NAME String => column/parameter name + *
  5. COLUMN_TYPE Short => kind of column/parameter: + * + *
  6. DATA_TYPE short => SQL type from java.sql.Types + *
  7. TYPE_NAME String => SQL type name + *
  8. PRECISION int => precision + *
  9. LENGTH int => length in bytes of data + *
  10. SCALE short => scale + *
  11. RADIX short => radix + *
  12. NULLABLE short => can it contain NULL? + *
+ * @param catalog This is ignored in postgresql, advise this is set to null + * @param schemaPattern This is ignored in postgresql, advise this is set to null + * @param procedureNamePattern a procedure name pattern + * @param columnNamePattern a column name pattern + * @return each row is a stored procedure parameter or column description + * @exception SQLException if a database-access error occurs + * @see #getSearchStringEscape + */ + // Implementation note: This is required for Borland's JBuilder to work + public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException + { + if(procedureNamePattern==null) + procedureNamePattern="%"; + + if(columnNamePattern==null) + columnNamePattern="%"; + + // for now, this returns an empty result set. + Field f[] = new Field[13]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, new String("PROCEDURE_CAT"), iVarcharOid, 32); + f[1] = new Field(connection, new String("PROCEDURE_SCHEM"), iVarcharOid, 32); + f[2] = new Field(connection, new String("PROCEDURE_NAME"), iVarcharOid, 32); + f[3] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32); + f[4] = new Field(connection, new String("COLUMN_TYPE"), iInt2Oid, 2); + f[5] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2); + f[6] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32); + f[7] = new Field(connection, new String("PRECISION"), iInt4Oid, 4); + f[8] = new Field(connection, new String("LENGTH"), iInt4Oid, 4); + f[9] = new Field(connection, new String("SCALE"), iInt2Oid, 2); + f[10] = new Field(connection, new String("RADIX"), iInt2Oid, 2); + f[11] = new Field(connection, new String("NULLABLE"), iInt2Oid, 2); + f[12] = new Field(connection, new String("REMARKS"), iVarcharOid, 32); + + // add query loop here + + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of tables available in a catalog. + * + *

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

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

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

The valid values for the types parameter are: + * "TABLE", "INDEX", "LARGE OBJECT", "SEQUENCE", "SYSTEM TABLE" and + * "SYSTEM INDEX" + * + * @param catalog a catalog name; For postgresql, this is ignored, and + * should be set to null + * @param schemaPattern a schema name pattern; For postgresql, this is ignored, and + * should be set to null + * @param tableNamePattern a table name pattern. For all tables this should be "%" + * @param types a list of table types to include; null returns + * all types + * @return each row is a table description + * @exception SQLException if a database-access error occurs. + */ + public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException + { + // Handle default value for types + if(types==null) + types = defaultTableTypes; + + if(tableNamePattern==null) + tableNamePattern="%"; + + // the field descriptors for the new ResultSet + Field f[] = new Field[5]; + java.sql.ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, new String("TABLE_CAT"), iVarcharOid, 32); + f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32); + f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32); + f[3] = new Field(connection, new String("TABLE_TYPE"), iVarcharOid, 32); + f[4] = new Field(connection, new String("REMARKS"), iVarcharOid, 32); + + // Now form the query + StringBuffer sql = new StringBuffer("select relname,oid from pg_class where ("); + boolean notFirst=false; + for(int i=0;i + // Now take the pattern into account + sql.append(") and relname like '"); + sql.append(tableNamePattern.toLowerCase()); + sql.append("'"); + + // Now run the query + r = connection.ExecSQL(sql.toString()); + + byte remarks[]; + + while (r.next()) + { + byte[][] tuple = new byte[5][0]; + + // Fetch the description for the table (if any) + java.sql.ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(2)); + if(((postgresql.ResultSet)dr).getTupleCount()==1) { + dr.next(); + remarks = dr.getBytes(1); + } else + remarks = defaultRemarks; + dr.close(); + + tuple[0] = null; // Catalog name + tuple[1] = null; // Schema name + tuple[2] = r.getBytes(1); // Table name + tuple[3] = null; // Table type + tuple[4] = remarks; // Remarks + v.addElement(tuple); + } + r.close(); + return new ResultSet(connection, f, v, "OK", 1); + } + + // This array contains the valid values for the types argument + // in getTables(). + // + // Each supported type consists of it's name, and the sql where + // clause to retrieve that value. + // + // IMPORTANT: the query must be enclosed in ( ) + private static final String getTableTypes[][] = { + {"TABLE", "(relkind='r' and relname !~ '^pg_' and relname !~ '^xinv')"}, + {"INDEX", "(relkind='i' and relname !~ '^pg_' and relname !~ '^xinx')"}, + {"LARGE OBJECT", "(relkind='r' and relname ~ '^xinv')"}, + {"SEQUENCE", "(relkind='S' and relname !~ '^pg_')"}, + {"SYSTEM TABLE", "(relkind='r' and relname ~ '^pg_')"}, + {"SYSTEM INDEX", "(relkind='i' and relname ~ '^pg_')"} + }; + + // These are the default tables, used when NULL is passed to getTables + // The choice of these provide the same behaviour as psql's \d + private static final String defaultTableTypes[] = { + "TABLE","INDEX","SEQUENCE" + }; + + /** + * Get the schema names available in this database. The results + * are ordered by schema name. + * + *

The schema column is: + *

    + *
  1. TABLE_SCHEM String => schema name + *
+ * + * @return ResultSet each row has a single String column that is a + * schema name + */ + public java.sql.ResultSet getSchemas() throws SQLException + { + // We don't use schemas, so we simply return a single schema name "". + // + Field f[] = new Field[1]; + Vector v = new Vector(); + byte[][] tuple = new byte[1][0]; + f[0] = new Field(connection,new String("TABLE_SCHEM"),iVarcharOid,32); + tuple[0] = "".getBytes(); + v.addElement(tuple); + return new ResultSet(connection,f,v,"OK",1); + } + + /** + * Get the catalog names available in this database. The results + * are ordered by catalog name. + * + *

The catalog column is: + *

    + *
  1. TABLE_CAT String => catalog name + *
+ * + * @return ResultSet each row has a single String column that is a + * catalog name + */ + public java.sql.ResultSet getCatalogs() throws SQLException + { + // We don't use catalogs, so we simply return a single catalog name "". + Field f[] = new Field[1]; + Vector v = new Vector(); + byte[][] tuple = new byte[1][0]; + f[0] = new Field(connection,new String("TABLE_CAT"),iVarcharOid,32); + tuple[0] = "".getBytes(); + v.addElement(tuple); + return new ResultSet(connection,f,v,"OK",1); + } + + /** + * Get the table types available in this database. The results + * are ordered by table type. + * + *

The table type is: + *

    + *
  1. TABLE_TYPE String => table type. Typical types are "TABLE", + * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", + * "LOCAL TEMPORARY", "ALIAS", "SYNONYM". + *
+ * + * @return ResultSet each row has a single String column that is a + * table type + */ + public java.sql.ResultSet getTableTypes() throws SQLException + { + Field f[] = new Field[1]; + Vector v = new Vector(); + byte[][] tuple = new byte[1][0]; + f[0] = new Field(connection,new String("TABLE_TYPE"),iVarcharOid,32); + for(int i=0;iOnly column descriptions matching the catalog, schema, table + * and column name criteria are returned. They are ordered by + * TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION. + * + *

Each column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. DATA_TYPE short => SQL type from java.sql.Types + *
  6. TYPE_NAME String => Data source dependent type name + *
  7. COLUMN_SIZE int => column size. For char or date + * types this is the maximum number of characters, for numeric or + * decimal types this is precision. + *
  8. BUFFER_LENGTH is not used. + *
  9. DECIMAL_DIGITS int => the number of fractional digits + *
  10. NUM_PREC_RADIX int => Radix (typically either 10 or 2) + *
  11. NULLABLE int => is NULL allowed? + *
      + *
    • columnNoNulls - might not allow NULL values + *
    • columnNullable - definitely allows NULL values + *
    • columnNullableUnknown - nullability unknown + *
    + *
  12. REMARKS String => comment describing column (may be null) + *
  13. COLUMN_DEF String => default value (may be null) + *
  14. SQL_DATA_TYPE int => unused + *
  15. SQL_DATETIME_SUB int => unused + *
  16. CHAR_OCTET_LENGTH int => for char types the + * maximum number of bytes in the column + *
  17. ORDINAL_POSITION int => index of column in table + * (starting at 1) + *
  18. IS_NULLABLE String => "NO" means column definitely + * does not allow NULL values; "YES" means the column might + * allow NULL values. An empty string means nobody knows. + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schemaPattern a schema name pattern; "" retrieves those + * without a schema + * @param tableNamePattern a table name pattern + * @param columnNamePattern a column name pattern + * @return ResultSet each row is a column description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException + { + // the field descriptors for the new ResultSet + Field f[] = new Field[18]; + java.sql.ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, new String("TABLE_CAT"), iVarcharOid, 32); + f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32); + f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32); + f[3] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32); + f[4] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2); + f[5] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32); + f[6] = new Field(connection, new String("COLUMN_SIZE"), iInt4Oid, 4); + f[7] = new Field(connection, new String("BUFFER_LENGTH"), iVarcharOid, 32); + f[8] = new Field(connection, new String("DECIMAL_DIGITS"), iInt4Oid, 4); + f[9] = new Field(connection, new String("NUM_PREC_RADIX"), iInt4Oid, 4); + f[10] = new Field(connection, new String("NULLABLE"), iInt4Oid, 4); + f[11] = new Field(connection, new String("REMARKS"), iVarcharOid, 32); + f[12] = new Field(connection, new String("COLUMN_DEF"), iVarcharOid, 32); + f[13] = new Field(connection, new String("SQL_DATA_TYPE"), iInt4Oid, 4); + f[14] = new Field(connection, new String("SQL_DATETIME_SUB"), iInt4Oid, 4); + f[15] = new Field(connection, new String("CHAR_OCTET_LENGTH"), iVarcharOid, 32); + f[16] = new Field(connection, new String("ORDINAL_POSITION"), iInt4Oid,4); + f[17] = new Field(connection, new String("IS_NULLABLE"), iVarcharOid, 32); + + // Added by Stefan Andreasen + // If the pattern are null then set them to % + if (tableNamePattern == null) tableNamePattern="%"; + if (columnNamePattern == null) columnNamePattern="%"; + + // Now form the query + // Modified by Stefan Andreasen + r = connection.ExecSQL("select a.oid,c.relname,a.attname,a.atttypid,a.attnum,a.attnotnull,a.attlen,a.atttypmod from pg_class c, pg_attribute a where a.attrelid=c.oid and c.relname like '"+tableNamePattern.toLowerCase()+"' and a.attname like '"+columnNamePattern.toLowerCase()+"' and a.attnum>0 order by c.relname,a.attnum"); + + byte remarks[]; + + while(r.next()) { + byte[][] tuple = new byte[18][0]; + + // Fetch the description for the table (if any) + java.sql.ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(1)); + if(((postgresql.ResultSet)dr).getTupleCount()==1) { + dr.next(); + tuple[11] = dr.getBytes(1); + } else + tuple[11] = defaultRemarks; + + dr.close(); + + tuple[0] = "".getBytes(); // Catalog name + tuple[1] = "".getBytes(); // Schema name + tuple[2] = r.getBytes(2); // Table name + tuple[3] = r.getBytes(3); // Column name + + dr = connection.ExecSQL("select typname from pg_type where oid = "+r.getString(4)); + dr.next(); + String typname=dr.getString(1); + dr.close(); + tuple[4] = Integer.toString(Field.getSQLType(typname)).getBytes(); // Data type + tuple[5] = typname.getBytes(); // Type name + + // Column size + // Looking at the psql source, + // I think the length of a varchar as specified when the table was created + // should be extracted from atttypmod which contains this length + sizeof(int32) + if (typname.equals("bpchar") || typname.equals("varchar")) { + int atttypmod = r.getInt(8); + tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes(); + } else + tuple[6] = r.getBytes(7); + + tuple[7] = null; // Buffer length + + tuple[8] = "0".getBytes(); // Decimal Digits - how to get this? + tuple[9] = "10".getBytes(); // Num Prec Radix - assume decimal + + // tuple[10] is below + // tuple[11] is above + + tuple[12] = null; // column default + + tuple[13] = null; // sql data type (unused) + tuple[14] = null; // sql datetime sub (unused) + + tuple[15] = tuple[6]; // char octet length + + tuple[16] = r.getBytes(5); // ordinal position + + String nullFlag = r.getString(6); + tuple[10] = Integer.toString(nullFlag.equals("f")?java.sql.DatabaseMetaData.columnNullable:java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable + tuple[17] = (nullFlag.equals("f")?"YES":"NO").getBytes(); // is nullable + + v.addElement(tuple); + } + r.close(); + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of the access rights for a table's columns. + * + *

Only privileges matching the column name criteria are + * returned. They are ordered by COLUMN_NAME and PRIVILEGE. + * + *

Each privilige description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. GRANTOR => grantor of access (may be null) + *
  6. GRANTEE String => grantee of access + *
  7. PRIVILEGE String => name of access (SELECT, + * INSERT, UPDATE, REFRENCES, ...) + *
  8. IS_GRANTABLE String => "YES" if grantee is permitted + * to grant to others; "NO" if not; null if unknown + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @param columnNamePattern a column name pattern + * @return ResultSet each row is a column privilege description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException + { + Field f[] = new Field[8]; + Vector v = new Vector(); + + if(table==null) + table="%"; + + if(columnNamePattern==null) + columnNamePattern="%"; + else + columnNamePattern=columnNamePattern.toLowerCase(); + + f[0] = new Field(connection,new String("TABLE_CAT"),iVarcharOid,32); + f[1] = new Field(connection,new String("TABLE_SCHEM"),iVarcharOid,32); + f[2] = new Field(connection,new String("TABLE_NAME"),iVarcharOid,32); + f[3] = new Field(connection,new String("COLUMN_NAME"),iVarcharOid,32); + f[4] = new Field(connection,new String("GRANTOR"),iVarcharOid,32); + f[5] = new Field(connection,new String("GRANTEE"),iVarcharOid,32); + f[6] = new Field(connection,new String("PRIVILEGE"),iVarcharOid,32); + f[7] = new Field(connection,new String("IS_GRANTABLE"),iVarcharOid,32); + + // This is taken direct from the psql source + java.sql.ResultSet r = connection.ExecSQL("SELECT relname, relacl FROM pg_class, pg_user WHERE ( relkind = 'r' OR relkind = 'i') and relname !~ '^pg_' and relname !~ '^xin[vx][0-9]+' and usesysid = relowner and relname like '"+table.toLowerCase()+"' ORDER BY relname"); + while(r.next()) { + byte[][] tuple = new byte[8][0]; + tuple[0] = tuple[1]= "".getBytes(); + DriverManager.println("relname=\""+r.getString(1)+"\" relacl=\""+r.getString(2)+"\""); + + // For now, don't add to the result as relacl needs to be processed. + //v.addElement(tuple); + } + + return new ResultSet(connection,f,v,"OK",1); + } + + /** + * Get a description of the access rights for each table available + * in a catalog. + * + *

Only privileges matching the schema and table name + * criteria are returned. They are ordered by TABLE_SCHEM, + * TABLE_NAME, and PRIVILEGE. + * + *

Each privilige description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. GRANTOR => grantor of access (may be null) + *
  6. GRANTEE String => grantee of access + *
  7. PRIVILEGE String => name of access (SELECT, + * INSERT, UPDATE, REFRENCES, ...) + *
  8. IS_GRANTABLE String => "YES" if grantee is permitted + * to grant to others; "NO" if not; null if unknown + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schemaPattern a schema name pattern; "" retrieves those + * without a schema + * @param tableNamePattern a table name pattern + * @return ResultSet each row is a table privilege description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of a table's optimal set of columns that + * uniquely identifies a row. They are ordered by SCOPE. + * + *

Each column description has the following columns: + *

    + *
  1. SCOPE short => actual scope of result + *
      + *
    • bestRowTemporary - very temporary, while using row + *
    • bestRowTransaction - valid for remainder of current transaction + *
    • bestRowSession - valid for remainder of current session + *
    + *
  2. COLUMN_NAME String => column name + *
  3. DATA_TYPE short => SQL data type from java.sql.Types + *
  4. TYPE_NAME String => Data source dependent type name + *
  5. COLUMN_SIZE int => precision + *
  6. BUFFER_LENGTH int => not used + *
  7. DECIMAL_DIGITS short => scale + *
  8. PSEUDO_COLUMN short => is this a pseudo column + * like an Oracle ROWID + *
      + *
    • bestRowUnknown - may or may not be pseudo column + *
    • bestRowNotPseudo - is NOT a pseudo column + *
    • bestRowPseudo - is a pseudo column + *
    + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @param scope the scope of interest; use same values as SCOPE + * @param nullable include columns that are nullable? + * @return ResultSet each row is a column description + */ + // Implementation note: This is required for Borland's JBuilder to work + public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException + { + // for now, this returns an empty result set. + Field f[] = new Field[8]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, new String("SCOPE"), iInt2Oid, 2); + f[1] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32); + f[2] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2); + f[3] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32); + f[4] = new Field(connection, new String("COLUMN_SIZE"), iInt4Oid, 4); + f[5] = new Field(connection, new String("BUFFER_LENGTH"), iInt4Oid, 4); + f[6] = new Field(connection, new String("DECIMAL_DIGITS"), iInt2Oid, 2); + f[7] = new Field(connection, new String("PSEUDO_COLUMN"), iInt2Oid, 2); + + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of a table's columns that are automatically + * updated when any value in a row is updated. They are + * unordered. + * + *

Each column description has the following columns: + *

    + *
  1. SCOPE short => is not used + *
  2. COLUMN_NAME String => column name + *
  3. DATA_TYPE short => SQL data type from java.sql.Types + *
  4. TYPE_NAME String => Data source dependent type name + *
  5. COLUMN_SIZE int => precision + *
  6. BUFFER_LENGTH int => length of column value in bytes + *
  7. DECIMAL_DIGITS short => scale + *
  8. PSEUDO_COLUMN short => is this a pseudo column + * like an Oracle ROWID + *
      + *
    • versionColumnUnknown - may or may not be pseudo column + *
    • versionColumnNotPseudo - is NOT a pseudo column + *
    • versionColumnPseudo - is a pseudo column + *
    + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @return ResultSet each row is a column description + */ + public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of a table's primary key columns. They + * are ordered by COLUMN_NAME. + * + *

Each column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. KEY_SEQ short => sequence number within primary key + *
  6. PK_NAME String => primary key name (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a primary key column description + */ + public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException + { + return connection.createStatement().executeQuery("SELECT " + + "'' as TABLE_CAT," + + "'' AS TABLE_SCHEM," + + "bc.relname AS TABLE_NAME," + + "ic.relname AS COLUMN_NAME," + + "'1' as KEY_SEQ,"+ // -- fake it as a String for now + "t.typname as PK_NAME " + + " FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a, pg_type t " + + " WHERE bc.relkind = 'r' " + // -- not indices + " and bc.relname ~ '"+table+"'" + + " and i.indrelid = bc.oid" + + " and i.indexrelid = ic.oid" + + " and i.indkey[0] = a.attnum" + + " and i.indproc = '0'::oid" + + " and a.attrelid = bc.oid" + + " ORDER BY TABLE_NAME, COLUMN_NAME;" + ); + } + + /** + * Get a description of the primary key columns that are + * referenced by a table's foreign key columns (the primary keys + * imported by a table). They are ordered by PKTABLE_CAT, + * PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. + * + *

Each primary key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog + * being imported (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema + * being imported (may be null) + *
  3. PKTABLE_NAME String => primary key table name + * being imported + *
  4. PKCOLUMN_NAME String => primary key column name + * being imported + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + *
  8. FKCOLUMN_NAME String => foreign key column name + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key name (may be null) + *
  13. PK_NAME String => primary key name (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a primary key column description + * @see #getExportedKeys + */ + public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of a foreign key columns that reference a + * table's primary key columns (the foreign keys exported by a + * table). They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, + * FKTABLE_NAME, and KEY_SEQ. + * + *

Each foreign key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) + *
  3. PKTABLE_NAME String => primary key table name + *
  4. PKCOLUMN_NAME String => primary key column name + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + * being exported (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + * being exported (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + * being exported + *
  8. FKCOLUMN_NAME String => foreign key column name + * being exported + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key identifier (may be null) + *
  13. PK_NAME String => primary key identifier (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a foreign key column description + * @see #getImportedKeys + */ + public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of the foreign key columns in the foreign key + * table that reference the primary key columns of the primary key + * table (describe how one table imports another's key.) This + * should normally return a single foreign key/primary key pair + * (most tables only import a foreign key from a table once.) They + * are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and + * KEY_SEQ. + * + *

Each foreign key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) + *
  3. PKTABLE_NAME String => primary key table name + *
  4. PKCOLUMN_NAME String => primary key column name + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + * being exported (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + * being exported (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + * being exported + *
  8. FKCOLUMN_NAME String => foreign key column name + * being exported + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key identifier (may be null) + *
  13. PK_NAME String => primary key identifier (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a foreign key column description + * @see #getImportedKeys + */ + public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of all the standard SQL types supported by + * this database. They are ordered by DATA_TYPE and then by how + * closely the data type maps to the corresponding JDBC SQL type. + * + *

Each type description has the following columns: + *

    + *
  1. TYPE_NAME String => Type name + *
  2. DATA_TYPE short => SQL data type from java.sql.Types + *
  3. PRECISION int => maximum precision + *
  4. LITERAL_PREFIX String => prefix used to quote a literal + * (may be null) + *
  5. LITERAL_SUFFIX String => suffix used to quote a literal + (may be null) + *
  6. CREATE_PARAMS String => parameters used in creating + * the type (may be null) + *
  7. NULLABLE short => can you use NULL for this type? + *
      + *
    • typeNoNulls - does not allow NULL values + *
    • typeNullable - allows NULL values + *
    • typeNullableUnknown - nullability unknown + *
    + *
  8. CASE_SENSITIVE boolean=> is it case sensitive? + *
  9. SEARCHABLE short => can you use "WHERE" based on this type: + *
      + *
    • typePredNone - No support + *
    • typePredChar - Only supported with WHERE .. LIKE + *
    • typePredBasic - Supported except for WHERE .. LIKE + *
    • typeSearchable - Supported for all WHERE .. + *
    + *
  10. UNSIGNED_ATTRIBUTE boolean => is it unsigned? + *
  11. FIXED_PREC_SCALE boolean => can it be a money value? + *
  12. AUTO_INCREMENT boolean => can it be used for an + * auto-increment value? + *
  13. LOCAL_TYPE_NAME String => localized version of type name + * (may be null) + *
  14. MINIMUM_SCALE short => minimum scale supported + *
  15. MAXIMUM_SCALE short => maximum scale supported + *
  16. SQL_DATA_TYPE int => unused + *
  17. SQL_DATETIME_SUB int => unused + *
  18. NUM_PREC_RADIX int => usually 2 or 10 + *
+ * + * @return ResultSet each row is a SQL type description + */ + public java.sql.ResultSet getTypeInfo() throws SQLException + { + java.sql.ResultSet rs = connection.ExecSQL("select typname from pg_type"); + if(rs!=null) { + Field f[] = new Field[18]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32); + f[1] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2); + f[2] = new Field(connection, new String("PRECISION"), iInt4Oid, 4); + f[3] = new Field(connection, new String("LITERAL_PREFIX"), iVarcharOid, 32); + f[4] = new Field(connection, new String("LITERAL_SUFFIX"), iVarcharOid, 32); + f[5] = new Field(connection, new String("CREATE_PARAMS"), iVarcharOid, 32); + f[6] = new Field(connection, new String("NULLABLE"), iInt2Oid, 2); + f[7] = new Field(connection, new String("CASE_SENSITIVE"), iBoolOid, 1); + f[8] = new Field(connection, new String("SEARCHABLE"), iInt2Oid, 2); + f[9] = new Field(connection, new String("UNSIGNED_ATTRIBUTE"), iBoolOid, 1); + f[10] = new Field(connection, new String("FIXED_PREC_SCALE"), iBoolOid, 1); + f[11] = new Field(connection, new String("AUTO_INCREMENT"), iBoolOid, 1); + f[12] = new Field(connection, new String("LOCAL_TYPE_NAME"), iVarcharOid, 32); + f[13] = new Field(connection, new String("MINIMUM_SCALE"), iInt2Oid, 2); + f[14] = new Field(connection, new String("MAXIMUM_SCALE"), iInt2Oid, 2); + f[15] = new Field(connection, new String("SQL_DATA_TYPE"), iInt4Oid, 4); + f[16] = new Field(connection, new String("SQL_DATETIME_SUB"), iInt4Oid, 4); + f[17] = new Field(connection, new String("NUM_PREC_RADIX"), iInt4Oid, 4); + + // cache some results, this will keep memory useage down, and speed + // things up a little. + byte b9[] = "9".getBytes(); + byte b10[] = "10".getBytes(); + byte bf[] = "f".getBytes(); + byte bnn[] = Integer.toString(typeNoNulls).getBytes(); + byte bts[] = Integer.toString(typeSearchable).getBytes(); + + while(rs.next()) { + byte[][] tuple = new byte[18][]; + String typname=rs.getString(1); + tuple[0] = typname.getBytes(); + tuple[1] = Integer.toString(Field.getSQLType(typname)).getBytes(); + tuple[2] = b9; // for now + tuple[6] = bnn; // for now + tuple[7] = bf; // false for now - not case sensitive + tuple[8] = bts; + tuple[9] = bf; // false for now - it's signed + tuple[10] = bf; // false for now - must handle money + tuple[11] = bf; // false for now - handle autoincrement + // 12 - LOCAL_TYPE_NAME is null + // 13 & 14 ? + // 15 & 16 are unused so we return null + tuple[17] = b10; // everything is base 10 + v.addElement(tuple); + } + rs.close(); + return new ResultSet(connection, f, v, "OK", 1); + } + + return null; + } + + /** + * Get a description of a table's indices and statistics. They are + * ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. + * + *

Each index column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. NON_UNIQUE boolean => Can index values be non-unique? + * false when TYPE is tableIndexStatistic + *
  5. INDEX_QUALIFIER String => index catalog (may be null); + * null when TYPE is tableIndexStatistic + *
  6. INDEX_NAME String => index name; null when TYPE is + * tableIndexStatistic + *
  7. TYPE short => index type: + *
      + *
    • tableIndexStatistic - this identifies table statistics that are + * returned in conjuction with a table's index descriptions + *
    • tableIndexClustered - this is a clustered index + *
    • tableIndexHashed - this is a hashed index + *
    • tableIndexOther - this is some other style of index + *
    + *
  8. ORDINAL_POSITION short => column sequence number + * within index; zero when TYPE is tableIndexStatistic + *
  9. COLUMN_NAME String => column name; null when TYPE is + * tableIndexStatistic + *
  10. ASC_OR_DESC String => column sort sequence, "A" => ascending + * "D" => descending, may be null if sort sequence is not supported; + * null when TYPE is tableIndexStatistic + *
  11. CARDINALITY int => When TYPE is tableIndexStatisic then + * this is the number of rows in the table; otherwise it is the + * number of unique values in the index. + *
  12. PAGES int => When TYPE is tableIndexStatisic then + * this is the number of pages used for the table, otherwise it + * is the number of pages used for the current index. + *
  13. FILTER_CONDITION String => Filter condition, if any. + * (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those without a schema + * @param table a table name + * @param unique when true, return only indices for unique values; + * when false, return indices regardless of whether unique or not + * @param approximate when true, result is allowed to reflect approximate + * or out of data values; when false, results are requested to be + * accurate + * @return ResultSet each row is an index column description + */ + // Implementation note: This is required for Borland's JBuilder to work + public java.sql.ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException + { + // for now, this returns an empty result set. + Field f[] = new Field[13]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, new String("TABLE_CAT"), iVarcharOid, 32); + f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32); + f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32); + f[3] = new Field(connection, new String("NON_UNIQUE"), iBoolOid, 1); + f[4] = new Field(connection, new String("INDEX_QUALIFIER"), iVarcharOid, 32); + f[5] = new Field(connection, new String("INDEX_NAME"), iVarcharOid, 32); + f[6] = new Field(connection, new String("TYPE"), iInt2Oid, 2); + f[7] = new Field(connection, new String("ORDINAL_POSITION"), iInt2Oid, 2); + f[8] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32); + f[9] = new Field(connection, new String("ASC_OR_DESC"), iVarcharOid, 32); + f[10] = new Field(connection, new String("CARDINALITY"), iInt4Oid, 4); + f[11] = new Field(connection, new String("PAGES"), iInt4Oid, 4); + f[12] = new Field(connection, new String("FILTER_CONDITION"), iVarcharOid, 32); + + return new ResultSet(connection, f, v, "OK", 1); + } +} + diff --git a/src/interfaces/jdbc/postgresql/jdbc1/PreparedStatement.java b/src/interfaces/jdbc/postgresql/jdbc1/PreparedStatement.java new file mode 100644 index 0000000000..78eaf3db35 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc1/PreparedStatement.java @@ -0,0 +1,600 @@ +package postgresql.jdbc1; + +// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 2 class in the +// postgresql.jdbc2 package. + +import java.io.*; +import java.math.*; +import java.sql.*; +import java.text.*; +import java.util.*; +import postgresql.largeobject.*; +import postgresql.util.*; + +/** + * A SQL Statement is pre-compiled and stored in a PreparedStatement object. + * This object can then be used to efficiently execute this statement multiple + * times. + * + *

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

If arbitrary parameter type conversions are required, then the setObject + * method should be used with a target SQL type. + * + * @see ResultSet + * @see java.sql.PreparedStatement + */ +public class PreparedStatement extends Statement implements java.sql.PreparedStatement +{ + String sql; + String[] templateStrings; + String[] inStrings; + Connection connection; + + /** + * Constructor for the PreparedStatement class. + * Split the SQL statement into segments - separated by the arguments. + * When we rebuild the thing with the arguments, we can substitute the + * args and join the whole thing together. + * + * @param conn the instanatiating connection + * @param sql the SQL statement with ? for IN markers + * @exception SQLException if something bad occurs + */ + public PreparedStatement(Connection connection, String sql) throws SQLException + { + super(connection); + + Vector v = new Vector(); + boolean inQuotes = false; + int lastParmEnd = 0, i; + + this.sql = sql; + this.connection = connection; + for (i = 0; i < sql.length(); ++i) + { + int c = sql.charAt(i); + + if (c == '\'') + inQuotes = !inQuotes; + if (c == '?' && !inQuotes) + { + v.addElement(sql.substring (lastParmEnd, i)); + lastParmEnd = i + 1; + } + } + v.addElement(sql.substring (lastParmEnd, sql.length())); + + templateStrings = new String[v.size()]; + inStrings = new String[v.size() - 1]; + clearParameters(); + + for (i = 0 ; i < templateStrings.length; ++i) + templateStrings[i] = (String)v.elementAt(i); + } + + /** + * A Prepared SQL query is executed and its ResultSet is returned + * + * @return a ResultSet that contains the data produced by the + * query - never null + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSet executeQuery() throws SQLException + { + StringBuffer s = new StringBuffer(); + int i; + + for (i = 0 ; i < inStrings.length ; ++i) + { + if (inStrings[i] == null) + throw new SQLException("No value specified for parameter " + (i + 1)); + s.append (templateStrings[i]); + s.append (inStrings[i]); + } + s.append(templateStrings[inStrings.length]); + return super.executeQuery(s.toString()); // in Statement class + } + + /** + * Execute a SQL INSERT, UPDATE or DELETE statement. In addition, + * SQL statements that return nothing such as SQL DDL statements can + * be executed. + * + * @return either the row count for INSERT, UPDATE or DELETE; or + * 0 for SQL statements that return nothing. + * @exception SQLException if a database access error occurs + */ + public int executeUpdate() throws SQLException + { + StringBuffer s = new StringBuffer(); + int i; + + for (i = 0 ; i < inStrings.length ; ++i) + { + if (inStrings[i] == null) + throw new SQLException("No value specified for parameter " + (i + 1)); + s.append (templateStrings[i]); + s.append (inStrings[i]); + } + s.append(templateStrings[inStrings.length]); + return super.executeUpdate(s.toString()); // in Statement class + } + + /** + * Set a parameter to SQL NULL + * + *

Note: You must specify the parameters SQL type (although + * PostgreSQL ignores it) + * + * @param parameterIndex the first parameter is 1, etc... + * @param sqlType the SQL type code defined in java.sql.Types + * @exception SQLException if a database access error occurs + */ + public void setNull(int parameterIndex, int sqlType) throws SQLException + { + set(parameterIndex, "null"); + } + + /** + * Set a parameter to a Java boolean value. The driver converts this + * to a SQL BIT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBoolean(int parameterIndex, boolean x) throws SQLException + { + set(parameterIndex, x ? "'t'" : "'f'"); + } + + /** + * Set a parameter to a Java byte value. The driver converts this to + * a SQL TINYINT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setByte(int parameterIndex, byte x) throws SQLException + { + set(parameterIndex, (new Integer(x)).toString()); + } + + /** + * Set a parameter to a Java short value. The driver converts this + * to a SQL SMALLINT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setShort(int parameterIndex, short x) throws SQLException + { + set(parameterIndex, (new Integer(x)).toString()); + } + + /** + * Set a parameter to a Java int value. The driver converts this to + * a SQL INTEGER value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setInt(int parameterIndex, int x) throws SQLException + { + set(parameterIndex, (new Integer(x)).toString()); + } + + /** + * Set a parameter to a Java long value. The driver converts this to + * a SQL BIGINT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setLong(int parameterIndex, long x) throws SQLException + { + set(parameterIndex, (new Long(x)).toString()); + } + + /** + * Set a parameter to a Java float value. The driver converts this + * to a SQL FLOAT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setFloat(int parameterIndex, float x) throws SQLException + { + set(parameterIndex, (new Float(x)).toString()); + } + + /** + * Set a parameter to a Java double value. The driver converts this + * to a SQL DOUBLE value when it sends it to the database + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setDouble(int parameterIndex, double x) throws SQLException + { + set(parameterIndex, (new Double(x)).toString()); + } + + /** + * Set a parameter to a java.lang.BigDecimal value. The driver + * converts this to a SQL NUMERIC value when it sends it to the + * database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException + { + set(parameterIndex, x.toString()); + } + + /** + * Set a parameter to a Java String value. The driver converts this + * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments + * size relative to the driver's limits on VARCHARs) when it sends it + * to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setString(int parameterIndex, String x) throws SQLException + { + // if the passed string is null, then set this column to null + if(x==null) + set(parameterIndex,"null"); + else { + StringBuffer b = new StringBuffer(); + int i; + + b.append('\''); + for (i = 0 ; i < x.length() ; ++i) + { + char c = x.charAt(i); + if (c == '\\' || c == '\'') + b.append((char)'\\'); + b.append(c); + } + b.append('\''); + set(parameterIndex, b.toString()); + } + } + + /** + * Set a parameter to a Java array of bytes. The driver converts this + * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's + * size relative to the driver's limits on VARBINARYs) when it sends + * it to the database. + * + *

Implementation note: + *
With postgresql, this creates a large object, and stores the + * objects oid in this column. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBytes(int parameterIndex, byte x[]) throws SQLException + { + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + lob.write(x); + lob.close(); + setInt(parameterIndex,oid); + } + + /** + * Set a parameter to a java.sql.Date value. The driver converts this + * to a SQL DATE value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setDate(int parameterIndex, java.sql.Date x) throws SQLException + { + SimpleDateFormat df = new SimpleDateFormat("''"+connection.getDateStyle()+"''"); + + set(parameterIndex, df.format(x)); + + // The above is how the date should be handled. + // + // However, in JDK's prior to 1.1.6 (confirmed with the + // Linux jdk1.1.3 and the Win95 JRE1.1.5), SimpleDateFormat seems + // to format a date to the previous day. So the fix is to add a day + // before formatting. + // + // PS: 86400000 is one day + // + //set(parameterIndex, df.format(new java.util.Date(x.getTime()+86400000))); + } + + /** + * Set a parameter to a java.sql.Time value. The driver converts + * this to a SQL TIME value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1...)); + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setTime(int parameterIndex, Time x) throws SQLException + { + set(parameterIndex, "'" + x.toString() + "'"); + } + + /** + * Set a parameter to a java.sql.Timestamp value. The driver converts + * this to a SQL TIMESTAMP value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException + { + set(parameterIndex, "'" + x.toString() + "'"); + } + + /** + * When a very large ASCII value is input to a LONGVARCHAR parameter, + * it may be more practical to send it via a java.io.InputStream. + * JDBC will read the data from the stream as needed, until it reaches + * end-of-file. The JDBC driver will do any necessary conversion from + * ASCII to the database char format. + * + *

Note: This stream object can either be a standard Java + * stream object or your own subclass that implements the standard + * interface. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @param length the number of bytes in the stream + * @exception SQLException if a database access error occurs + */ + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException + { + setBinaryStream(parameterIndex, x, length); + } + + /** + * When a very large Unicode value is input to a LONGVARCHAR parameter, + * it may be more practical to send it via a java.io.InputStream. + * JDBC will read the data from the stream as needed, until it reaches + * end-of-file. The JDBC driver will do any necessary conversion from + * UNICODE to the database char format. + * + *

Note: This stream object can either be a standard Java + * stream object or your own subclass that implements the standard + * interface. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException + { + setBinaryStream(parameterIndex, x, length); + } + + /** + * When a very large binary value is input to a LONGVARBINARY parameter, + * it may be more practical to send it via a java.io.InputStream. + * JDBC will read the data from the stream as needed, until it reaches + * end-of-file. + * + *

Note: This stream object can either be a standard Java + * stream object or your own subclass that implements the standard + * interface. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException + { + throw new SQLException("InputStream as parameter not supported"); + } + + /** + * In general, parameter values remain in force for repeated used of a + * Statement. Setting a parameter value automatically clears its + * previous value. However, in coms cases, it is useful to immediately + * release the resources used by the current parameter values; this + * can be done by calling clearParameters + * + * @exception SQLException if a database access error occurs + */ + public void clearParameters() throws SQLException + { + int i; + + for (i = 0 ; i < inStrings.length ; i++) + inStrings[i] = null; + } + + /** + * Set the value of a parameter using an object; use the java.lang + * equivalent objects for integral values. + * + *

The given Java object will be converted to the targetSqlType before + * being sent to the database. + * + *

note that this method may be used to pass database-specific + * abstract data types. This is done by using a Driver-specific + * Java type and using a targetSqlType of java.sql.Types.OTHER + * + * @param parameterIndex the first parameter is 1... + * @param x the object containing the input parameter value + * @param targetSqlType The SQL type to be send to the database + * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC + * types this is the number of digits after the decimal. For + * all other types this value will be ignored. + * @exception SQLException if a database access error occurs + */ + public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException + { + switch (targetSqlType) + { + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.REAL: + case Types.FLOAT: + case Types.DOUBLE: + case Types.DECIMAL: + case Types.NUMERIC: + if (x instanceof Boolean) + set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0"); + else + set(parameterIndex, x.toString()); + break; + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + setString(parameterIndex, x.toString()); + break; + case Types.DATE: + setDate(parameterIndex, (java.sql.Date)x); + break; + case Types.TIME: + setTime(parameterIndex, (Time)x); + break; + case Types.TIMESTAMP: + setTimestamp(parameterIndex, (Timestamp)x); + break; + case Types.OTHER: + setString(parameterIndex, ((PGobject)x).getValue()); + break; + default: + throw new SQLException("Unknown Types value"); + } + } + + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException + { + setObject(parameterIndex, x, targetSqlType, 0); + } + + /** + * This stores an Object into a parameter. + *

New for 6.4, if the object is not recognised, but it is + * Serializable, then the object is serialised using the + * postgresql.util.Serialize class. + */ + public void setObject(int parameterIndex, Object x) throws SQLException + { + if (x instanceof String) + setString(parameterIndex, (String)x); + else if (x instanceof BigDecimal) + setBigDecimal(parameterIndex, (BigDecimal)x); + else if (x instanceof Short) + setShort(parameterIndex, ((Short)x).shortValue()); + else if (x instanceof Integer) + setInt(parameterIndex, ((Integer)x).intValue()); + else if (x instanceof Long) + setLong(parameterIndex, ((Long)x).longValue()); + else if (x instanceof Float) + setFloat(parameterIndex, ((Float)x).floatValue()); + else if (x instanceof Double) + setDouble(parameterIndex, ((Double)x).doubleValue()); + else if (x instanceof byte[]) + setBytes(parameterIndex, (byte[])x); + else if (x instanceof java.sql.Date) + setDate(parameterIndex, (java.sql.Date)x); + else if (x instanceof Time) + setTime(parameterIndex, (Time)x); + else if (x instanceof Timestamp) + setTimestamp(parameterIndex, (Timestamp)x); + else if (x instanceof Boolean) + setBoolean(parameterIndex, ((Boolean)x).booleanValue()); + else if (x instanceof PGobject) + setString(parameterIndex, ((PGobject)x).getValue()); + else + setLong(parameterIndex, connection.putObject(x)); + } + + /** + * Some prepared statements return multiple results; the execute method + * handles these complex statements as well as the simpler form of + * statements handled by executeQuery and executeUpdate + * + * @return true if the next result is a ResultSet; false if it is an + * update count or there are no more results + * @exception SQLException if a database access error occurs + */ + public boolean execute() throws SQLException + { + StringBuffer s = new StringBuffer(); + int i; + + for (i = 0 ; i < inStrings.length ; ++i) + { + if (inStrings[i] == null) + throw new SQLException("No value specified for parameter " + (i + 1)); + s.append (templateStrings[i]); + s.append (inStrings[i]); + } + s.append(templateStrings[inStrings.length]); + return super.execute(s.toString()); // in Statement class + } + + /** + * Returns the SQL statement with the current template values + * substituted. + */ + public String toString() { + StringBuffer s = new StringBuffer(); + int i; + + for (i = 0 ; i < inStrings.length ; ++i) + { + if (inStrings[i] == null) + s.append( '?' ); + else + s.append (templateStrings[i]); + s.append (inStrings[i]); + } + s.append(templateStrings[inStrings.length]); + return s.toString(); + } + + // ************************************************************** + // END OF PUBLIC INTERFACE + // ************************************************************** + + /** + * There are a lot of setXXX classes which all basically do + * the same thing. We need a method which actually does the + * set for us. + * + * @param paramIndex the index into the inString + * @param s a string to be stored + * @exception SQLException if something goes wrong + */ + private void set(int paramIndex, String s) throws SQLException + { + if (paramIndex < 1 || paramIndex > inStrings.length) + throw new SQLException("Parameter index out of range"); + inStrings[paramIndex - 1] = s; + } +} diff --git a/src/interfaces/jdbc/postgresql/jdbc1/ResultSet.java b/src/interfaces/jdbc/postgresql/jdbc1/ResultSet.java new file mode 100644 index 0000000000..5d5e0334ac --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc1/ResultSet.java @@ -0,0 +1,776 @@ +package postgresql.jdbc1; + +// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 2 class in the +// postgresql.jdbc2 package. + +import java.lang.*; +import java.io.*; +import java.math.*; +import java.text.*; +import java.util.*; +import java.sql.*; +import postgresql.Field; +import postgresql.largeobject.*; +import postgresql.util.*; + +/** + * A ResultSet provides access to a table of data generated by executing a + * Statement. The table rows are retrieved in sequence. Within a row its + * column values can be accessed in any order. + * + *

A ResultSet maintains a cursor pointing to its current row of data. + * Initially the cursor is positioned before the first row. The 'next' + * method moves the cursor to the next row. + * + *

The getXXX methods retrieve column values for the current row. You can + * retrieve values either using the index number of the column, or by using + * the name of the column. In general using the column index will be more + * efficient. Columns are numbered from 1. + * + *

For maximum portability, ResultSet columns within each row should be read + * in left-to-right order and each column should be read only once. + * + *

For the getXXX methods, the JDBC driver attempts to convert the + * underlying data to the specified Java type and returns a suitable Java + * value. See the JDBC specification for allowable mappings from SQL types + * to Java types with the ResultSet getXXX methods. + * + *

Column names used as input to getXXX methods are case insenstive. When + * performing a getXXX using a column name, if several columns have the same + * name, then the value of the first matching column will be returned. The + * column name option is designed to be used when column names are used in the + * SQL Query. For columns that are NOT explicitly named in the query, it is + * best to use column numbers. If column names were used there is no way for + * 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 + * next result from a sequence of multiple results. + * + *

The number, types and properties of a ResultSet's columns are provided by + * the ResultSetMetaData object returned by the getMetaData method. + * + * @see ResultSetMetaData + * @see java.sql.ResultSet + */ +public class ResultSet extends postgresql.ResultSet implements java.sql.ResultSet +{ + /** + * Create a new ResultSet - Note that we create ResultSets to + * represent the results of everything. + * + * @param fields an array of Field objects (basically, the + * ResultSet MetaData) + * @param tuples Vector of the actual data + * @param status the status string returned from the back end + * @param updateCount the number of rows affected by the operation + * @param cursor the positioned update/delete cursor name + */ + public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount) + { + super(conn,fields,tuples,status,updateCount); + } + + /** + * A ResultSet is initially positioned before its first row, + * the first call to next makes the first row the current row; + * the second call makes the second row the current row, etc. + * + *

If an input stream from the previous row is open, it is + * implicitly closed. The ResultSet's warning chain is cleared + * when a new row is read + * + * @return true if the new current is valid; false if there are no + * more rows + * @exception SQLException if a database access error occurs + */ + public boolean next() throws SQLException + { + if (++current_row >= rows.size()) + return false; + 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 + * when it is automatically closed. The close method provides this + * immediate release. + * + *

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 + * when it is garbage collected. + * + * @exception SQLException if a database access error occurs + */ + public void close() throws SQLException + { + // No-op + } + + /** + * A column may have the value of SQL NULL; wasNull() reports whether + * the last column read had this special value. Note that you must + * first call getXXX on a column to try to read its value and then + * call wasNull() to find if the value was SQL NULL + * + * @return true if the last column read was SQL NULL + * @exception SQLException if a database access error occurred + */ + public boolean wasNull() throws SQLException + { + return wasNullFlag; + } + + /** + * Get the value of a column in the current row as a Java String + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value, null for SQL NULL + * @exception SQLException if a database access error occurs + */ + public String getString(int columnIndex) throws SQLException + { + //byte[] bytes = getBytes(columnIndex); + // + //if (bytes == null) + //return null; + //return new String(bytes); + if (columnIndex < 1 || columnIndex > fields.length) + throw new SQLException("Column Index out of range"); + wasNullFlag = (this_row[columnIndex - 1] == null); + if(wasNullFlag) + return null; + return new String(this_row[columnIndex - 1]); + } + + /** + * Get the value of a column in the current row as a Java boolean + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value, false for SQL NULL + * @exception SQLException if a database access error occurs + */ + public boolean getBoolean(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + int c = s.charAt(0); + return ((c == 't') || (c == 'T')); + } + return false; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java byte. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public byte getByte(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Byte.parseByte(s); + } catch (NumberFormatException e) { + throw new SQLException("Bad Byte Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java short. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public short getShort(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Short.parseShort(s); + } catch (NumberFormatException e) { + throw new SQLException("Bad Short Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java int. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public int getInt(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Integer Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java long. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public long getLong(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Long.parseLong(s); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Long Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java float. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public float getFloat(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Float.valueOf(s).floatValue(); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Float Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java double. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public double getDouble(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Double.valueOf(s).doubleValue(); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Double Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a + * java.lang.BigDecimal object + * + * @param columnIndex the first column is 1, the second is 2... + * @param scale the number of digits to the right of the decimal + * @return the column value; if the value is SQL NULL, null + * @exception SQLException if a database access error occurs + */ + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException + { + String s = getString(columnIndex); + BigDecimal val; + + if (s != null) + { + try + { + val = new BigDecimal(s); + } catch (NumberFormatException e) { + throw new SQLException ("Bad BigDecimal Form: " + s); + } + try + { + return val.setScale(scale); + } catch (ArithmeticException e) { + throw new SQLException ("Bad BigDecimal Form: " + s); + } + } + return null; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java byte array. + * + *

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

Be warned If the large object is huge, then you may run out + * of memory. + * + * @param columnIndex the first column is 1, the second is 2, ... + * @return the column value; if the value is SQL NULL, the result + * is null + * @exception SQLException if a database access error occurs + */ + public byte[] getBytes(int columnIndex) throws SQLException + { + if (columnIndex < 1 || columnIndex > fields.length) + throw new SQLException("Column Index out of range"); + wasNullFlag = (this_row[columnIndex - 1] == null); + + // Handle OID's as BLOBS + if(!wasNullFlag) + if( fields[columnIndex - 1].getOID() == 26) { + LargeObjectManager lom = connection.getLargeObjectAPI(); + LargeObject lob = lom.open(getInt(columnIndex)); + byte buf[] = lob.read(lob.size()); + lob.close(); + return buf; + } + + return this_row[columnIndex - 1]; + } + + /** + * Get the value of a column in the current row as a java.sql.Date + * object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public java.sql.Date getDate(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + if(s==null) + return null; + SimpleDateFormat df = new SimpleDateFormat(connection.getDateStyle()); + try { + return new java.sql.Date(df.parse(s).getTime()); + } catch (ParseException e) { + throw new SQLException("Bad Date Format: at " + e.getErrorOffset() + " in " + s); + } + } + + /** + * Get the value of a column in the current row as a java.sql.Time + * object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public Time getTime(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + if (s.length() != 5 && s.length() != 8) + throw new NumberFormatException("Wrong Length!"); + int hr = Integer.parseInt(s.substring(0,2)); + int min = Integer.parseInt(s.substring(3,5)); + int sec = (s.length() == 5) ? 0 : Integer.parseInt(s.substring(6)); + return new Time(hr, min, sec); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Time Form: " + s); + } + } + return null; // SQL NULL + } + + /** + * 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... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public Timestamp getTimestamp(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzz"); + + if (s != null) + { + int TZ = new Float(s.substring(19)).intValue(); + TZ = TZ * 60 * 60 * 1000; + TimeZone zone = TimeZone.getDefault(); + zone.setRawOffset(TZ); + String nm = zone.getID(); + s = s.substring(0,18) + nm; + try { + java.util.Date d = df.parse(s); + return new Timestamp(d.getTime()); + } catch (ParseException e) { + throw new SQLException("Bad Timestamp Format: at " + e.getErrorOffset() + " in " + s); + } + } + return null; // SQL NULL + } + + /** + * A column value can be retrieved as a stream of ASCII characters + * 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. + * + *

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

We implement an ASCII stream as a Binary stream - we should really + * do the data conversion, but I cannot be bothered to implement this + * right now. + * + * @param columnIndex the first column is 1, the second is 2, ... + * @return a Java InputStream that delivers the database column + * value as a stream of one byte ASCII characters. If the + * value is SQL NULL then the result is null + * @exception SQLException if a database access error occurs + * @see getBinaryStream + */ + public InputStream getAsciiStream(int columnIndex) throws SQLException + { + return getBinaryStream(columnIndex); + } + + /** + * A column value can also be retrieved as a stream of Unicode + * characters. We implement this as a binary stream. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Java InputStream that delivers the database column value + * as a stream of two byte Unicode characters. If the value is + * SQL NULL, then the result is null + * @exception SQLException if a database access error occurs + * @see getAsciiStream + * @see getBinaryStream + */ + public InputStream getUnicodeStream(int columnIndex) throws SQLException + { + return getBinaryStream(columnIndex); + } + + /** + * A column value can also be retrieved as a binary strea. This + * method is suitable for retrieving LONGVARBINARY values. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Java InputStream that delivers the database column value + * as a stream of bytes. If the value is SQL NULL, then the result + * is null + * @exception SQLException if a database access error occurs + * @see getAsciiStream + * @see getUnicodeStream + */ + 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. + * + * @param columnName is the SQL name of the column + * @return the column value + * @exception SQLException if a database access error occurs + */ + public String getString(String columnName) throws SQLException + { + 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)); + } + + public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException + { + 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)); + } + + public InputStream getUnicodeStream(String columnName) throws SQLException + { + 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 + * to this SQLWarning. + * + *

The warning chain is automatically cleared each time a new + * row is read. + * + *

Note: This warning chain only covers warnings caused by + * ResultSet methods. Any warnings caused by statement methods + * (such as reading OUT parameters) will be chained on the + * Statement object. + * + * @return the first SQLWarning or null; + * @exception SQLException if a database access error occurs. + */ + public SQLWarning getWarnings() throws SQLException + { + return warnings; + } + + /** + * After this call, getWarnings returns null until a new warning + * is reported for this ResultSet + * + * @exception SQLException if a database access error occurs + */ + public void clearWarnings() throws SQLException + { + warnings = null; + } + + /** + * Get the name of the SQL cursor used by this ResultSet + * + *

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

JDBC supports this SQL feature by providing the name of the + * SQL cursor used by a ResultSet. The current row of a ResulSet + * is also the current row of this SQL cursor. + * + *

Note: If positioned update is not supported, a SQLException + * is thrown. + * + * @return the ResultSet's SQL cursor name. + * @exception SQLException if a database access error occurs + */ + public String getCursorName() throws SQLException + { + return connection.getCursorName(); + } + + /** + * The numbers, types and properties of a ResultSet's columns are + * provided by the getMetaData method + * + * @return a description of the ResultSet's columns + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSetMetaData getMetaData() throws SQLException + { + return new ResultSetMetaData(rows, fields); + } + + /** + * Get the value of a column in the current row as a Java object + * + *

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

This method may also be used to read database specific abstract + * data types. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Object holding the column value + * @exception SQLException if a database access error occurs + */ + public Object getObject(int columnIndex) throws SQLException + { + Field field; + + if (columnIndex < 1 || columnIndex > fields.length) + throw new SQLException("Column index out of range"); + 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: + return new Boolean(getBoolean(columnIndex)); + case Types.SMALLINT: + return new Integer(getInt(columnIndex)); + case Types.INTEGER: + return new Integer(getInt(columnIndex)); + case Types.BIGINT: + return new Long(getLong(columnIndex)); + case Types.NUMERIC: + return getBigDecimal(columnIndex, 0); + case Types.REAL: + return new Float(getFloat(columnIndex)); + case Types.DOUBLE: + return new Double(getDouble(columnIndex)); + case Types.CHAR: + case Types.VARCHAR: + return getString(columnIndex); + case Types.DATE: + return getDate(columnIndex); + case Types.TIME: + return getTime(columnIndex); + case Types.TIMESTAMP: + return getTimestamp(columnIndex); + default: + return connection.getObject(field.getTypeName(), getString(columnIndex)); + } + } + + /** + * Get the value of a column in the current row as a Java object + * + *

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

This method may also be used to read database specific abstract + * data types. + * + * @param columnName is the SQL name of the column + * @return a Object holding the column value + * @exception SQLException if a database access error occurs + */ + public Object getObject(String columnName) throws SQLException + { + return getObject(findColumn(columnName)); + } + + /** + * Map a ResultSet column name to a ResultSet column index + * + * @param columnName the name of the column + * @return the column index + * @exception SQLException if a database access error occurs + */ + 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 SQLException ("Column name not found"); + } +} + diff --git a/src/interfaces/jdbc/postgresql/jdbc1/ResultSetMetaData.java b/src/interfaces/jdbc/postgresql/jdbc1/ResultSetMetaData.java new file mode 100644 index 0000000000..95492e81b9 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc1/ResultSetMetaData.java @@ -0,0 +1,426 @@ +package postgresql.jdbc1; + +// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 2 class in the +// postgresql.jdbc2 package. + +import java.lang.*; +import java.sql.*; +import java.util.*; +import postgresql.*; + +/** + * A ResultSetMetaData object can be used to find out about the types and + * properties of the columns in a ResultSet + * + * @see java.sql.ResultSetMetaData + */ +public class ResultSetMetaData implements java.sql.ResultSetMetaData +{ + Vector rows; + Field[] fields; + + /** + * Initialise for a result with a tuple set and + * a field descriptor set + * + * @param rows the Vector of rows returned by the ResultSet + * @param fields the array of field descriptors + */ + public ResultSetMetaData(Vector rows, Field[] fields) + { + this.rows = rows; + this.fields = fields; + } + + /** + * Whats the number of columns in the ResultSet? + * + * @return the number + * @exception SQLException if a database access error occurs + */ + public int getColumnCount() throws SQLException + { + return fields.length; + } + + /** + * Is the column automatically numbered (and thus read-only) + * I believe that PostgreSQL does not support this feature. + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isAutoIncrement(int column) throws SQLException + { + return false; + } + + /** + * Does a column's case matter? ASSUMPTION: Any field that is + * not obviously case insensitive is assumed to be case sensitive + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isCaseSensitive(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + case Types.INTEGER: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return false; + default: + return true; + } + } + + /** + * Can the column be used in a WHERE clause? Basically for + * this, I split the functions into two types: recognised + * types (which are always useable), and OTHER types (which + * may or may not be useable). The OTHER types, for now, I + * will assume they are useable. We should really query the + * catalog to see if they are useable. + * + * @param column the first column is 1, the second is 2... + * @return true if they can be used in a WHERE clause + * @exception SQLException if a database access error occurs + */ + public boolean isSearchable(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + // This switch is pointless, I know - but it is a set-up + // for further expansion. + switch (sql_type) + { + case Types.OTHER: + return true; + default: + return true; + } + } + + /** + * Is the column a cash value? 6.1 introduced the cash/money + * type, which haven't been incorporated as of 970414, so I + * just check the type name for both 'cash' and 'money' + * + * @param column the first column is 1, the second is 2... + * @return true if its a cash column + * @exception SQLException if a database access error occurs + */ + public boolean isCurrency(int column) throws SQLException + { + String type_name = getField(column).getTypeName(); + + return type_name.equals("cash") || type_name.equals("money"); + } + + /** + * Can you put a NULL in this column? I think this is always + * true in 6.1's case. It would only be false if the field had + * been defined NOT NULL (system catalogs could be queried?) + * + * @param column the first column is 1, the second is 2... + * @return one of the columnNullable values + * @exception SQLException if a database access error occurs + */ + public int isNullable(int column) throws SQLException + { + return columnNullable; // We can always put NULL in + } + + /** + * Is the column a signed number? In PostgreSQL, all numbers + * are signed, so this is trivial. However, strings are not + * signed (duh!) + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isSigned(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + case Types.INTEGER: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + return true; + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return false; // I don't know about these? + default: + return false; + } + } + + /** + * What is the column's normal maximum width in characters? + * + * @param column the first column is 1, the second is 2, etc. + * @return the maximum width + * @exception SQLException if a database access error occurs + */ + public int getColumnDisplaySize(int column) throws SQLException + { + int max = getColumnLabel(column).length(); + int i; + + for (i = 0 ; i < rows.size(); ++i) + { + byte[][] x = (byte[][])(rows.elementAt(i)); + if(x[column-1]!=null) { + int xl = x[column - 1].length; + if (xl > max) + max = xl; + } + } + return max; + } + + /** + * What is the suggested column title for use in printouts and + * displays? We suggest the ColumnName! + * + * @param column the first column is 1, the second is 2, etc. + * @return the column label + * @exception SQLException if a database access error occurs + */ + public String getColumnLabel(int column) throws SQLException + { + return getColumnName(column); + } + + /** + * What's a column's name? + * + * @param column the first column is 1, the second is 2, etc. + * @return the column name + * @exception SQLException if a database access error occurs + */ + public String getColumnName(int column) throws SQLException + { + Field f = getField(column); + if(f!=null) + return f.name; + return "field"+column; + } + + /** + * What is a column's table's schema? This relies on us knowing + * the table name....which I don't know how to do as yet. The + * JDBC specification allows us to return "" if this is not + * applicable. + * + * @param column the first column is 1, the second is 2... + * @return the Schema + * @exception SQLException if a database access error occurs + */ + public String getSchemaName(int column) throws SQLException + { + return ""; + } + + /** + * What is a column's number of decimal digits. + * + * @param column the first column is 1, the second is 2... + * @return the precision + * @exception SQLException if a database access error occurs + */ + public int getPrecision(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + return 5; + case Types.INTEGER: + return 10; + case Types.REAL: + return 8; + case Types.FLOAT: + return 16; + case Types.DOUBLE: + return 16; + case Types.VARCHAR: + return 0; + default: + return 0; + } + } + + /** + * What is a column's number of digits to the right of the + * decimal point? + * + * @param column the first column is 1, the second is 2... + * @return the scale + * @exception SQLException if a database access error occurs + */ + public int getScale(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + return 0; + case Types.INTEGER: + return 0; + case Types.REAL: + return 8; + case Types.FLOAT: + return 16; + case Types.DOUBLE: + return 16; + case Types.VARCHAR: + return 0; + default: + return 0; + } + } + + /** + * Whats a column's table's name? How do I find this out? Both + * getSchemaName() and getCatalogName() rely on knowing the table + * Name, so we need this before we can work on them. + * + * @param column the first column is 1, the second is 2... + * @return column name, or "" if not applicable + * @exception SQLException if a database access error occurs + */ + public String getTableName(int column) throws SQLException + { + return ""; + } + + /** + * What's a column's table's catalog name? As with getSchemaName(), + * we can say that if getTableName() returns n/a, then we can too - + * otherwise, we need to work on it. + * + * @param column the first column is 1, the second is 2... + * @return catalog name, or "" if not applicable + * @exception SQLException if a database access error occurs + */ + public String getCatalogName(int column) throws SQLException + { + return ""; + } + + /** + * What is a column's SQL Type? (java.sql.Type int) + * + * @param column the first column is 1, the second is 2, etc. + * @return the java.sql.Type value + * @exception SQLException if a database access error occurs + * @see postgresql.Field#getSQLType + * @see java.sql.Types + */ + public int getColumnType(int column) throws SQLException + { + return getField(column).getSQLType(); + } + + /** + * Whats is the column's data source specific type name? + * + * @param column the first column is 1, the second is 2, etc. + * @return the type name + * @exception SQLException if a database access error occurs + */ + public String getColumnTypeName(int column) throws SQLException + { + return getField(column).getTypeName(); + } + + /** + * Is the column definitely not writable? In reality, we would + * have to check the GRANT/REVOKE stuff for this to be effective, + * and I haven't really looked into that yet, so this will get + * re-visited. + * + * @param column the first column is 1, the second is 2, etc. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly(int column) throws SQLException + { + return false; + } + + /** + * Is it possible for a write on the column to succeed? Again, we + * would in reality have to check the GRANT/REVOKE stuff, which + * I haven't worked with as yet. However, if it isn't ReadOnly, then + * it is obviously writable. + * + * @param column the first column is 1, the second is 2, etc. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isWritable(int column) throws SQLException + { + if (isReadOnly(column)) + return true; + else + return false; + } + + /** + * Will a write on this column definately succeed? Hmmm...this + * is a bad one, since the two preceding functions have not been + * really defined. I cannot tell is the short answer. I thus + * return isWritable() just to give us an idea. + * + * @param column the first column is 1, the second is 2, etc.. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isDefinitelyWritable(int column) throws SQLException + { + return isWritable(column); + } + + // ******************************************************** + // END OF PUBLIC INTERFACE + // ******************************************************** + + /** + * For several routines in this package, we need to convert + * a columnIndex into a Field[] descriptor. Rather than do + * the same code several times, here it is. + * + * @param columnIndex the first column is 1, the second is 2... + * @return the Field description + * @exception SQLException if a database access error occurs + */ + private Field getField(int columnIndex) throws SQLException + { + if (columnIndex < 1 || columnIndex > fields.length) + throw new SQLException("Column index out of range"); + return fields[columnIndex - 1]; + } +} + diff --git a/src/interfaces/jdbc/postgresql/jdbc1/Statement.java b/src/interfaces/jdbc/postgresql/jdbc1/Statement.java new file mode 100644 index 0000000000..4bc026331b --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc1/Statement.java @@ -0,0 +1,323 @@ +package postgresql.jdbc1; + +// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 2 class in the +// postgresql.jdbc2 package. + +import java.sql.*; + +/** + * 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. + * 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 + * statement's current ResultSet if an open one exists. + * + * @see java.sql.Statement + * @see ResultSet + */ +public class Statement implements java.sql.Statement +{ + Connection connection; // The connection who created us + java.sql.ResultSet result = null; // The current results + 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. + * + * @param c the Connection instantation that creates us + */ + public Statement (Connection c) + { + connection = c; + } + + /** + * Execute a SQL statement that retruns a single ResultSet + * + * @param sql typically a static SQL SELECT statement + * @return a ResulSet that contains the data produced by the query + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSet executeQuery(String sql) throws SQLException + { + this.execute(sql); + while (result != null && !((postgresql.ResultSet)result).reallyResultSet()) + result = ((postgresql.ResultSet)result).getNext(); + if (result == null) + throw new SQLException("no results returned"); + return result; + } + + /** + * Execute a SQL INSERT, UPDATE or DELETE statement. In addition + * SQL statements that return nothing such as SQL DDL statements + * can be executed + * + * @param sql a SQL statement + * @return either a row count, or 0 for SQL commands + * @exception SQLException if a database access error occurs + */ + public int executeUpdate(String sql) throws SQLException + { + this.execute(sql); + if (((postgresql.ResultSet)result).reallyResultSet()) + throw new SQLException("results returned"); + return this.getUpdateCount(); + } + + /** + * In many cases, it is desirable to immediately release a + * Statement's database and JDBC resources instead of waiting + * 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 + * ResultSet, if one exists, is also closed. + * + * @exception SQLException if a database access error occurs (why?) + */ + public void close() throws SQLException + { + result = null; + } + + /** + * The maxFieldSize limit (in bytes) is the maximum amount of + * data returned for any column value; it only applies to + * BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR + * columns. If the limit is exceeded, the excess data is silently + * discarded. + * + * @return the current max column size limit; zero means unlimited + * @exception SQLException if a database access error occurs + */ + public int getMaxFieldSize() throws SQLException + { + return 8192; // We cannot change this + } + + /** + * Sets the maxFieldSize - NOT! - We throw an SQLException just + * to inform them to stop doing this. + * + * @param max the new max column size limit; zero means unlimited + * @exception SQLException if a database access error occurs + */ + public void setMaxFieldSize(int max) throws SQLException + { + throw new SQLException("Attempt to setMaxFieldSize failed - compile time default"); + } + + /** + * The maxRows limit is set to limit the number of rows that + * any ResultSet can contain. If the limit is exceeded, the + * excess rows are silently dropped. + * + * @return the current maximum row limit; zero means unlimited + * @exception SQLException if a database access error occurs + */ + public int getMaxRows() throws SQLException + { + return connection.maxrows; + } + + /** + * Set the maximum number of rows + * + * @param max the new max rows limit; zero means unlimited + * @exception SQLException if a database access error occurs + * @see getMaxRows + */ + public void setMaxRows(int max) throws SQLException + { + connection.maxrows = max; + } + + /** + * If escape scanning is on (the default), the driver will do escape + * substitution before sending the SQL to the database. + * + * @param enable true to enable; false to disable + * @exception SQLException if a database access error occurs + */ + public void setEscapeProcessing(boolean enable) throws SQLException + { + escapeProcessing = enable; + } + + /** + * The queryTimeout limit is the number of seconds the driver + * will wait for a Statement to execute. If the limit is + * exceeded, a SQLException is thrown. + * + * @return the current query timeout limit in seconds; 0 = unlimited + * @exception SQLException if a database access error occurs + */ + public int getQueryTimeout() throws SQLException + { + return timeout; + } + + /** + * Sets the queryTimeout limit + * + * @param seconds - the new query timeout limit in seconds + * @exception SQLException if a database access error occurs + */ + public void setQueryTimeout(int seconds) throws SQLException + { + timeout = seconds; + } + + /** + * 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 + * define it as a no-op (i.e. you can't cancel, but there is no + * error if you try.) + * + * 6.4 introduced a cancel operation, but we have not implemented it + * yet. Sometime before 6.5, this method will be implemented. + * + * @exception SQLException only because thats the spec. + */ + public void cancel() throws SQLException + { + // No-op + } + + /** + * The first warning reported by calls on this Statement is + * returned. A Statement's execute methods clear its SQLWarning + * chain. Subsequent Statement warnings will be chained to this + * SQLWarning. + * + *

The Warning chain is automatically cleared each time a statement + * is (re)executed. + * + *

Note: If you are processing a ResultSet then any warnings + * associated with ResultSet reads will be chained on the ResultSet + * object. + * + * @return the first SQLWarning on null + * @exception SQLException if a database access error occurs + */ + public SQLWarning getWarnings() throws SQLException + { + return warnings; + } + + /** + * After this call, getWarnings returns null until a new warning + * is reported for this Statement. + * + * @exception SQLException if a database access error occurs (why?) + */ + public void clearWarnings() throws SQLException + { + warnings = null; + } + + /** + * setCursorName defines the SQL cursor name that will be used by + * subsequent execute methods. This name can then be used in SQL + * positioned update/delete statements to identify the current row + * in the ResultSet generated by this statement. If a database + * doesn't support positioned update/delete, this method is a + * no-op. + * + *

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

We throw an additional constriction. There can only be one + * cursor active at any one time. + * + * @param name the new cursor name + * @exception SQLException if a database access error occurs + */ + public void setCursorName(String name) throws SQLException + { + connection.setCursorName(name); + } + + /** + * 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 + * retrieve the result. + * + * @param sql any SQL statement + * @return true if the next result is a ResulSet, false if it is + * an update count or there are no more results + * @exception SQLException if a database access error occurs + */ + public boolean execute(String sql) throws SQLException + { + result = connection.ExecSQL(sql); + return (result != null && ((postgresql.ResultSet)result).reallyResultSet()); + } + + /** + * getResultSet returns the current result as a ResultSet. It + * should only be called once per result. + * + * @return the current result set; null if there are no more + * @exception SQLException if a database access error occurs (why?) + */ + public java.sql.ResultSet getResultSet() throws SQLException + { + return result; + } + + /** + * getUpdateCount returns the current result as an update count, + * if the result is a ResultSet or there are no more results, -1 + * is returned. It should only be called once per result. + * + * @return the current result as an update count. + * @exception SQLException if a database access error occurs + */ + public int getUpdateCount() throws SQLException + { + if (result == null) return -1; + if (((postgresql.ResultSet)result).reallyResultSet()) return -1; + return ((postgresql.ResultSet)result).getResultCount(); + } + + /** + * getMoreResults moves to a Statement's next result. If it returns + * true, this result is a ResulSet. + * + * @return true if the next ResultSet is valid + * @exception SQLException if a database access error occurs + */ + public boolean getMoreResults() throws SQLException + { + result = ((postgresql.ResultSet)result).getNext(); + return (result != null && ((postgresql.ResultSet)result).reallyResultSet()); + } + + /** + * Returns the status message from the current Result.

+ * This is used internally by the driver. + * + * @return status message from backend + */ + public String getResultStatusString() + { + if(result == null) + return null; + return ((postgresql.ResultSet)result).getStatusString(); + } +} diff --git a/src/interfaces/jdbc/postgresql/jdbc2/CallableStatement.java b/src/interfaces/jdbc/postgresql/jdbc2/CallableStatement.java new file mode 100644 index 0000000000..6cbc2ef148 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc2/CallableStatement.java @@ -0,0 +1,361 @@ +package postgresql.jdbc2; + +// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 1 class in the +// postgresql.jdbc1 package. + +import java.sql.*; +import java.math.*; + +/** + * CallableStatement is used to execute SQL stored procedures. + * + *

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

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

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

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

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

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

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

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

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

See the JDBC spec's "Dynamic Programming" chapter for details. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return A java.lang.Object holding the OUT parameter value. + * @exception SQLException if a database-access error occurs. + */ + public Object getObject(int parameterIndex) + throws SQLException { + return null; + } + + // ** JDBC 2 Extensions ** + + public Array getArray(int i) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public java.math.BigDecimal getBigDecimal(int i) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Blob getBlob(int i) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Clob getClob(int i) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Object getObject(int i,java.util.Map map) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Ref getRef(int i) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public java.sql.Date getDate(int i,java.util.Calendar cal) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Time getTime(int i,java.util.Calendar cal) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Timestamp getTimestamp(int i,java.util.Calendar cal) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void registerOutParameter(int parameterIndex, int sqlType,String typeName) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + +} + diff --git a/src/interfaces/jdbc/postgresql/jdbc2/Connection.java b/src/interfaces/jdbc/postgresql/jdbc2/Connection.java new file mode 100644 index 0000000000..53e1bd6529 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc2/Connection.java @@ -0,0 +1,418 @@ +package postgresql.jdbc2; + +// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 1 class in the +// postgresql.jdbc1 package. + +import java.io.*; +import java.lang.*; +import java.lang.reflect.*; +import java.net.*; +import java.util.*; +import java.sql.*; +import postgresql.Field; +import postgresql.fastpath.*; +import postgresql.largeobject.*; +import postgresql.util.*; + +/** + * $Id: Connection.java,v 1.1 1999/01/17 04:51:56 momjian Exp $ + * + * A Connection represents a session with a specific database. Within the + * context of a Connection, SQL statements are executed and results are + * returned. + * + *

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

Note: By default, the Connection automatically commits changes + * after executing each statement. If auto-commit has been disabled, an + * explicit commit must be done or database changes will not be saved. + * + * @see java.sql.Connection + */ +public class Connection extends postgresql.Connection implements java.sql.Connection +{ + // This is a cache of the DatabaseMetaData instance for this connection + protected DatabaseMetaData metadata; + + /** + * SQL statements without parameters are normally executed using + * Statement objects. If the same SQL statement is executed many + * times, it is more efficient to use a PreparedStatement + * + * @return a new Statement object + * @exception SQLException passed through from the constructor + */ + public java.sql.Statement createStatement() throws SQLException + { + return new Statement(this); + } + + /** + * A SQL statement with or without IN parameters can be pre-compiled + * and stored in a PreparedStatement object. This object can then + * be used to efficiently execute this statement multiple times. + * + * Note: This method is optimized for handling parametric + * SQL statements that benefit from precompilation if the drivers + * supports precompilation. PostgreSQL does not support precompilation. + * In this case, the statement is not sent to the database until the + * PreparedStatement is executed. This has no direct effect on users; + * however it does affect which method throws certain SQLExceptions + * + * @param sql a SQL statement that may contain one or more '?' IN + * parameter placeholders + * @return a new PreparedStatement object containing the pre-compiled + * statement. + * @exception SQLException if a database access error occurs. + */ + public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException + { + return new PreparedStatement(this, sql); + } + + /** + * A SQL stored procedure call statement is handled by creating a + * CallableStatement for it. The CallableStatement provides methods + * for setting up its IN and OUT parameters and methods for executing + * it. + * + * Note: This method is optimised for handling stored procedure + * call statements. Some drivers may send the call statement to the + * database when the prepareCall is done; others may wait until the + * CallableStatement is executed. This has no direct effect on users; + * however, it does affect which method throws certain SQLExceptions + * + * @param sql a SQL statement that may contain one or more '?' parameter + * placeholders. Typically this statement is a JDBC function call + * escape string. + * @return a new CallableStatement object containing the pre-compiled + * SQL statement + * @exception SQLException if a database access error occurs + */ + public java.sql.CallableStatement prepareCall(String sql) throws SQLException + { + throw new SQLException("Callable Statements are not supported at this time"); + // return new CallableStatement(this, sql); + } + + /** + * A driver may convert the JDBC sql grammar into its system's + * native SQL grammar prior to sending it; nativeSQL returns the + * native form of the statement that the driver would have sent. + * + * @param sql a SQL statement that may contain one or more '?' + * parameter placeholders + * @return the native form of this statement + * @exception SQLException if a database access error occurs + */ + public String nativeSQL(String sql) throws SQLException + { + return sql; + } + + /** + * If a connection is in auto-commit mode, than all its SQL + * statements will be executed and committed as individual + * transactions. Otherwise, its SQL statements are grouped + * into transactions that are terminated by either commit() + * or rollback(). By default, new connections are in auto- + * commit mode. The commit occurs when the statement completes + * or the next execute occurs, whichever comes first. In the + * case of statements returning a ResultSet, the statement + * completes when the last row of the ResultSet has been retrieved + * or the ResultSet has been closed. In advanced cases, a single + * statement may return multiple results as well as output parameter + * values. Here the commit occurs when all results and output param + * values have been retrieved. + * + * @param autoCommit - true enables auto-commit; false disables it + * @exception SQLException if a database access error occurs + */ + public void setAutoCommit(boolean autoCommit) throws SQLException + { + if (this.autoCommit == autoCommit) + return; + if (autoCommit) + ExecSQL("end"); + else + ExecSQL("begin"); + this.autoCommit = autoCommit; + } + + /** + * gets the current auto-commit state + * + * @return Current state of the auto-commit mode + * @exception SQLException (why?) + * @see setAutoCommit + */ + public boolean getAutoCommit() throws SQLException + { + return this.autoCommit; + } + + /** + * The method commit() makes all changes made since the previous + * commit/rollback permanent and releases any database locks currently + * held by the Connection. This method should only be used when + * auto-commit has been disabled. (If autoCommit == true, then we + * just return anyhow) + * + * @exception SQLException if a database access error occurs + * @see setAutoCommit + */ + public void commit() throws SQLException + { + if (autoCommit) + return; + ExecSQL("commit"); + autoCommit = true; + ExecSQL("begin"); + autoCommit = false; + } + + /** + * The method rollback() drops all changes made since the previous + * commit/rollback and releases any database locks currently held by + * the Connection. + * + * @exception SQLException if a database access error occurs + * @see commit + */ + public void rollback() throws SQLException + { + if (autoCommit) + return; + ExecSQL("rollback"); + autoCommit = true; + ExecSQL("begin"); + autoCommit = false; + } + + /** + * In some cases, it is desirable to immediately release a Connection's + * database and JDBC resources instead of waiting for them to be + * automatically released (cant think why off the top of my head) + * + * Note: A Connection is automatically closed when it is + * garbage collected. Certain fatal errors also result in a closed + * connection. + * + * @exception SQLException if a database access error occurs + */ + public void close() throws SQLException + { + if (pg_stream != null) + { + try + { + pg_stream.close(); + } catch (IOException e) {} + pg_stream = null; + } + } + + /** + * Tests to see if a Connection is closed + * + * @return the status of the connection + * @exception SQLException (why?) + */ + public boolean isClosed() throws SQLException + { + return (pg_stream == null); + } + + /** + * A connection's database is able to provide information describing + * its tables, its supported SQL grammar, its stored procedures, the + * capabilities of this connection, etc. This information is made + * available through a DatabaseMetaData object. + * + * @return a DatabaseMetaData object for this connection + * @exception SQLException if a database access error occurs + */ + public java.sql.DatabaseMetaData getMetaData() throws SQLException + { + if(metadata==null) + metadata = new DatabaseMetaData(this); + return metadata; + } + + /** + * You can put a connection in read-only mode as a hunt to enable + * database optimizations + * + * Note: setReadOnly cannot be called while in the middle + * of a transaction + * + * @param readOnly - true enables read-only mode; false disables it + * @exception SQLException if a database access error occurs + */ + public void setReadOnly (boolean readOnly) throws SQLException + { + this.readOnly = readOnly; + } + + /** + * Tests to see if the connection is in Read Only Mode. Note that + * we cannot really put the database in read only mode, but we pretend + * we can by returning the value of the readOnly flag + * + * @return true if the connection is read only + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly() throws SQLException + { + return readOnly; + } + + /** + * A sub-space of this Connection's database may be selected by + * setting a catalog name. If the driver does not support catalogs, + * it will silently ignore this request + * + * @exception SQLException if a database access error occurs + */ + public void setCatalog(String catalog) throws SQLException + { + // No-op + } + + /** + * Return the connections current catalog name, or null if no + * catalog name is set, or we dont support catalogs. + * + * @return the current catalog name or null + * @exception SQLException if a database access error occurs + */ + public String getCatalog() throws SQLException + { + return null; + } + + /** + * You can call this method to try to change the transaction + * isolation level using one of the TRANSACTION_* values. + * + * Note: setTransactionIsolation cannot be called while + * in the middle of a transaction + * + * @param level one of the TRANSACTION_* isolation values with + * the exception of TRANSACTION_NONE; some databases may + * not support other values + * @exception SQLException if a database access error occurs + * @see java.sql.DatabaseMetaData#supportsTransactionIsolationLevel + */ + public void setTransactionIsolation(int level) throws SQLException + { + throw new SQLException("Transaction Isolation Levels are not implemented"); + } + + /** + * Get this Connection's current transaction isolation mode. + * + * @return the current TRANSACTION_* mode value + * @exception SQLException if a database access error occurs + */ + public int getTransactionIsolation() throws SQLException + { + return java.sql.Connection.TRANSACTION_SERIALIZABLE; + } + + /** + * The first warning reported by calls on this Connection is + * returned. + * + * Note: Sebsequent warnings will be changed to this + * SQLWarning + * + * @return the first SQLWarning or null + * @exception SQLException if a database access error occurs + */ + public SQLWarning getWarnings() throws SQLException + { + return firstWarning; + } + + /** + * After this call, getWarnings returns null until a new warning + * is reported for this connection. + * + * @exception SQLException if a database access error occurs + */ + public void clearWarnings() throws SQLException + { + firstWarning = null; + } + + /** + * This overides the method in postgresql.Connection and returns a + * ResultSet. + */ + protected java.sql.ResultSet getResultSet(postgresql.Connection conn, Field[] fields, Vector tuples, String status, int updateCount) throws SQLException + { + return new postgresql.jdbc2.ResultSet((postgresql.jdbc2.Connection)conn,fields,tuples,status,updateCount); + } + + // ***************** + // JDBC 2 extensions + // ***************** + + public java.sql.Statement createStatement(int resultSetType,int resultSetConcurrency) throws SQLException + { + // normal create followed by 2 sets? + throw postgresql.Driver.notImplemented(); + } + + public java.sql.PreparedStatement prepareStatement(String sql,int resultSetType,int resultSetConcurrency) throws SQLException + { + // normal prepare followed by 2 sets? + throw postgresql.Driver.notImplemented(); + } + + public java.sql.CallableStatement prepareCall(String sql,int resultSetType,int resultSetConcurrency) throws SQLException + { + // normal prepare followed by 2 sets? + throw postgresql.Driver.notImplemented(); + } + + public int getResultSetConcurrency() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public int getResultSetType() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public java.util.Map getTypeMap() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setResultSetConcurrency(int value) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setResultSetType(int type) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setTypeMap(java.util.Map map) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + +} + +// *********************************************************************** + diff --git a/src/interfaces/jdbc/postgresql/jdbc2/DatabaseMetaData.java b/src/interfaces/jdbc/postgresql/jdbc2/DatabaseMetaData.java new file mode 100644 index 0000000000..9a73f22d81 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc2/DatabaseMetaData.java @@ -0,0 +1,2623 @@ +package postgresql.jdbc2; + +// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 1 class in the +// postgresql.jdbc1 package. + +import java.sql.*; +import java.util.*; +import postgresql.Field; + +/** + * This class provides information about the database as a whole. + * + *

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

Some of these methods take arguments that are String patterns. These + * arguments all have names such as fooPattern. Within a pattern String, + * "%" means match any substring of 0 or more characters, and "_" means + * match any one character. Only metadata entries matching the search + * pattern are returned. if a search pattern argument is set to a null + * ref, it means that argument's criteria should be dropped from the + * search. + * + *

A SQLException will be throws if a driver does not support a meta + * data method. In the case of methods that return a ResultSet, either + * a ResultSet (which may be empty) is returned or a SQLException is + * thrown. + * + * @see java.sql.DatabaseMetaData + */ +public class DatabaseMetaData implements java.sql.DatabaseMetaData +{ + Connection connection; // The connection association + + // These define various OID's. Hopefully they will stay constant. + static final int iVarcharOid = 1043; // OID for varchar + static final int iBoolOid = 16; // OID for bool + static final int iInt2Oid = 21; // OID for int2 + static final int iInt4Oid = 23; // OID for int4 + static final int VARHDRSZ = 4; // length for int4 + + // This is a default value for remarks + private static final byte defaultRemarks[]="no remarks".getBytes(); + + public DatabaseMetaData(Connection conn) + { + this.connection = conn; + } + + /** + * Can all the procedures returned by getProcedures be called + * by the current user? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean allProceduresAreCallable() throws SQLException + { + return true; // For now... + } + + /** + * Can all the tables returned by getTable be SELECTed by + * the current user? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean allTablesAreSelectable() throws SQLException + { + return true; // For now... + } + + /** + * What is the URL for this database? + * + * @return the url or null if it cannott be generated + * @exception SQLException if a database access error occurs + */ + public String getURL() throws SQLException + { + return connection.getURL(); + } + + /** + * What is our user name as known to the database? + * + * @return our database user name + * @exception SQLException if a database access error occurs + */ + public String getUserName() throws SQLException + { + return connection.getUserName(); + } + + /** + * Is the database in read-only mode? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly() throws SQLException + { + return connection.isReadOnly(); + } + + /** + * Are NULL values sorted high? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedHigh() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted low? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedLow() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted at the start regardless of sort order? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedAtStart() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted at the end regardless of sort order? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedAtEnd() throws SQLException + { + return true; + } + + /** + * What is the name of this database product - we hope that it is + * PostgreSQL, so we return that explicitly. + * + * @return the database product name + * @exception SQLException if a database access error occurs + */ + public String getDatabaseProductName() throws SQLException + { + return new String("PostgreSQL"); + } + + /** + * What is the version of this database product. + * + *

Note that PostgreSQL 6.3 has a system catalog called pg_version - + * however, select * from pg_version on any database retrieves + * no rows. + * + *

For now, we will return the version 6.3 (in the hope that we change + * this driver as often as we change the database) + * + * @return the database version + * @exception SQLException if a database access error occurs + */ + public String getDatabaseProductVersion() throws SQLException + { + return ("6.4"); + } + + /** + * What is the name of this JDBC driver? If we don't know this + * we are doing something wrong! + * + * @return the JDBC driver name + * @exception SQLException why? + */ + public String getDriverName() throws SQLException + { + return new String("PostgreSQL Native Driver"); + } + + /** + * What is the version string of this JDBC driver? Again, this is + * static. + * + * @return the JDBC driver name. + * @exception SQLException why? + */ + public String getDriverVersion() throws SQLException + { + return new String(Integer.toString(connection.this_driver.getMajorVersion())+"."+Integer.toString(connection.this_driver.getMinorVersion())); + } + + /** + * What is this JDBC driver's major version number? + * + * @return the JDBC driver major version + */ + public int getDriverMajorVersion() + { + return connection.this_driver.getMajorVersion(); + } + + /** + * What is this JDBC driver's minor version number? + * + * @return the JDBC driver minor version + */ + public int getDriverMinorVersion() + { + return connection.this_driver.getMinorVersion(); + } + + /** + * Does the database store tables in a local file? No - it + * stores them in a file on the server. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean usesLocalFiles() throws SQLException + { + return false; + } + + /** + * Does the database use a file for each table? Well, not really, + * since it doesnt use local files. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean usesLocalFilePerTable() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers + * as case sensitive and as a result store them in mixed case? + * A JDBC-Compliant driver will always return false. + * + *

Predicament - what do they mean by "SQL identifiers" - if it + * means the names of the tables and columns, then the answers + * given below are correct - otherwise I don't know. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMixedCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in upper case? + * + * @return true if so + */ + public boolean storesUpperCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in lower case? + * + * @return true if so + */ + public boolean storesLowerCaseIdentifiers() throws SQLException + { + return true; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in mixed case? + * + * @return true if so + */ + public boolean storesMixedCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as + * case sensitive and as a result store them in mixed case? A + * JDBC compliant driver will always return true. + * + *

Predicament - what do they mean by "SQL identifiers" - if it + * means the names of the tables and columns, then the answers + * given below are correct - otherwise I don't know. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException + { + return true; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as + * case insensitive and store them in upper case? + * + * @return true if so + */ + public boolean storesUpperCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as case + * insensitive and store them in lower case? + * + * @return true if so + */ + public boolean storesLowerCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as case + * insensitive and store them in mixed case? + * + * @return true if so + */ + public boolean storesMixedCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * What is the string used to quote SQL identifiers? This returns + * a space if identifier quoting isn't supported. A JDBC Compliant + * driver will always use a double quote character. + * + *

If an SQL identifier is a table name, column name, etc. then + * we do not support it. + * + * @return the quoting string + * @exception SQLException if a database access error occurs + */ + public String getIdentifierQuoteString() throws SQLException + { + return null; + } + + /** + * Get a comma separated list of all a database's SQL keywords that + * are NOT also SQL92 keywords. + * + *

Within PostgreSQL, the keywords are found in + * src/backend/parser/keywords.c + * + *

For SQL Keywords, I took the list provided at + * + * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt + * which is for SQL3, not SQL-92, but it is close enough for + * this purpose. + * + * @return a comma separated list of keywords we use + * @exception SQLException if a database access error occurs + */ + public String getSQLKeywords() throws SQLException + { + return new String("abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version"); + } + + public String getNumericFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getStringFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getSystemFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getTimeDateFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + /** + * This is the string that can be used to escape '_' and '%' in + * a search string pattern style catalog search parameters + * + * @return the string used to escape wildcard characters + * @exception SQLException if a database access error occurs + */ + public String getSearchStringEscape() throws SQLException + { + return new String("\\"); + } + + /** + * Get all the "extra" characters that can bew used in unquoted + * identifier names (those beyond a-zA-Z0-9 and _) + * + *

From the file src/backend/parser/scan.l, an identifier is + * {letter}{letter_or_digit} which makes it just those listed + * above. + * + * @return a string containing the extra characters + * @exception SQLException if a database access error occurs + */ + public String getExtraNameCharacters() throws SQLException + { + return new String(""); + } + + /** + * Is "ALTER TABLE" with an add column supported? + * Yes for PostgreSQL 6.1 + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsAlterTableWithAddColumn() throws SQLException + { + return true; + } + + /** + * Is "ALTER TABLE" with a drop column supported? + * Yes for PostgreSQL 6.1 + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsAlterTableWithDropColumn() throws SQLException + { + return true; + } + + /** + * Is column aliasing supported? + * + *

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

e.g. + * + *

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

+ * should return a column named as C_COUNT instead of count(C) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsColumnAliasing() throws SQLException + { + return true; + } + + /** + * Are concatenations between NULL and non-NULL values NULL? A + * JDBC Compliant driver always returns true + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullPlusNonNullIsNull() throws SQLException + { + return true; + } + + public boolean supportsConvert() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsConvert(int fromType, int toType) throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsTableCorrelationNames() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsDifferentTableCorrelationNames() throws SQLException + { + // XXX-Not Implemented + return false; + } + + /** + * Are expressions in "ORCER BY" lists supported? + * + *
e.g. select * from t order by a + b; + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsExpressionsInOrderBy() throws SQLException + { + return true; + } + + /** + * Can an "ORDER BY" clause use columns not in the SELECT? + * I checked it, and you can't. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOrderByUnrelated() throws SQLException + { + return false; + } + + /** + * Is some form of "GROUP BY" clause supported? + * I checked it, and yes it is. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupBy() throws SQLException + { + return true; + } + + /** + * Can a "GROUP BY" clause use columns not in the SELECT? + * I checked it - it seems to allow it + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupByUnrelated() throws SQLException + { + return true; + } + + /** + * Can a "GROUP BY" clause add columns not in the SELECT provided + * it specifies all the columns in the SELECT? Does anyone actually + * understand what they mean here? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupByBeyondSelect() throws SQLException + { + return true; // For now... + } + + /** + * Is the escape character in "LIKE" clauses supported? A + * JDBC compliant driver always returns true. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsLikeEscapeClause() throws SQLException + { + return true; + } + + /** + * Are multiple ResultSets from a single execute supported? + * Well, I implemented it, but I dont think this is possible from + * the back ends point of view. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMultipleResultSets() throws SQLException + { + return false; + } + + /** + * Can we have multiple transactions open at once (on different + * connections?) + * I guess we can have, since Im relying on it. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMultipleTransactions() throws SQLException + { + return true; + } + + /** + * Can columns be defined as non-nullable. A JDBC Compliant driver + * always returns true. + * + *

This changed from false to true in v6.2 of the driver, as this + * support was added to the backend. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsNonNullableColumns() throws SQLException + { + return true; + } + + /** + * Does this driver support the minimum ODBC SQL grammar. This + * grammar is defined at: + * + *

http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm + * + *

In Appendix C. From this description, we seem to support the + * ODBC minimal (Level 0) grammar. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMinimumSQLGrammar() throws SQLException + { + return true; + } + + /** + * Does this driver support the Core ODBC SQL grammar. We need + * SQL-92 conformance for this. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCoreSQLGrammar() throws SQLException + { + return false; + } + + /** + * Does this driver support the Extended (Level 2) ODBC SQL + * grammar. We don't conform to the Core (Level 1), so we can't + * conform to the Extended SQL Grammar. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsExtendedSQLGrammar() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 entry level SQL grammar? + * All JDBC Compliant drivers must return true. I think we have + * to support outer joins for this to be true. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92EntryLevelSQL() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 intermediate level SQL + * grammar? Anyone who does not support Entry level cannot support + * Intermediate level. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92IntermediateSQL() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 full SQL grammar? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92FullSQL() throws SQLException + { + return false; + } + + /** + * Is the SQL Integrity Enhancement Facility supported? + * I haven't seen this mentioned anywhere, so I guess not + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsIntegrityEnhancementFacility() throws SQLException + { + return false; + } + + /** + * Is some form of outer join supported? From my knowledge, nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOuterJoins() throws SQLException + { + return false; + } + + /** + * Are full nexted outer joins supported? Well, we dont support any + * form of outer join, so this is no as well + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsFullOuterJoins() throws SQLException + { + return false; + } + + /** + * Is there limited support for outer joins? (This will be true if + * supportFullOuterJoins is true) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsLimitedOuterJoins() throws SQLException + { + return false; + } + + /** + * What is the database vendor's preferred term for "schema" - well, + * we do not provide support for schemas, so lets just use that + * term. + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getSchemaTerm() throws SQLException + { + return new String("Schema"); + } + + /** + * What is the database vendor's preferred term for "procedure" - + * I kind of like "Procedure" myself. + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getProcedureTerm() throws SQLException + { + return new String("Procedure"); + } + + /** + * What is the database vendor's preferred term for "catalog"? - + * we dont have a preferred term, so just use Catalog + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getCatalogTerm() throws SQLException + { + return new String("Catalog"); + } + + /** + * Does a catalog appear at the start of a qualified table name? + * (Otherwise it appears at the end). + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isCatalogAtStart() throws SQLException + { + return false; + } + + /** + * What is the Catalog separator. Hmmm....well, I kind of like + * a period (so we get catalog.table definitions). - I don't think + * PostgreSQL supports catalogs anyhow, so it makes no difference. + * + * @return the catalog separator string + * @exception SQLException if a database access error occurs + */ + public String getCatalogSeparator() throws SQLException + { + // PM Sep 29 97 - changed from "." as we don't support catalogs. + return new String(""); + } + + /** + * Can a schema name be used in a data manipulation statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInDataManipulation() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in a procedure call statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInProcedureCalls() throws SQLException + { + return false; + } + + /** + * Can a schema be used in a table definition statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInTableDefinitions() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in an index definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInIndexDefinitions() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in a privilege definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a data manipulation statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInDataManipulation() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a procedure call statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInProcedureCalls() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a table definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInTableDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in an index definition? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInIndexDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a privilege definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException + { + return false; + } + + /** + * We support cursors for gets only it seems. I dont see a method + * to get a positioned delete. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsPositionedDelete() throws SQLException + { + return false; // For now... + } + + /** + * Is positioned UPDATE supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsPositionedUpdate() throws SQLException + { + return false; // For now... + } + + public boolean supportsSelectForUpdate() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsStoredProcedures() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsSubqueriesInComparisons() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsSubqueriesInExists() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsSubqueriesInIns() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsSubqueriesInQuantifieds() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsCorrelatedSubqueries() throws SQLException + { + // XXX-Not Implemented + return false; + } + + /** + * Is SQL UNION supported? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsUnion() throws SQLException + { + return false; + } + + /** + * Is SQL UNION ALL supported? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsUnionAll() throws SQLException + { + return false; + } + + /** + * In PostgreSQL, Cursors are only open within transactions. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenCursorsAcrossCommit() throws SQLException + { + return false; + } + + /** + * Do we support open cursors across multiple transactions? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenCursorsAcrossRollback() throws SQLException + { + return false; + } + + /** + * Can statements remain open across commits? They may, but + * this driver cannot guarentee that. In further reflection. + * we are talking a Statement object jere, so the answer is + * yes, since the Statement is only a vehicle to ExecSQL() + * + * @return true if they always remain open; false otherwise + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenStatementsAcrossCommit() throws SQLException + { + return true; + } + + /** + * Can statements remain open across rollbacks? They may, but + * this driver cannot guarentee that. In further contemplation, + * we are talking a Statement object here, so the answer is yes, + * since the Statement is only a vehicle to ExecSQL() in Connection + * + * @return true if they always remain open; false otherwise + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenStatementsAcrossRollback() throws SQLException + { + return true; + } + + /** + * How many hex characters can you have in an inline binary literal + * + * @return the max literal length + * @exception SQLException if a database access error occurs + */ + public int getMaxBinaryLiteralLength() throws SQLException + { + return 0; // For now... + } + + /** + * What is the maximum length for a character literal + * I suppose it is 8190 (8192 - 2 for the quotes) + * + * @return the max literal length + * @exception SQLException if a database access error occurs + */ + public int getMaxCharLiteralLength() throws SQLException + { + return 8190; + } + + /** + * Whats the limit on column name length. The description of + * pg_class would say '32' (length of pg_class.relname) - we + * should probably do a query for this....but.... + * + * @return the maximum column name length + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum number of columns in a "GROUP BY" clause? + * + * @return the max number of columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInGroupBy() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What's the maximum number of columns allowed in an index? + * 6.0 only allowed one column, but 6.1 introduced multi-column + * indices, so, theoretically, its all of them. + * + * @return max number of columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInIndex() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What's the maximum number of columns in an "ORDER BY clause? + * Theoretically, all of them! + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInOrderBy() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What is the maximum number of columns in a "SELECT" list? + * Theoretically, all of them! + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInSelect() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What is the maximum number of columns in a table? From the + * create_table(l) manual page... + * + *

"The new class is created as a heap with no initial data. A + * class can have no more than 1600 attributes (realistically, + * this is limited by the fact that tuple sizes must be less than + * 8192 bytes)..." + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInTable() throws SQLException + { + return 1600; + } + + /** + * How many active connection can we have at a time to this + * database? Well, since it depends on postmaster, which just + * does a listen() followed by an accept() and fork(), its + * basically very high. Unless the system runs out of processes, + * it can be 65535 (the number of aux. ports on a TCP/IP system). + * I will return 8192 since that is what even the largest system + * can realistically handle, + * + * @return the maximum number of connections + * @exception SQLException if a database access error occurs + */ + public int getMaxConnections() throws SQLException + { + return 8192; + } + + /** + * What is the maximum cursor name length (the same as all + * the other F***** identifiers!) + * + * @return max cursor name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxCursorNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum length of an index (in bytes)? Now, does + * the spec. mean name of an index (in which case its 32, the + * same as a table) or does it mean length of an index element + * (in which case its 8192, the size of a row) or does it mean + * the number of rows it can access (in which case it 2^32 - + * a 4 byte OID number)? I think its the length of an index + * element, personally, so Im setting it to 8192. + * + * @return max index length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxIndexLength() throws SQLException + { + return 8192; + } + + public int getMaxSchemaNameLength() throws SQLException + { + // XXX-Not Implemented + return 0; + } + + /** + * What is the maximum length of a procedure name? + * (length of pg_proc.proname used) - again, I really + * should do a query here to get it. + * + * @return the max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxProcedureNameLength() throws SQLException + { + return 32; + } + + public int getMaxCatalogNameLength() throws SQLException + { + // XXX-Not Implemented + return 0; + } + + /** + * What is the maximum length of a single row? (not including + * blobs). 8192 is defined in PostgreSQL. + * + * @return max row size in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxRowSize() throws SQLException + { + return 8192; + } + + /** + * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY + * blobs? We don't handle blobs yet + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean doesMaxRowSizeIncludeBlobs() throws SQLException + { + return false; + } + + /** + * What is the maximum length of a SQL statement? + * + * @return max length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxStatementLength() throws SQLException + { + return 8192; + } + + /** + * How many active statements can we have open at one time to + * this database? Basically, since each Statement downloads + * the results as the query is executed, we can have many. However, + * we can only really have one statement per connection going + * at once (since they are executed serially) - so we return + * one. + * + * @return the maximum + * @exception SQLException if a database access error occurs + */ + public int getMaxStatements() throws SQLException + { + return 1; + } + + /** + * What is the maximum length of a table name? This was found + * from pg_class.relname length + * + * @return max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxTableNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum number of tables that can be specified + * in a SELECT? Theoretically, this is the same number as the + * number of tables allowable. In practice tho, it is much smaller + * since the number of tables is limited by the statement, we + * return 1024 here - this is just a number I came up with (being + * the number of tables roughly of three characters each that you + * can fit inside a 8192 character buffer with comma separators). + * + * @return the maximum + * @exception SQLException if a database access error occurs + */ + public int getMaxTablesInSelect() throws SQLException + { + return 1024; + } + + /** + * What is the maximum length of a user name? Well, we generally + * use UNIX like user names in PostgreSQL, so I think this would + * be 8. However, showing the schema for pg_user shows a length + * for username of 32. + * + * @return the max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxUserNameLength() throws SQLException + { + return 32; + } + + + /** + * What is the database's default transaction isolation level? We + * do not support this, so all transactions are SERIALIZABLE. + * + * @return the default isolation level + * @exception SQLException if a database access error occurs + * @see Connection + */ + public int getDefaultTransactionIsolation() throws SQLException + { + return Connection.TRANSACTION_SERIALIZABLE; + } + + /** + * Are transactions supported? If not, commit and rollback are noops + * and the isolation level is TRANSACTION_NONE. We do support + * transactions. + * + * @return true if transactions are supported + * @exception SQLException if a database access error occurs + */ + public boolean supportsTransactions() throws SQLException + { + return true; + } + + /** + * Does the database support the given transaction isolation level? + * We only support TRANSACTION_SERIALIZABLE + * + * @param level the values are defined in java.sql.Connection + * @return true if so + * @exception SQLException if a database access error occurs + * @see Connection + */ + public boolean supportsTransactionIsolationLevel(int level) throws SQLException + { + if (level == Connection.TRANSACTION_SERIALIZABLE) + return true; + else + return false; + } + + /** + * Are both data definition and data manipulation transactions + * supported? I checked it, and could not do a CREATE TABLE + * within a transaction, so I am assuming that we don't + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException + { + return false; + } + + /** + * Are only data manipulation statements withing a transaction + * supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsDataManipulationTransactionsOnly() throws SQLException + { + return true; + } + + /** + * Does a data definition statement within a transaction force + * the transaction to commit? I think this means something like: + * + *

+   * CREATE TABLE T (A INT);
+   * INSERT INTO T (A) VALUES (2);
+   * BEGIN;
+   * UPDATE T SET A = A + 1;
+   * CREATE TABLE X (A INT);
+   * SELECT A FROM T INTO X;
+   * COMMIT;
+   * 

+ * + * does the CREATE TABLE call cause a commit? The answer is no. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean dataDefinitionCausesTransactionCommit() throws SQLException + { + return false; + } + + /** + * Is a data definition statement within a transaction ignored? + * It seems to be (from experiment in previous method) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean dataDefinitionIgnoredInTransactions() throws SQLException + { + return true; + } + + /** + * Get a description of stored procedures available in a catalog + * + *

Only procedure descriptions matching the schema and procedure + * name criteria are returned. They are ordered by PROCEDURE_SCHEM + * and PROCEDURE_NAME + * + *

Each procedure description has the following columns: + *

    + *
  1. PROCEDURE_CAT String => procedure catalog (may be null) + *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) + *
  3. PROCEDURE_NAME String => procedure name + *
  4. Field 4 reserved (make it null) + *
  5. Field 5 reserved (make it null) + *
  6. Field 6 reserved (make it null) + *
  7. REMARKS String => explanatory comment on the procedure + *
  8. PROCEDURE_TYPE short => kind of procedure + *
      + *
    • procedureResultUnknown - May return a result + *
    • procedureNoResult - Does not return a result + *
    • procedureReturnsResult - Returns a result + *
    + *
+ * + * @param catalog - a catalog name; "" retrieves those without a + * catalog; null means drop catalog name from criteria + * @param schemaParrern - a schema name pattern; "" retrieves those + * without a schema - we ignore this parameter + * @param procedureNamePattern - a procedure name pattern + * @return ResultSet - each row is a procedure description + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException + { + // the field descriptors for the new ResultSet + Field f[] = new Field[8]; + java.sql.ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + byte remarks[] = defaultRemarks; + + f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, 32); + f[3] = f[4] = f[5] = null; // reserved, must be null for now + f[6] = new Field(connection, "REMARKS", iVarcharOid, 8192); + f[7] = new Field(connection, "PROCEDURE_TYPE", iInt2Oid, 2); + + // If the pattern is null, then set it to the default + if(procedureNamePattern==null) + procedureNamePattern="%"; + + r = connection.ExecSQL("select proname, proretset from pg_proc where proname like '"+procedureNamePattern.toLowerCase()+"' order by proname"); + + while (r.next()) + { + byte[][] tuple = new byte[8][0]; + + tuple[0] = null; // Catalog name + tuple[1] = null; // Schema name + tuple[2] = r.getBytes(1); // Procedure name + tuple[3] = tuple[4] = tuple[5] = null; // Reserved + tuple[6] = remarks; // Remarks + + if (r.getBoolean(2)) + tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureReturnsResult).getBytes(); + else + tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureNoResult).getBytes(); + + v.addElement(tuple); + } + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of a catalog's stored procedure parameters + * and result columns. + * + *

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

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

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

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

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

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

The valid values for the types parameter are: + * "TABLE", "INDEX", "LARGE OBJECT", "SEQUENCE", "SYSTEM TABLE" and + * "SYSTEM INDEX" + * + * @param catalog a catalog name; For postgresql, this is ignored, and + * should be set to null + * @param schemaPattern a schema name pattern; For postgresql, this is ignored, and + * should be set to null + * @param tableNamePattern a table name pattern. For all tables this should be "%" + * @param types a list of table types to include; null returns + * all types + * @return each row is a table description + * @exception SQLException if a database-access error occurs. + */ + public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException + { + // Handle default value for types + if(types==null) + types = defaultTableTypes; + + if(tableNamePattern==null) + tableNamePattern="%"; + + // the field descriptors for the new ResultSet + Field f[] = new Field[5]; + java.sql.ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, new String("TABLE_CAT"), iVarcharOid, 32); + f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32); + f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32); + f[3] = new Field(connection, new String("TABLE_TYPE"), iVarcharOid, 32); + f[4] = new Field(connection, new String("REMARKS"), iVarcharOid, 32); + + // Now form the query + StringBuffer sql = new StringBuffer("select relname,oid from pg_class where ("); + boolean notFirst=false; + for(int i=0;i + // Now take the pattern into account + sql.append(") and relname like '"); + sql.append(tableNamePattern.toLowerCase()); + sql.append("'"); + + // Now run the query + r = connection.ExecSQL(sql.toString()); + + byte remarks[]; + + while (r.next()) + { + byte[][] tuple = new byte[5][0]; + + // Fetch the description for the table (if any) + java.sql.ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(2)); + if(((postgresql.ResultSet)dr).getTupleCount()==1) { + dr.next(); + remarks = dr.getBytes(1); + } else + remarks = defaultRemarks; + dr.close(); + + tuple[0] = null; // Catalog name + tuple[1] = null; // Schema name + tuple[2] = r.getBytes(1); // Table name + tuple[3] = null; // Table type + tuple[4] = remarks; // Remarks + v.addElement(tuple); + } + r.close(); + return new ResultSet(connection, f, v, "OK", 1); + } + + // This array contains the valid values for the types argument + // in getTables(). + // + // Each supported type consists of it's name, and the sql where + // clause to retrieve that value. + // + // IMPORTANT: the query must be enclosed in ( ) + private static final String getTableTypes[][] = { + {"TABLE", "(relkind='r' and relname !~ '^pg_' and relname !~ '^xinv')"}, + {"INDEX", "(relkind='i' and relname !~ '^pg_' and relname !~ '^xinx')"}, + {"LARGE OBJECT", "(relkind='r' and relname ~ '^xinv')"}, + {"SEQUENCE", "(relkind='S' and relname !~ '^pg_')"}, + {"SYSTEM TABLE", "(relkind='r' and relname ~ '^pg_')"}, + {"SYSTEM INDEX", "(relkind='i' and relname ~ '^pg_')"} + }; + + // These are the default tables, used when NULL is passed to getTables + // The choice of these provide the same behaviour as psql's \d + private static final String defaultTableTypes[] = { + "TABLE","INDEX","SEQUENCE" + }; + + /** + * Get the schema names available in this database. The results + * are ordered by schema name. + * + *

The schema column is: + *

    + *
  1. TABLE_SCHEM String => schema name + *
+ * + * @return ResultSet each row has a single String column that is a + * schema name + */ + public java.sql.ResultSet getSchemas() throws SQLException + { + // We don't use schemas, so we simply return a single schema name "". + // + Field f[] = new Field[1]; + Vector v = new Vector(); + byte[][] tuple = new byte[1][0]; + f[0] = new Field(connection,new String("TABLE_SCHEM"),iVarcharOid,32); + tuple[0] = "".getBytes(); + v.addElement(tuple); + return new ResultSet(connection,f,v,"OK",1); + } + + /** + * Get the catalog names available in this database. The results + * are ordered by catalog name. + * + *

The catalog column is: + *

    + *
  1. TABLE_CAT String => catalog name + *
+ * + * @return ResultSet each row has a single String column that is a + * catalog name + */ + public java.sql.ResultSet getCatalogs() throws SQLException + { + // We don't use catalogs, so we simply return a single catalog name "". + Field f[] = new Field[1]; + Vector v = new Vector(); + byte[][] tuple = new byte[1][0]; + f[0] = new Field(connection,new String("TABLE_CAT"),iVarcharOid,32); + tuple[0] = "".getBytes(); + v.addElement(tuple); + return new ResultSet(connection,f,v,"OK",1); + } + + /** + * Get the table types available in this database. The results + * are ordered by table type. + * + *

The table type is: + *

    + *
  1. TABLE_TYPE String => table type. Typical types are "TABLE", + * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", + * "LOCAL TEMPORARY", "ALIAS", "SYNONYM". + *
+ * + * @return ResultSet each row has a single String column that is a + * table type + */ + public java.sql.ResultSet getTableTypes() throws SQLException + { + Field f[] = new Field[1]; + Vector v = new Vector(); + byte[][] tuple = new byte[1][0]; + f[0] = new Field(connection,new String("TABLE_TYPE"),iVarcharOid,32); + for(int i=0;iOnly column descriptions matching the catalog, schema, table + * and column name criteria are returned. They are ordered by + * TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION. + * + *

Each column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. DATA_TYPE short => SQL type from java.sql.Types + *
  6. TYPE_NAME String => Data source dependent type name + *
  7. COLUMN_SIZE int => column size. For char or date + * types this is the maximum number of characters, for numeric or + * decimal types this is precision. + *
  8. BUFFER_LENGTH is not used. + *
  9. DECIMAL_DIGITS int => the number of fractional digits + *
  10. NUM_PREC_RADIX int => Radix (typically either 10 or 2) + *
  11. NULLABLE int => is NULL allowed? + *
      + *
    • columnNoNulls - might not allow NULL values + *
    • columnNullable - definitely allows NULL values + *
    • columnNullableUnknown - nullability unknown + *
    + *
  12. REMARKS String => comment describing column (may be null) + *
  13. COLUMN_DEF String => default value (may be null) + *
  14. SQL_DATA_TYPE int => unused + *
  15. SQL_DATETIME_SUB int => unused + *
  16. CHAR_OCTET_LENGTH int => for char types the + * maximum number of bytes in the column + *
  17. ORDINAL_POSITION int => index of column in table + * (starting at 1) + *
  18. IS_NULLABLE String => "NO" means column definitely + * does not allow NULL values; "YES" means the column might + * allow NULL values. An empty string means nobody knows. + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schemaPattern a schema name pattern; "" retrieves those + * without a schema + * @param tableNamePattern a table name pattern + * @param columnNamePattern a column name pattern + * @return ResultSet each row is a column description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException + { + // the field descriptors for the new ResultSet + Field f[] = new Field[18]; + java.sql.ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, new String("TABLE_CAT"), iVarcharOid, 32); + f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32); + f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32); + f[3] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32); + f[4] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2); + f[5] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32); + f[6] = new Field(connection, new String("COLUMN_SIZE"), iInt4Oid, 4); + f[7] = new Field(connection, new String("BUFFER_LENGTH"), iVarcharOid, 32); + f[8] = new Field(connection, new String("DECIMAL_DIGITS"), iInt4Oid, 4); + f[9] = new Field(connection, new String("NUM_PREC_RADIX"), iInt4Oid, 4); + f[10] = new Field(connection, new String("NULLABLE"), iInt4Oid, 4); + f[11] = new Field(connection, new String("REMARKS"), iVarcharOid, 32); + f[12] = new Field(connection, new String("COLUMN_DEF"), iVarcharOid, 32); + f[13] = new Field(connection, new String("SQL_DATA_TYPE"), iInt4Oid, 4); + f[14] = new Field(connection, new String("SQL_DATETIME_SUB"), iInt4Oid, 4); + f[15] = new Field(connection, new String("CHAR_OCTET_LENGTH"), iVarcharOid, 32); + f[16] = new Field(connection, new String("ORDINAL_POSITION"), iInt4Oid,4); + f[17] = new Field(connection, new String("IS_NULLABLE"), iVarcharOid, 32); + + // Added by Stefan Andreasen + // If the pattern are null then set them to % + if (tableNamePattern == null) tableNamePattern="%"; + if (columnNamePattern == null) columnNamePattern="%"; + + // Now form the query + // Modified by Stefan Andreasen + r = connection.ExecSQL("select a.oid,c.relname,a.attname,a.atttypid,a.attnum,a.attnotnull,a.attlen,a.atttypmod from pg_class c, pg_attribute a where a.attrelid=c.oid and c.relname like '"+tableNamePattern.toLowerCase()+"' and a.attname like '"+columnNamePattern.toLowerCase()+"' and a.attnum>0 order by c.relname,a.attnum"); + + byte remarks[]; + + while(r.next()) { + byte[][] tuple = new byte[18][0]; + + // Fetch the description for the table (if any) + java.sql.ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(1)); + if(((postgresql.ResultSet)dr).getTupleCount()==1) { + dr.next(); + tuple[11] = dr.getBytes(1); + } else + tuple[11] = defaultRemarks; + + dr.close(); + + tuple[0] = "".getBytes(); // Catalog name + tuple[1] = "".getBytes(); // Schema name + tuple[2] = r.getBytes(2); // Table name + tuple[3] = r.getBytes(3); // Column name + + dr = connection.ExecSQL("select typname from pg_type where oid = "+r.getString(4)); + dr.next(); + String typname=dr.getString(1); + dr.close(); + tuple[4] = Integer.toString(Field.getSQLType(typname)).getBytes(); // Data type + tuple[5] = typname.getBytes(); // Type name + + // Column size + // Looking at the psql source, + // I think the length of a varchar as specified when the table was created + // should be extracted from atttypmod which contains this length + sizeof(int32) + if (typname.equals("bpchar") || typname.equals("varchar")) { + int atttypmod = r.getInt(8); + tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes(); + } else + tuple[6] = r.getBytes(7); + + tuple[7] = null; // Buffer length + + tuple[8] = "0".getBytes(); // Decimal Digits - how to get this? + tuple[9] = "10".getBytes(); // Num Prec Radix - assume decimal + + // tuple[10] is below + // tuple[11] is above + + tuple[12] = null; // column default + + tuple[13] = null; // sql data type (unused) + tuple[14] = null; // sql datetime sub (unused) + + tuple[15] = tuple[6]; // char octet length + + tuple[16] = r.getBytes(5); // ordinal position + + String nullFlag = r.getString(6); + tuple[10] = Integer.toString(nullFlag.equals("f")?java.sql.DatabaseMetaData.columnNullable:java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable + tuple[17] = (nullFlag.equals("f")?"YES":"NO").getBytes(); // is nullable + + v.addElement(tuple); + } + r.close(); + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of the access rights for a table's columns. + * + *

Only privileges matching the column name criteria are + * returned. They are ordered by COLUMN_NAME and PRIVILEGE. + * + *

Each privilige description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. GRANTOR => grantor of access (may be null) + *
  6. GRANTEE String => grantee of access + *
  7. PRIVILEGE String => name of access (SELECT, + * INSERT, UPDATE, REFRENCES, ...) + *
  8. IS_GRANTABLE String => "YES" if grantee is permitted + * to grant to others; "NO" if not; null if unknown + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @param columnNamePattern a column name pattern + * @return ResultSet each row is a column privilege description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException + { + Field f[] = new Field[8]; + Vector v = new Vector(); + + if(table==null) + table="%"; + + if(columnNamePattern==null) + columnNamePattern="%"; + else + columnNamePattern=columnNamePattern.toLowerCase(); + + f[0] = new Field(connection,new String("TABLE_CAT"),iVarcharOid,32); + f[1] = new Field(connection,new String("TABLE_SCHEM"),iVarcharOid,32); + f[2] = new Field(connection,new String("TABLE_NAME"),iVarcharOid,32); + f[3] = new Field(connection,new String("COLUMN_NAME"),iVarcharOid,32); + f[4] = new Field(connection,new String("GRANTOR"),iVarcharOid,32); + f[5] = new Field(connection,new String("GRANTEE"),iVarcharOid,32); + f[6] = new Field(connection,new String("PRIVILEGE"),iVarcharOid,32); + f[7] = new Field(connection,new String("IS_GRANTABLE"),iVarcharOid,32); + + // This is taken direct from the psql source + java.sql.ResultSet r = connection.ExecSQL("SELECT relname, relacl FROM pg_class, pg_user WHERE ( relkind = 'r' OR relkind = 'i') and relname !~ '^pg_' and relname !~ '^xin[vx][0-9]+' and usesysid = relowner and relname like '"+table.toLowerCase()+"' ORDER BY relname"); + while(r.next()) { + byte[][] tuple = new byte[8][0]; + tuple[0] = tuple[1]= "".getBytes(); + DriverManager.println("relname=\""+r.getString(1)+"\" relacl=\""+r.getString(2)+"\""); + + // For now, don't add to the result as relacl needs to be processed. + //v.addElement(tuple); + } + + return new ResultSet(connection,f,v,"OK",1); + } + + /** + * Get a description of the access rights for each table available + * in a catalog. + * + *

Only privileges matching the schema and table name + * criteria are returned. They are ordered by TABLE_SCHEM, + * TABLE_NAME, and PRIVILEGE. + * + *

Each privilige description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. GRANTOR => grantor of access (may be null) + *
  6. GRANTEE String => grantee of access + *
  7. PRIVILEGE String => name of access (SELECT, + * INSERT, UPDATE, REFRENCES, ...) + *
  8. IS_GRANTABLE String => "YES" if grantee is permitted + * to grant to others; "NO" if not; null if unknown + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schemaPattern a schema name pattern; "" retrieves those + * without a schema + * @param tableNamePattern a table name pattern + * @return ResultSet each row is a table privilege description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of a table's optimal set of columns that + * uniquely identifies a row. They are ordered by SCOPE. + * + *

Each column description has the following columns: + *

    + *
  1. SCOPE short => actual scope of result + *
      + *
    • bestRowTemporary - very temporary, while using row + *
    • bestRowTransaction - valid for remainder of current transaction + *
    • bestRowSession - valid for remainder of current session + *
    + *
  2. COLUMN_NAME String => column name + *
  3. DATA_TYPE short => SQL data type from java.sql.Types + *
  4. TYPE_NAME String => Data source dependent type name + *
  5. COLUMN_SIZE int => precision + *
  6. BUFFER_LENGTH int => not used + *
  7. DECIMAL_DIGITS short => scale + *
  8. PSEUDO_COLUMN short => is this a pseudo column + * like an Oracle ROWID + *
      + *
    • bestRowUnknown - may or may not be pseudo column + *
    • bestRowNotPseudo - is NOT a pseudo column + *
    • bestRowPseudo - is a pseudo column + *
    + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @param scope the scope of interest; use same values as SCOPE + * @param nullable include columns that are nullable? + * @return ResultSet each row is a column description + */ + // Implementation note: This is required for Borland's JBuilder to work + public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException + { + // for now, this returns an empty result set. + Field f[] = new Field[8]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, new String("SCOPE"), iInt2Oid, 2); + f[1] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32); + f[2] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2); + f[3] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32); + f[4] = new Field(connection, new String("COLUMN_SIZE"), iInt4Oid, 4); + f[5] = new Field(connection, new String("BUFFER_LENGTH"), iInt4Oid, 4); + f[6] = new Field(connection, new String("DECIMAL_DIGITS"), iInt2Oid, 2); + f[7] = new Field(connection, new String("PSEUDO_COLUMN"), iInt2Oid, 2); + + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of a table's columns that are automatically + * updated when any value in a row is updated. They are + * unordered. + * + *

Each column description has the following columns: + *

    + *
  1. SCOPE short => is not used + *
  2. COLUMN_NAME String => column name + *
  3. DATA_TYPE short => SQL data type from java.sql.Types + *
  4. TYPE_NAME String => Data source dependent type name + *
  5. COLUMN_SIZE int => precision + *
  6. BUFFER_LENGTH int => length of column value in bytes + *
  7. DECIMAL_DIGITS short => scale + *
  8. PSEUDO_COLUMN short => is this a pseudo column + * like an Oracle ROWID + *
      + *
    • versionColumnUnknown - may or may not be pseudo column + *
    • versionColumnNotPseudo - is NOT a pseudo column + *
    • versionColumnPseudo - is a pseudo column + *
    + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @return ResultSet each row is a column description + */ + public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of a table's primary key columns. They + * are ordered by COLUMN_NAME. + * + *

Each column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. KEY_SEQ short => sequence number within primary key + *
  6. PK_NAME String => primary key name (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a primary key column description + */ + public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException + { + return connection.createStatement().executeQuery("SELECT " + + "'' as TABLE_CAT," + + "'' AS TABLE_SCHEM," + + "bc.relname AS TABLE_NAME," + + "ic.relname AS COLUMN_NAME," + + "'1' as KEY_SEQ,"+ // -- fake it as a String for now + "t.typname as PK_NAME " + + " FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a, pg_type t " + + " WHERE bc.relkind = 'r' " + // -- not indices + " and bc.relname ~ '"+table+"'" + + " and i.indrelid = bc.oid" + + " and i.indexrelid = ic.oid" + + " and i.indkey[0] = a.attnum" + + " and i.indproc = '0'::oid" + + " and a.attrelid = bc.oid" + + " ORDER BY TABLE_NAME, COLUMN_NAME;" + ); + } + + /** + * Get a description of the primary key columns that are + * referenced by a table's foreign key columns (the primary keys + * imported by a table). They are ordered by PKTABLE_CAT, + * PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. + * + *

Each primary key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog + * being imported (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema + * being imported (may be null) + *
  3. PKTABLE_NAME String => primary key table name + * being imported + *
  4. PKCOLUMN_NAME String => primary key column name + * being imported + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + *
  8. FKCOLUMN_NAME String => foreign key column name + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key name (may be null) + *
  13. PK_NAME String => primary key name (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a primary key column description + * @see #getExportedKeys + */ + public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of a foreign key columns that reference a + * table's primary key columns (the foreign keys exported by a + * table). They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, + * FKTABLE_NAME, and KEY_SEQ. + * + *

Each foreign key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) + *
  3. PKTABLE_NAME String => primary key table name + *
  4. PKCOLUMN_NAME String => primary key column name + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + * being exported (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + * being exported (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + * being exported + *
  8. FKCOLUMN_NAME String => foreign key column name + * being exported + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key identifier (may be null) + *
  13. PK_NAME String => primary key identifier (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a foreign key column description + * @see #getImportedKeys + */ + public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of the foreign key columns in the foreign key + * table that reference the primary key columns of the primary key + * table (describe how one table imports another's key.) This + * should normally return a single foreign key/primary key pair + * (most tables only import a foreign key from a table once.) They + * are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and + * KEY_SEQ. + * + *

Each foreign key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) + *
  3. PKTABLE_NAME String => primary key table name + *
  4. PKCOLUMN_NAME String => primary key column name + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + * being exported (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + * being exported (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + * being exported + *
  8. FKCOLUMN_NAME String => foreign key column name + * being exported + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key identifier (may be null) + *
  13. PK_NAME String => primary key identifier (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a foreign key column description + * @see #getImportedKeys + */ + public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of all the standard SQL types supported by + * this database. They are ordered by DATA_TYPE and then by how + * closely the data type maps to the corresponding JDBC SQL type. + * + *

Each type description has the following columns: + *

    + *
  1. TYPE_NAME String => Type name + *
  2. DATA_TYPE short => SQL data type from java.sql.Types + *
  3. PRECISION int => maximum precision + *
  4. LITERAL_PREFIX String => prefix used to quote a literal + * (may be null) + *
  5. LITERAL_SUFFIX String => suffix used to quote a literal + (may be null) + *
  6. CREATE_PARAMS String => parameters used in creating + * the type (may be null) + *
  7. NULLABLE short => can you use NULL for this type? + *
      + *
    • typeNoNulls - does not allow NULL values + *
    • typeNullable - allows NULL values + *
    • typeNullableUnknown - nullability unknown + *
    + *
  8. CASE_SENSITIVE boolean=> is it case sensitive? + *
  9. SEARCHABLE short => can you use "WHERE" based on this type: + *
      + *
    • typePredNone - No support + *
    • typePredChar - Only supported with WHERE .. LIKE + *
    • typePredBasic - Supported except for WHERE .. LIKE + *
    • typeSearchable - Supported for all WHERE .. + *
    + *
  10. UNSIGNED_ATTRIBUTE boolean => is it unsigned? + *
  11. FIXED_PREC_SCALE boolean => can it be a money value? + *
  12. AUTO_INCREMENT boolean => can it be used for an + * auto-increment value? + *
  13. LOCAL_TYPE_NAME String => localized version of type name + * (may be null) + *
  14. MINIMUM_SCALE short => minimum scale supported + *
  15. MAXIMUM_SCALE short => maximum scale supported + *
  16. SQL_DATA_TYPE int => unused + *
  17. SQL_DATETIME_SUB int => unused + *
  18. NUM_PREC_RADIX int => usually 2 or 10 + *
+ * + * @return ResultSet each row is a SQL type description + */ + public java.sql.ResultSet getTypeInfo() throws SQLException + { + java.sql.ResultSet rs = connection.ExecSQL("select typname from pg_type"); + if(rs!=null) { + Field f[] = new Field[18]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32); + f[1] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2); + f[2] = new Field(connection, new String("PRECISION"), iInt4Oid, 4); + f[3] = new Field(connection, new String("LITERAL_PREFIX"), iVarcharOid, 32); + f[4] = new Field(connection, new String("LITERAL_SUFFIX"), iVarcharOid, 32); + f[5] = new Field(connection, new String("CREATE_PARAMS"), iVarcharOid, 32); + f[6] = new Field(connection, new String("NULLABLE"), iInt2Oid, 2); + f[7] = new Field(connection, new String("CASE_SENSITIVE"), iBoolOid, 1); + f[8] = new Field(connection, new String("SEARCHABLE"), iInt2Oid, 2); + f[9] = new Field(connection, new String("UNSIGNED_ATTRIBUTE"), iBoolOid, 1); + f[10] = new Field(connection, new String("FIXED_PREC_SCALE"), iBoolOid, 1); + f[11] = new Field(connection, new String("AUTO_INCREMENT"), iBoolOid, 1); + f[12] = new Field(connection, new String("LOCAL_TYPE_NAME"), iVarcharOid, 32); + f[13] = new Field(connection, new String("MINIMUM_SCALE"), iInt2Oid, 2); + f[14] = new Field(connection, new String("MAXIMUM_SCALE"), iInt2Oid, 2); + f[15] = new Field(connection, new String("SQL_DATA_TYPE"), iInt4Oid, 4); + f[16] = new Field(connection, new String("SQL_DATETIME_SUB"), iInt4Oid, 4); + f[17] = new Field(connection, new String("NUM_PREC_RADIX"), iInt4Oid, 4); + + // cache some results, this will keep memory useage down, and speed + // things up a little. + byte b9[] = "9".getBytes(); + byte b10[] = "10".getBytes(); + byte bf[] = "f".getBytes(); + byte bnn[] = Integer.toString(typeNoNulls).getBytes(); + byte bts[] = Integer.toString(typeSearchable).getBytes(); + + while(rs.next()) { + byte[][] tuple = new byte[18][]; + String typname=rs.getString(1); + tuple[0] = typname.getBytes(); + tuple[1] = Integer.toString(Field.getSQLType(typname)).getBytes(); + tuple[2] = b9; // for now + tuple[6] = bnn; // for now + tuple[7] = bf; // false for now - not case sensitive + tuple[8] = bts; + tuple[9] = bf; // false for now - it's signed + tuple[10] = bf; // false for now - must handle money + tuple[11] = bf; // false for now - handle autoincrement + // 12 - LOCAL_TYPE_NAME is null + // 13 & 14 ? + // 15 & 16 are unused so we return null + tuple[17] = b10; // everything is base 10 + v.addElement(tuple); + } + rs.close(); + return new ResultSet(connection, f, v, "OK", 1); + } + + return null; + } + + /** + * Get a description of a table's indices and statistics. They are + * ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. + * + *

Each index column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. NON_UNIQUE boolean => Can index values be non-unique? + * false when TYPE is tableIndexStatistic + *
  5. INDEX_QUALIFIER String => index catalog (may be null); + * null when TYPE is tableIndexStatistic + *
  6. INDEX_NAME String => index name; null when TYPE is + * tableIndexStatistic + *
  7. TYPE short => index type: + *
      + *
    • tableIndexStatistic - this identifies table statistics that are + * returned in conjuction with a table's index descriptions + *
    • tableIndexClustered - this is a clustered index + *
    • tableIndexHashed - this is a hashed index + *
    • tableIndexOther - this is some other style of index + *
    + *
  8. ORDINAL_POSITION short => column sequence number + * within index; zero when TYPE is tableIndexStatistic + *
  9. COLUMN_NAME String => column name; null when TYPE is + * tableIndexStatistic + *
  10. ASC_OR_DESC String => column sort sequence, "A" => ascending + * "D" => descending, may be null if sort sequence is not supported; + * null when TYPE is tableIndexStatistic + *
  11. CARDINALITY int => When TYPE is tableIndexStatisic then + * this is the number of rows in the table; otherwise it is the + * number of unique values in the index. + *
  12. PAGES int => When TYPE is tableIndexStatisic then + * this is the number of pages used for the table, otherwise it + * is the number of pages used for the current index. + *
  13. FILTER_CONDITION String => Filter condition, if any. + * (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those without a schema + * @param table a table name + * @param unique when true, return only indices for unique values; + * when false, return indices regardless of whether unique or not + * @param approximate when true, result is allowed to reflect approximate + * or out of data values; when false, results are requested to be + * accurate + * @return ResultSet each row is an index column description + */ + // Implementation note: This is required for Borland's JBuilder to work + public java.sql.ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException + { + // for now, this returns an empty result set. + Field f[] = new Field[13]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, new String("TABLE_CAT"), iVarcharOid, 32); + f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32); + f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32); + f[3] = new Field(connection, new String("NON_UNIQUE"), iBoolOid, 1); + f[4] = new Field(connection, new String("INDEX_QUALIFIER"), iVarcharOid, 32); + f[5] = new Field(connection, new String("INDEX_NAME"), iVarcharOid, 32); + f[6] = new Field(connection, new String("TYPE"), iInt2Oid, 2); + f[7] = new Field(connection, new String("ORDINAL_POSITION"), iInt2Oid, 2); + f[8] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32); + f[9] = new Field(connection, new String("ASC_OR_DESC"), iVarcharOid, 32); + f[10] = new Field(connection, new String("CARDINALITY"), iInt4Oid, 4); + f[11] = new Field(connection, new String("PAGES"), iInt4Oid, 4); + f[12] = new Field(connection, new String("FILTER_CONDITION"), iVarcharOid, 32); + + return new ResultSet(connection, f, v, "OK", 1); + } + + // ** JDBC 2 Extensions ** + + public boolean deletesAreDetected(int i) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean othersDeletesAreVisible(int i) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Class getClass(String catalog, + String schema, + String table, + String columnNamePattern + ) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public java.sql.Connection getConnection() throws SQLException + { + return (java.sql.Connection)connection; + } + + public java.sql.ResultSet getUDTs(String catalog, + String schemaPattern, + String typeNamePattern, + int[] types + ) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean othersInsertsAreVisible(int type) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean updatesAreDetected(int type) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean othersUpdatesAreVisible(int type) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean ownUpdatesAreVisible(int type) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean ownInsertsAreVisible(int type) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean insertsAreDetected(int type) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean ownDeletesAreVisible(int type) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean rowChangesAreDetected(int type) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean rowChangesAreVisible(int type) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean supportsBatchUpdates() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean supportsResultSetConcurrency(int type,int concurrency) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean supportsResultSetType(int type) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + +} + diff --git a/src/interfaces/jdbc/postgresql/jdbc2/PreparedStatement.java b/src/interfaces/jdbc/postgresql/jdbc2/PreparedStatement.java new file mode 100644 index 0000000000..7a835a8193 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc2/PreparedStatement.java @@ -0,0 +1,661 @@ +package postgresql.jdbc2; + +// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 1 class in the +// postgresql.jdbc1 package. + +import java.io.*; +import java.math.*; +import java.sql.*; +import java.text.*; +import java.util.*; +import postgresql.largeobject.*; +import postgresql.util.*; + +/** + * A SQL Statement is pre-compiled and stored in a PreparedStatement object. + * This object can then be used to efficiently execute this statement multiple + * times. + * + *

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

If arbitrary parameter type conversions are required, then the setObject + * method should be used with a target SQL type. + * + * @see ResultSet + * @see java.sql.PreparedStatement + */ +public class PreparedStatement extends Statement implements java.sql.PreparedStatement +{ + String sql; + String[] templateStrings; + String[] inStrings; + Connection connection; + + /** + * Constructor for the PreparedStatement class. + * Split the SQL statement into segments - separated by the arguments. + * When we rebuild the thing with the arguments, we can substitute the + * args and join the whole thing together. + * + * @param conn the instanatiating connection + * @param sql the SQL statement with ? for IN markers + * @exception SQLException if something bad occurs + */ + public PreparedStatement(Connection connection, String sql) throws SQLException + { + super(connection); + + Vector v = new Vector(); + boolean inQuotes = false; + int lastParmEnd = 0, i; + + this.sql = sql; + this.connection = connection; + for (i = 0; i < sql.length(); ++i) + { + int c = sql.charAt(i); + + if (c == '\'') + inQuotes = !inQuotes; + if (c == '?' && !inQuotes) + { + v.addElement(sql.substring (lastParmEnd, i)); + lastParmEnd = i + 1; + } + } + v.addElement(sql.substring (lastParmEnd, sql.length())); + + templateStrings = new String[v.size()]; + inStrings = new String[v.size() - 1]; + clearParameters(); + + for (i = 0 ; i < templateStrings.length; ++i) + templateStrings[i] = (String)v.elementAt(i); + } + + /** + * A Prepared SQL query is executed and its ResultSet is returned + * + * @return a ResultSet that contains the data produced by the + * query - never null + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSet executeQuery() throws SQLException + { + StringBuffer s = new StringBuffer(); + int i; + + for (i = 0 ; i < inStrings.length ; ++i) + { + if (inStrings[i] == null) + throw new SQLException("No value specified for parameter " + (i + 1)); + s.append (templateStrings[i]); + s.append (inStrings[i]); + } + s.append(templateStrings[inStrings.length]); + return super.executeQuery(s.toString()); // in Statement class + } + + /** + * Execute a SQL INSERT, UPDATE or DELETE statement. In addition, + * SQL statements that return nothing such as SQL DDL statements can + * be executed. + * + * @return either the row count for INSERT, UPDATE or DELETE; or + * 0 for SQL statements that return nothing. + * @exception SQLException if a database access error occurs + */ + public int executeUpdate() throws SQLException + { + StringBuffer s = new StringBuffer(); + int i; + + for (i = 0 ; i < inStrings.length ; ++i) + { + if (inStrings[i] == null) + throw new SQLException("No value specified for parameter " + (i + 1)); + s.append (templateStrings[i]); + s.append (inStrings[i]); + } + s.append(templateStrings[inStrings.length]); + return super.executeUpdate(s.toString()); // in Statement class + } + + /** + * Set a parameter to SQL NULL + * + *

Note: You must specify the parameters SQL type (although + * PostgreSQL ignores it) + * + * @param parameterIndex the first parameter is 1, etc... + * @param sqlType the SQL type code defined in java.sql.Types + * @exception SQLException if a database access error occurs + */ + public void setNull(int parameterIndex, int sqlType) throws SQLException + { + set(parameterIndex, "null"); + } + + /** + * Set a parameter to a Java boolean value. The driver converts this + * to a SQL BIT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBoolean(int parameterIndex, boolean x) throws SQLException + { + set(parameterIndex, x ? "'t'" : "'f'"); + } + + /** + * Set a parameter to a Java byte value. The driver converts this to + * a SQL TINYINT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setByte(int parameterIndex, byte x) throws SQLException + { + set(parameterIndex, (new Integer(x)).toString()); + } + + /** + * Set a parameter to a Java short value. The driver converts this + * to a SQL SMALLINT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setShort(int parameterIndex, short x) throws SQLException + { + set(parameterIndex, (new Integer(x)).toString()); + } + + /** + * Set a parameter to a Java int value. The driver converts this to + * a SQL INTEGER value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setInt(int parameterIndex, int x) throws SQLException + { + set(parameterIndex, (new Integer(x)).toString()); + } + + /** + * Set a parameter to a Java long value. The driver converts this to + * a SQL BIGINT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setLong(int parameterIndex, long x) throws SQLException + { + set(parameterIndex, (new Long(x)).toString()); + } + + /** + * Set a parameter to a Java float value. The driver converts this + * to a SQL FLOAT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setFloat(int parameterIndex, float x) throws SQLException + { + set(parameterIndex, (new Float(x)).toString()); + } + + /** + * Set a parameter to a Java double value. The driver converts this + * to a SQL DOUBLE value when it sends it to the database + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setDouble(int parameterIndex, double x) throws SQLException + { + set(parameterIndex, (new Double(x)).toString()); + } + + /** + * Set a parameter to a java.lang.BigDecimal value. The driver + * converts this to a SQL NUMERIC value when it sends it to the + * database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException + { + set(parameterIndex, x.toString()); + } + + /** + * Set a parameter to a Java String value. The driver converts this + * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments + * size relative to the driver's limits on VARCHARs) when it sends it + * to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setString(int parameterIndex, String x) throws SQLException + { + // if the passed string is null, then set this column to null + if(x==null) + set(parameterIndex,"null"); + else { + StringBuffer b = new StringBuffer(); + int i; + + b.append('\''); + for (i = 0 ; i < x.length() ; ++i) + { + char c = x.charAt(i); + if (c == '\\' || c == '\'') + b.append((char)'\\'); + b.append(c); + } + b.append('\''); + set(parameterIndex, b.toString()); + } + } + + /** + * Set a parameter to a Java array of bytes. The driver converts this + * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's + * size relative to the driver's limits on VARBINARYs) when it sends + * it to the database. + * + *

Implementation note: + *
With postgresql, this creates a large object, and stores the + * objects oid in this column. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBytes(int parameterIndex, byte x[]) throws SQLException + { + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + lob.write(x); + lob.close(); + setInt(parameterIndex,oid); + } + + /** + * Set a parameter to a java.sql.Date value. The driver converts this + * to a SQL DATE value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setDate(int parameterIndex, java.sql.Date x) throws SQLException + { + SimpleDateFormat df = new SimpleDateFormat("''"+connection.getDateStyle()+"''"); + + set(parameterIndex, df.format(x)); + + // The above is how the date should be handled. + // + // However, in JDK's prior to 1.1.6 (confirmed with the + // Linux jdk1.1.3 and the Win95 JRE1.1.5), SimpleDateFormat seems + // to format a date to the previous day. So the fix is to add a day + // before formatting. + // + // PS: 86400000 is one day + // + //set(parameterIndex, df.format(new java.util.Date(x.getTime()+86400000))); + } + + /** + * Set a parameter to a java.sql.Time value. The driver converts + * this to a SQL TIME value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1...)); + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setTime(int parameterIndex, Time x) throws SQLException + { + set(parameterIndex, "'" + x.toString() + "'"); + } + + /** + * Set a parameter to a java.sql.Timestamp value. The driver converts + * this to a SQL TIMESTAMP value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException + { + set(parameterIndex, "'" + x.toString() + "'"); + } + + /** + * When a very large ASCII value is input to a LONGVARCHAR parameter, + * it may be more practical to send it via a java.io.InputStream. + * JDBC will read the data from the stream as needed, until it reaches + * end-of-file. The JDBC driver will do any necessary conversion from + * ASCII to the database char format. + * + *

Note: This stream object can either be a standard Java + * stream object or your own subclass that implements the standard + * interface. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @param length the number of bytes in the stream + * @exception SQLException if a database access error occurs + */ + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException + { + setBinaryStream(parameterIndex, x, length); + } + + /** + * When a very large Unicode value is input to a LONGVARCHAR parameter, + * it may be more practical to send it via a java.io.InputStream. + * JDBC will read the data from the stream as needed, until it reaches + * end-of-file. The JDBC driver will do any necessary conversion from + * UNICODE to the database char format. + * + * ** DEPRECIATED IN JDBC 2 ** + * + *

Note: This stream object can either be a standard Java + * stream object or your own subclass that implements the standard + * interface. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + * @deprecated + */ + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException + { + setBinaryStream(parameterIndex, x, length); + } + + /** + * When a very large binary value is input to a LONGVARBINARY parameter, + * it may be more practical to send it via a java.io.InputStream. + * JDBC will read the data from the stream as needed, until it reaches + * end-of-file. + * + *

Note: This stream object can either be a standard Java + * stream object or your own subclass that implements the standard + * interface. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException + { + throw new SQLException("InputStream as parameter not supported"); + } + + /** + * In general, parameter values remain in force for repeated used of a + * Statement. Setting a parameter value automatically clears its + * previous value. However, in coms cases, it is useful to immediately + * release the resources used by the current parameter values; this + * can be done by calling clearParameters + * + * @exception SQLException if a database access error occurs + */ + public void clearParameters() throws SQLException + { + int i; + + for (i = 0 ; i < inStrings.length ; i++) + inStrings[i] = null; + } + + /** + * Set the value of a parameter using an object; use the java.lang + * equivalent objects for integral values. + * + *

The given Java object will be converted to the targetSqlType before + * being sent to the database. + * + *

note that this method may be used to pass database-specific + * abstract data types. This is done by using a Driver-specific + * Java type and using a targetSqlType of java.sql.Types.OTHER + * + * @param parameterIndex the first parameter is 1... + * @param x the object containing the input parameter value + * @param targetSqlType The SQL type to be send to the database + * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC + * types this is the number of digits after the decimal. For + * all other types this value will be ignored. + * @exception SQLException if a database access error occurs + */ + public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException + { + switch (targetSqlType) + { + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.REAL: + case Types.FLOAT: + case Types.DOUBLE: + case Types.DECIMAL: + case Types.NUMERIC: + if (x instanceof Boolean) + set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0"); + else + set(parameterIndex, x.toString()); + break; + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + setString(parameterIndex, x.toString()); + break; + case Types.DATE: + setDate(parameterIndex, (java.sql.Date)x); + break; + case Types.TIME: + setTime(parameterIndex, (Time)x); + break; + case Types.TIMESTAMP: + setTimestamp(parameterIndex, (Timestamp)x); + break; + case Types.OTHER: + setString(parameterIndex, ((PGobject)x).getValue()); + break; + default: + throw new SQLException("Unknown Types value"); + } + } + + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException + { + setObject(parameterIndex, x, targetSqlType, 0); + } + + /** + * This stores an Object into a parameter. + *

New for 6.4, if the object is not recognised, but it is + * Serializable, then the object is serialised using the + * postgresql.util.Serialize class. + */ + public void setObject(int parameterIndex, Object x) throws SQLException + { + if (x instanceof String) + setString(parameterIndex, (String)x); + else if (x instanceof BigDecimal) + setBigDecimal(parameterIndex, (BigDecimal)x); + else if (x instanceof Short) + setShort(parameterIndex, ((Short)x).shortValue()); + else if (x instanceof Integer) + setInt(parameterIndex, ((Integer)x).intValue()); + else if (x instanceof Long) + setLong(parameterIndex, ((Long)x).longValue()); + else if (x instanceof Float) + setFloat(parameterIndex, ((Float)x).floatValue()); + else if (x instanceof Double) + setDouble(parameterIndex, ((Double)x).doubleValue()); + else if (x instanceof byte[]) + setBytes(parameterIndex, (byte[])x); + else if (x instanceof java.sql.Date) + setDate(parameterIndex, (java.sql.Date)x); + else if (x instanceof Time) + setTime(parameterIndex, (Time)x); + else if (x instanceof Timestamp) + setTimestamp(parameterIndex, (Timestamp)x); + else if (x instanceof Boolean) + setBoolean(parameterIndex, ((Boolean)x).booleanValue()); + else if (x instanceof PGobject) + setString(parameterIndex, ((PGobject)x).getValue()); + else + setLong(parameterIndex, connection.putObject(x)); + } + + /** + * Some prepared statements return multiple results; the execute method + * handles these complex statements as well as the simpler form of + * statements handled by executeQuery and executeUpdate + * + * @return true if the next result is a ResultSet; false if it is an + * update count or there are no more results + * @exception SQLException if a database access error occurs + */ + public boolean execute() throws SQLException + { + StringBuffer s = new StringBuffer(); + int i; + + for (i = 0 ; i < inStrings.length ; ++i) + { + if (inStrings[i] == null) + throw new SQLException("No value specified for parameter " + (i + 1)); + s.append (templateStrings[i]); + s.append (inStrings[i]); + } + s.append(templateStrings[inStrings.length]); + return super.execute(s.toString()); // in Statement class + } + + /** + * Returns the SQL statement with the current template values + * substituted. + */ + public String toString() { + StringBuffer s = new StringBuffer(); + int i; + + for (i = 0 ; i < inStrings.length ; ++i) + { + if (inStrings[i] == null) + s.append( '?' ); + else + s.append (templateStrings[i]); + s.append (inStrings[i]); + } + s.append(templateStrings[inStrings.length]); + return s.toString(); + } + + // ************************************************************** + // END OF PUBLIC INTERFACE + // ************************************************************** + + /** + * There are a lot of setXXX classes which all basically do + * the same thing. We need a method which actually does the + * set for us. + * + * @param paramIndex the index into the inString + * @param s a string to be stored + * @exception SQLException if something goes wrong + */ + private void set(int paramIndex, String s) throws SQLException + { + if (paramIndex < 1 || paramIndex > inStrings.length) + throw new SQLException("Parameter index out of range"); + inStrings[paramIndex - 1] = s; + } + + // ** JDBC 2 Extensions ** + + public void addBatch() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public java.sql.ResultSetMetaData getMetaData() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setArray(int i,Array x) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setBlob(int i,Blob x) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setCharacterStream(int i,java.io.Reader x,int length) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setClob(int i,Clob x) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setNull(int i,int t,String s) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setRef(int i,Ref x) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setDate(int i,java.sql.Date d,java.util.Calendar cal) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setTime(int i,Time t,java.util.Calendar cal) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setTimestamp(int i,Timestamp t,java.util.Calendar cal) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + +} diff --git a/src/interfaces/jdbc/postgresql/jdbc2/ResultSet.java b/src/interfaces/jdbc/postgresql/jdbc2/ResultSet.java new file mode 100644 index 0000000000..9790dac4aa --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc2/ResultSet.java @@ -0,0 +1,1261 @@ +package postgresql.jdbc2; + +// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 1 class in the +// postgresql.jdbc1 package. + + +import java.lang.*; +import java.io.*; +import java.math.*; +import java.text.*; +import java.util.*; +import java.sql.*; +import postgresql.Field; +import postgresql.largeobject.*; +import postgresql.util.*; + +/** + * A ResultSet provides access to a table of data generated by executing a + * Statement. The table rows are retrieved in sequence. Within a row its + * column values can be accessed in any order. + * + *

A ResultSet maintains a cursor pointing to its current row of data. + * Initially the cursor is positioned before the first row. The 'next' + * method moves the cursor to the next row. + * + *

The getXXX methods retrieve column values for the current row. You can + * retrieve values either using the index number of the column, or by using + * the name of the column. In general using the column index will be more + * efficient. Columns are numbered from 1. + * + *

For maximum portability, ResultSet columns within each row should be read + * in left-to-right order and each column should be read only once. + * + *

For the getXXX methods, the JDBC driver attempts to convert the + * underlying data to the specified Java type and returns a suitable Java + * value. See the JDBC specification for allowable mappings from SQL types + * to Java types with the ResultSet getXXX methods. + * + *

Column names used as input to getXXX methods are case insenstive. When + * performing a getXXX using a column name, if several columns have the same + * name, then the value of the first matching column will be returned. The + * column name option is designed to be used when column names are used in the + * SQL Query. For columns that are NOT explicitly named in the query, it is + * best to use column numbers. If column names were used there is no way for + * 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 + * next result from a sequence of multiple results. + * + *

The number, types and properties of a ResultSet's columns are provided by + * the ResultSetMetaData object returned by the getMetaData method. + * + * @see ResultSetMetaData + * @see java.sql.ResultSet + */ +public class ResultSet extends postgresql.ResultSet implements java.sql.ResultSet +{ + /** + * Create a new ResultSet - Note that we create ResultSets to + * represent the results of everything. + * + * @param fields an array of Field objects (basically, the + * ResultSet MetaData) + * @param tuples Vector of the actual data + * @param status the status string returned from the back end + * @param updateCount the number of rows affected by the operation + * @param cursor the positioned update/delete cursor name + */ + public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount) + { + super(conn,fields,tuples,status,updateCount); + } + + /** + * A ResultSet is initially positioned before its first row, + * the first call to next makes the first row the current row; + * the second call makes the second row the current row, etc. + * + *

If an input stream from the previous row is open, it is + * implicitly closed. The ResultSet's warning chain is cleared + * when a new row is read + * + * @return true if the new current is valid; false if there are no + * more rows + * @exception SQLException if a database access error occurs + */ + public boolean next() throws SQLException + { + if (++current_row >= rows.size()) + return false; + 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 + * when it is automatically closed. The close method provides this + * immediate release. + * + *

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 + * when it is garbage collected. + * + * @exception SQLException if a database access error occurs + */ + public void close() throws SQLException + { + // No-op + } + + /** + * A column may have the value of SQL NULL; wasNull() reports whether + * the last column read had this special value. Note that you must + * first call getXXX on a column to try to read its value and then + * call wasNull() to find if the value was SQL NULL + * + * @return true if the last column read was SQL NULL + * @exception SQLException if a database access error occurred + */ + public boolean wasNull() throws SQLException + { + return wasNullFlag; + } + + /** + * Get the value of a column in the current row as a Java String + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value, null for SQL NULL + * @exception SQLException if a database access error occurs + */ + public String getString(int columnIndex) throws SQLException + { + //byte[] bytes = getBytes(columnIndex); + // + //if (bytes == null) + //return null; + //return new String(bytes); + if (columnIndex < 1 || columnIndex > fields.length) + throw new SQLException("Column Index out of range"); + wasNullFlag = (this_row[columnIndex - 1] == null); + if(wasNullFlag) + return null; + return new String(this_row[columnIndex - 1]); + } + + /** + * Get the value of a column in the current row as a Java boolean + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value, false for SQL NULL + * @exception SQLException if a database access error occurs + */ + public boolean getBoolean(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + int c = s.charAt(0); + return ((c == 't') || (c == 'T')); + } + return false; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java byte. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public byte getByte(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Byte.parseByte(s); + } catch (NumberFormatException e) { + throw new SQLException("Bad Byte Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java short. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public short getShort(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Short.parseShort(s); + } catch (NumberFormatException e) { + throw new SQLException("Bad Short Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java int. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public int getInt(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Integer Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java long. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public long getLong(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Long.parseLong(s); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Long Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java float. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public float getFloat(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Float.valueOf(s).floatValue(); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Float Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java double. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public double getDouble(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Double.valueOf(s).doubleValue(); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Double Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a + * java.lang.BigDecimal object + * + * @param columnIndex the first column is 1, the second is 2... + * @param scale the number of digits to the right of the decimal + * @return the column value; if the value is SQL NULL, null + * @exception SQLException if a database access error occurs + * @deprecated + */ + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException + { + String s = getString(columnIndex); + BigDecimal val; + + if (s != null) + { + try + { + val = new BigDecimal(s); + } catch (NumberFormatException e) { + throw new SQLException ("Bad BigDecimal Form: " + s); + } + try + { + return val.setScale(scale); + } catch (ArithmeticException e) { + throw new SQLException ("Bad BigDecimal Form: " + s); + } + } + return null; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java byte array. + * + *

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

Be warned If the large object is huge, then you may run out + * of memory. + * + * @param columnIndex the first column is 1, the second is 2, ... + * @return the column value; if the value is SQL NULL, the result + * is null + * @exception SQLException if a database access error occurs + */ + public byte[] getBytes(int columnIndex) throws SQLException + { + if (columnIndex < 1 || columnIndex > fields.length) + throw new SQLException("Column Index out of range"); + wasNullFlag = (this_row[columnIndex - 1] == null); + + // Handle OID's as BLOBS + if(!wasNullFlag) + if( fields[columnIndex - 1].getOID() == 26) { + LargeObjectManager lom = connection.getLargeObjectAPI(); + LargeObject lob = lom.open(getInt(columnIndex)); + byte buf[] = lob.read(lob.size()); + lob.close(); + return buf; + } + + return this_row[columnIndex - 1]; + } + + /** + * Get the value of a column in the current row as a java.sql.Date + * object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public java.sql.Date getDate(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + if(s==null) + return null; + SimpleDateFormat df = new SimpleDateFormat(connection.getDateStyle()); + try { + return new java.sql.Date(df.parse(s).getTime()); + } catch (ParseException e) { + throw new SQLException("Bad Date Format: at " + e.getErrorOffset() + " in " + s); + } + } + + /** + * Get the value of a column in the current row as a java.sql.Time + * object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public Time getTime(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + if (s.length() != 5 && s.length() != 8) + throw new NumberFormatException("Wrong Length!"); + int hr = Integer.parseInt(s.substring(0,2)); + int min = Integer.parseInt(s.substring(3,5)); + int sec = (s.length() == 5) ? 0 : Integer.parseInt(s.substring(6)); + return new Time(hr, min, sec); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Time Form: " + s); + } + } + return null; // SQL NULL + } + + /** + * 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... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public Timestamp getTimestamp(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzz"); + + if (s != null) + { + int TZ = new Float(s.substring(19)).intValue(); + TZ = TZ * 60 * 60 * 1000; + TimeZone zone = TimeZone.getDefault(); + zone.setRawOffset(TZ); + String nm = zone.getID(); + s = s.substring(0,18) + nm; + try { + java.util.Date d = df.parse(s); + return new Timestamp(d.getTime()); + } catch (ParseException e) { + throw new SQLException("Bad Timestamp Format: at " + e.getErrorOffset() + " in " + s); + } + } + return null; // SQL NULL + } + + /** + * A column value can be retrieved as a stream of ASCII characters + * 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. + * + *

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

We implement an ASCII stream as a Binary stream - we should really + * do the data conversion, but I cannot be bothered to implement this + * right now. + * + * @param columnIndex the first column is 1, the second is 2, ... + * @return a Java InputStream that delivers the database column + * value as a stream of one byte ASCII characters. If the + * value is SQL NULL then the result is null + * @exception SQLException if a database access error occurs + * @see getBinaryStream + */ + public InputStream getAsciiStream(int columnIndex) throws SQLException + { + return getBinaryStream(columnIndex); + } + + /** + * A column value can also be retrieved as a stream of Unicode + * characters. We implement this as a binary stream. + * + * ** DEPRECATED IN JDBC 2 ** + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Java InputStream that delivers the database column value + * as a stream of two byte Unicode characters. If the value is + * SQL NULL, then the result is null + * @exception SQLException if a database access error occurs + * @see getAsciiStream + * @see getBinaryStream + * @deprecated + */ + public InputStream getUnicodeStream(int columnIndex) throws SQLException + { + return getBinaryStream(columnIndex); + } + + /** + * A column value can also be retrieved as a binary strea. This + * method is suitable for retrieving LONGVARBINARY values. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Java InputStream that delivers the database column value + * as a stream of bytes. If the value is SQL NULL, then the result + * is null + * @exception SQLException if a database access error occurs + * @see getAsciiStream + * @see getUnicodeStream + */ + 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. + * + * @param columnName is the SQL name of the column + * @return the column value + * @exception SQLException if a database access error occurs + */ + public String getString(String columnName) throws SQLException + { + 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 + */ + public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException + { + 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 ** + * + * @deprecated + */ + public InputStream getUnicodeStream(String columnName) throws SQLException + { + 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 + * to this SQLWarning. + * + *

The warning chain is automatically cleared each time a new + * row is read. + * + *

Note: This warning chain only covers warnings caused by + * ResultSet methods. Any warnings caused by statement methods + * (such as reading OUT parameters) will be chained on the + * Statement object. + * + * @return the first SQLWarning or null; + * @exception SQLException if a database access error occurs. + */ + public SQLWarning getWarnings() throws SQLException + { + return warnings; + } + + /** + * After this call, getWarnings returns null until a new warning + * is reported for this ResultSet + * + * @exception SQLException if a database access error occurs + */ + public void clearWarnings() throws SQLException + { + warnings = null; + } + + /** + * Get the name of the SQL cursor used by this ResultSet + * + *

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

JDBC supports this SQL feature by providing the name of the + * SQL cursor used by a ResultSet. The current row of a ResulSet + * is also the current row of this SQL cursor. + * + *

Note: If positioned update is not supported, a SQLException + * is thrown. + * + * @return the ResultSet's SQL cursor name. + * @exception SQLException if a database access error occurs + */ + public String getCursorName() throws SQLException + { + return connection.getCursorName(); + } + + /** + * The numbers, types and properties of a ResultSet's columns are + * provided by the getMetaData method + * + * @return a description of the ResultSet's columns + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSetMetaData getMetaData() throws SQLException + { + return new ResultSetMetaData(rows, fields); + } + + /** + * Get the value of a column in the current row as a Java object + * + *

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

This method may also be used to read database specific abstract + * data types. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Object holding the column value + * @exception SQLException if a database access error occurs + */ + public Object getObject(int columnIndex) throws SQLException + { + Field field; + + if (columnIndex < 1 || columnIndex > fields.length) + throw new SQLException("Column index out of range"); + 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: + return new Boolean(getBoolean(columnIndex)); + case Types.SMALLINT: + return new Integer(getInt(columnIndex)); + case Types.INTEGER: + return new Integer(getInt(columnIndex)); + case Types.BIGINT: + return new Long(getLong(columnIndex)); + case Types.NUMERIC: + return getBigDecimal(columnIndex, 0); + case Types.REAL: + return new Float(getFloat(columnIndex)); + case Types.DOUBLE: + return new Double(getDouble(columnIndex)); + case Types.CHAR: + case Types.VARCHAR: + return getString(columnIndex); + case Types.DATE: + return getDate(columnIndex); + case Types.TIME: + return getTime(columnIndex); + case Types.TIMESTAMP: + return getTimestamp(columnIndex); + default: + return connection.getObject(field.getTypeName(), getString(columnIndex)); + } + } + + /** + * Get the value of a column in the current row as a Java object + * + *

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

This method may also be used to read database specific abstract + * data types. + * + * @param columnName is the SQL name of the column + * @return a Object holding the column value + * @exception SQLException if a database access error occurs + */ + public Object getObject(String columnName) throws SQLException + { + return getObject(findColumn(columnName)); + } + + /** + * Map a ResultSet column name to a ResultSet column index + * + * @param columnName the name of the column + * @return the column index + * @exception SQLException if a database access error occurs + */ + 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 SQLException ("Column name not found"); + } + + // ** JDBC 2 Extensions ** + + public boolean absolute(int row) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void afterLast() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void beforeFirst() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void cancelRowUpdates() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void deleteRow() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean first() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Array getArray(String colName) throws SQLException + { + return getArray(findColumn(colName)); + } + + public Array getArray(int i) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + 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 + { + throw postgresql.Driver.notImplemented(); + } + + public java.io.Reader getCharacterStream(String columnName) throws SQLException + { + return getCharacterStream(findColumn(columnName)); + } + + public java.io.Reader getCharacterStream(int i) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Clob getClob(String columnName) throws SQLException + { + return getClob(findColumn(columnName)); + } + + public Clob getClob(int i) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public int getConcurrency() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public java.sql.Date getDate(int i,java.util.Calendar cal) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Time getTime(int i,java.util.Calendar cal) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Timestamp getTimestamp(int i,java.util.Calendar cal) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + 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 postgresql.Driver.notImplemented(); + } + + public int getFetchSize() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public int getKeysetSize() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Object getObject(String columnName,java.util.Map map) throws SQLException + { + return getObject(findColumn(columnName),map); + } + + public Object getObject(int i,java.util.Map map) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public Ref getRef(String columnName) throws SQLException + { + return getRef(findColumn(columnName)); + } + + public Ref getRef(int i) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public int getRow() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + // This one needs some thought, as not all ResultSets come from a statement + public java.sql.Statement getStatement() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public int getType() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void insertRow() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean isAfterLast() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean isBeforeFirst() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean isFirst() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean isLast() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean last() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void moveToCurrentRow() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void moveToInsertRow() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean previous() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void refreshRow() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean relative(int rows) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean rowDeleted() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean rowInserted() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public boolean rowUpdated() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setFetchDirection(int direction) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setFetchSize(int rows) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setKeysetSize(int keys) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void updateAsciiStream(int columnIndex, + java.io.InputStream x, + int length + ) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void updateAsciiStream(String columnName, + java.io.InputStream x, + int length + ) throws SQLException + { + updateAsciiStream(findColumn(columnName),x,length); + } + + public void updateBigDecimal(int columnIndex, + java.math.BigDecimal x + ) throws SQLException + { + throw 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 + ) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void updateBinaryStream(String columnName, + java.io.InputStream x, + int length + ) throws SQLException + { + updateBinaryStream(findColumn(columnName),x,length); + } + + public void updateBoolean(int columnIndex,boolean x) throws SQLException + { + throw 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 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 postgresql.Driver.notImplemented(); + } + + public void updateCharacterStream(int columnIndex, + java.io.Reader x, + int length + ) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void updateCharacterStream(String columnName, + java.io.Reader x, + int length + ) throws SQLException + { + updateCharacterStream(findColumn(columnName),x,length); + } + + public void updateDate(int columnIndex,java.sql.Date x) throws SQLException + { + throw 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 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 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 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 postgresql.Driver.notImplemented(); + } + + public void updateLong(String columnName,long x) throws SQLException + { + updateLong(findColumn(columnName),x); + } + + public void updateNull(int columnIndex) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void updateNull(String columnName) throws SQLException + { + updateNull(findColumn(columnName)); + } + + public void updateObject(int columnIndex,Object x) throws SQLException + { + throw 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 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 postgresql.Driver.notImplemented(); + } + + public void updateShort(int columnIndex,short x) throws SQLException + { + throw 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 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 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 postgresql.Driver.notImplemented(); + } + + public void updateTimestamp(String columnName,Timestamp x) throws SQLException + { + updateTimestamp(findColumn(columnName),x); + } + +} + diff --git a/src/interfaces/jdbc/postgresql/jdbc2/ResultSetMetaData.java b/src/interfaces/jdbc/postgresql/jdbc2/ResultSetMetaData.java new file mode 100644 index 0000000000..8ac88b905f --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc2/ResultSetMetaData.java @@ -0,0 +1,435 @@ +package postgresql.jdbc2; + +// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 1 class in the +// postgresql.jdbc1 package. + +import java.lang.*; +import java.sql.*; +import java.util.*; +import postgresql.*; + +/** + * A ResultSetMetaData object can be used to find out about the types and + * properties of the columns in a ResultSet + * + * @see java.sql.ResultSetMetaData + */ +public class ResultSetMetaData implements java.sql.ResultSetMetaData +{ + Vector rows; + Field[] fields; + + /** + * Initialise for a result with a tuple set and + * a field descriptor set + * + * @param rows the Vector of rows returned by the ResultSet + * @param fields the array of field descriptors + */ + public ResultSetMetaData(Vector rows, Field[] fields) + { + this.rows = rows; + this.fields = fields; + } + + /** + * Whats the number of columns in the ResultSet? + * + * @return the number + * @exception SQLException if a database access error occurs + */ + public int getColumnCount() throws SQLException + { + return fields.length; + } + + /** + * Is the column automatically numbered (and thus read-only) + * I believe that PostgreSQL does not support this feature. + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isAutoIncrement(int column) throws SQLException + { + return false; + } + + /** + * Does a column's case matter? ASSUMPTION: Any field that is + * not obviously case insensitive is assumed to be case sensitive + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isCaseSensitive(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + case Types.INTEGER: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return false; + default: + return true; + } + } + + /** + * Can the column be used in a WHERE clause? Basically for + * this, I split the functions into two types: recognised + * types (which are always useable), and OTHER types (which + * may or may not be useable). The OTHER types, for now, I + * will assume they are useable. We should really query the + * catalog to see if they are useable. + * + * @param column the first column is 1, the second is 2... + * @return true if they can be used in a WHERE clause + * @exception SQLException if a database access error occurs + */ + public boolean isSearchable(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + // This switch is pointless, I know - but it is a set-up + // for further expansion. + switch (sql_type) + { + case Types.OTHER: + return true; + default: + return true; + } + } + + /** + * Is the column a cash value? 6.1 introduced the cash/money + * type, which haven't been incorporated as of 970414, so I + * just check the type name for both 'cash' and 'money' + * + * @param column the first column is 1, the second is 2... + * @return true if its a cash column + * @exception SQLException if a database access error occurs + */ + public boolean isCurrency(int column) throws SQLException + { + String type_name = getField(column).getTypeName(); + + return type_name.equals("cash") || type_name.equals("money"); + } + + /** + * Can you put a NULL in this column? I think this is always + * true in 6.1's case. It would only be false if the field had + * been defined NOT NULL (system catalogs could be queried?) + * + * @param column the first column is 1, the second is 2... + * @return one of the columnNullable values + * @exception SQLException if a database access error occurs + */ + public int isNullable(int column) throws SQLException + { + return columnNullable; // We can always put NULL in + } + + /** + * Is the column a signed number? In PostgreSQL, all numbers + * are signed, so this is trivial. However, strings are not + * signed (duh!) + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isSigned(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + case Types.INTEGER: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + return true; + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return false; // I don't know about these? + default: + return false; + } + } + + /** + * What is the column's normal maximum width in characters? + * + * @param column the first column is 1, the second is 2, etc. + * @return the maximum width + * @exception SQLException if a database access error occurs + */ + public int getColumnDisplaySize(int column) throws SQLException + { + int max = getColumnLabel(column).length(); + int i; + + for (i = 0 ; i < rows.size(); ++i) + { + byte[][] x = (byte[][])(rows.elementAt(i)); + if(x[column-1]!=null) { + int xl = x[column - 1].length; + if (xl > max) + max = xl; + } + } + return max; + } + + /** + * What is the suggested column title for use in printouts and + * displays? We suggest the ColumnName! + * + * @param column the first column is 1, the second is 2, etc. + * @return the column label + * @exception SQLException if a database access error occurs + */ + public String getColumnLabel(int column) throws SQLException + { + return getColumnName(column); + } + + /** + * What's a column's name? + * + * @param column the first column is 1, the second is 2, etc. + * @return the column name + * @exception SQLException if a database access error occurs + */ + public String getColumnName(int column) throws SQLException + { + Field f = getField(column); + if(f!=null) + return f.name; + return "field"+column; + } + + /** + * What is a column's table's schema? This relies on us knowing + * the table name....which I don't know how to do as yet. The + * JDBC specification allows us to return "" if this is not + * applicable. + * + * @param column the first column is 1, the second is 2... + * @return the Schema + * @exception SQLException if a database access error occurs + */ + public String getSchemaName(int column) throws SQLException + { + return ""; + } + + /** + * What is a column's number of decimal digits. + * + * @param column the first column is 1, the second is 2... + * @return the precision + * @exception SQLException if a database access error occurs + */ + public int getPrecision(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + return 5; + case Types.INTEGER: + return 10; + case Types.REAL: + return 8; + case Types.FLOAT: + return 16; + case Types.DOUBLE: + return 16; + case Types.VARCHAR: + return 0; + default: + return 0; + } + } + + /** + * What is a column's number of digits to the right of the + * decimal point? + * + * @param column the first column is 1, the second is 2... + * @return the scale + * @exception SQLException if a database access error occurs + */ + public int getScale(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + return 0; + case Types.INTEGER: + return 0; + case Types.REAL: + return 8; + case Types.FLOAT: + return 16; + case Types.DOUBLE: + return 16; + case Types.VARCHAR: + return 0; + default: + return 0; + } + } + + /** + * Whats a column's table's name? How do I find this out? Both + * getSchemaName() and getCatalogName() rely on knowing the table + * Name, so we need this before we can work on them. + * + * @param column the first column is 1, the second is 2... + * @return column name, or "" if not applicable + * @exception SQLException if a database access error occurs + */ + public String getTableName(int column) throws SQLException + { + return ""; + } + + /** + * What's a column's table's catalog name? As with getSchemaName(), + * we can say that if getTableName() returns n/a, then we can too - + * otherwise, we need to work on it. + * + * @param column the first column is 1, the second is 2... + * @return catalog name, or "" if not applicable + * @exception SQLException if a database access error occurs + */ + public String getCatalogName(int column) throws SQLException + { + return ""; + } + + /** + * What is a column's SQL Type? (java.sql.Type int) + * + * @param column the first column is 1, the second is 2, etc. + * @return the java.sql.Type value + * @exception SQLException if a database access error occurs + * @see postgresql.Field#getSQLType + * @see java.sql.Types + */ + public int getColumnType(int column) throws SQLException + { + return getField(column).getSQLType(); + } + + /** + * Whats is the column's data source specific type name? + * + * @param column the first column is 1, the second is 2, etc. + * @return the type name + * @exception SQLException if a database access error occurs + */ + public String getColumnTypeName(int column) throws SQLException + { + return getField(column).getTypeName(); + } + + /** + * Is the column definitely not writable? In reality, we would + * have to check the GRANT/REVOKE stuff for this to be effective, + * and I haven't really looked into that yet, so this will get + * re-visited. + * + * @param column the first column is 1, the second is 2, etc. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly(int column) throws SQLException + { + return false; + } + + /** + * Is it possible for a write on the column to succeed? Again, we + * would in reality have to check the GRANT/REVOKE stuff, which + * I haven't worked with as yet. However, if it isn't ReadOnly, then + * it is obviously writable. + * + * @param column the first column is 1, the second is 2, etc. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isWritable(int column) throws SQLException + { + if (isReadOnly(column)) + return true; + else + return false; + } + + /** + * Will a write on this column definately succeed? Hmmm...this + * is a bad one, since the two preceding functions have not been + * really defined. I cannot tell is the short answer. I thus + * return isWritable() just to give us an idea. + * + * @param column the first column is 1, the second is 2, etc.. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isDefinitelyWritable(int column) throws SQLException + { + return isWritable(column); + } + + // ******************************************************** + // END OF PUBLIC INTERFACE + // ******************************************************** + + /** + * For several routines in this package, we need to convert + * a columnIndex into a Field[] descriptor. Rather than do + * the same code several times, here it is. + * + * @param columnIndex the first column is 1, the second is 2... + * @return the Field description + * @exception SQLException if a database access error occurs + */ + private Field getField(int columnIndex) throws SQLException + { + if (columnIndex < 1 || columnIndex > fields.length) + throw new SQLException("Column index out of range"); + return fields[columnIndex - 1]; + } + + // ** JDBC 2 Extensions ** + + // This can hook into our PG_Object mechanism + public String getColumnClassName(int column) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + +} + diff --git a/src/interfaces/jdbc/postgresql/jdbc2/Statement.java b/src/interfaces/jdbc/postgresql/jdbc2/Statement.java new file mode 100644 index 0000000000..427efe14df --- /dev/null +++ b/src/interfaces/jdbc/postgresql/jdbc2/Statement.java @@ -0,0 +1,397 @@ +package postgresql.jdbc2; + +// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver. +// If you make any modifications to this file, you must make sure that the +// changes are also made (if relevent) to the related JDBC 1 class in the +// postgresql.jdbc1 package. + +import java.sql.*; + +/** + * 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. + * 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 + * statement's current ResultSet if an open one exists. + * + * @see java.sql.Statement + * @see ResultSet + */ +public class Statement implements java.sql.Statement +{ + Connection connection; // The connection who created us + java.sql.ResultSet result = null; // The current results + 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. + * + * @param c the Connection instantation that creates us + */ + public Statement (Connection c) + { + connection = c; + } + + /** + * Execute a SQL statement that retruns a single ResultSet + * + * @param sql typically a static SQL SELECT statement + * @return a ResulSet that contains the data produced by the query + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSet executeQuery(String sql) throws SQLException + { + this.execute(sql); + while (result != null && !((postgresql.ResultSet)result).reallyResultSet()) + result = ((postgresql.ResultSet)result).getNext(); + if (result == null) + throw new SQLException("no results returned"); + return result; + } + + /** + * Execute a SQL INSERT, UPDATE or DELETE statement. In addition + * SQL statements that return nothing such as SQL DDL statements + * can be executed + * + * @param sql a SQL statement + * @return either a row count, or 0 for SQL commands + * @exception SQLException if a database access error occurs + */ + public int executeUpdate(String sql) throws SQLException + { + this.execute(sql); + if (((postgresql.ResultSet)result).reallyResultSet()) + throw new SQLException("results returned"); + return this.getUpdateCount(); + } + + /** + * In many cases, it is desirable to immediately release a + * Statement's database and JDBC resources instead of waiting + * 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 + * ResultSet, if one exists, is also closed. + * + * @exception SQLException if a database access error occurs (why?) + */ + public void close() throws SQLException + { + result = null; + } + + /** + * The maxFieldSize limit (in bytes) is the maximum amount of + * data returned for any column value; it only applies to + * BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR + * columns. If the limit is exceeded, the excess data is silently + * discarded. + * + * @return the current max column size limit; zero means unlimited + * @exception SQLException if a database access error occurs + */ + public int getMaxFieldSize() throws SQLException + { + return 8192; // We cannot change this + } + + /** + * Sets the maxFieldSize - NOT! - We throw an SQLException just + * to inform them to stop doing this. + * + * @param max the new max column size limit; zero means unlimited + * @exception SQLException if a database access error occurs + */ + public void setMaxFieldSize(int max) throws SQLException + { + throw new SQLException("Attempt to setMaxFieldSize failed - compile time default"); + } + + /** + * The maxRows limit is set to limit the number of rows that + * any ResultSet can contain. If the limit is exceeded, the + * excess rows are silently dropped. + * + * @return the current maximum row limit; zero means unlimited + * @exception SQLException if a database access error occurs + */ + public int getMaxRows() throws SQLException + { + return connection.maxrows; + } + + /** + * Set the maximum number of rows + * + * @param max the new max rows limit; zero means unlimited + * @exception SQLException if a database access error occurs + * @see getMaxRows + */ + public void setMaxRows(int max) throws SQLException + { + connection.maxrows = max; + } + + /** + * If escape scanning is on (the default), the driver will do escape + * substitution before sending the SQL to the database. + * + * @param enable true to enable; false to disable + * @exception SQLException if a database access error occurs + */ + public void setEscapeProcessing(boolean enable) throws SQLException + { + escapeProcessing = enable; + } + + /** + * The queryTimeout limit is the number of seconds the driver + * will wait for a Statement to execute. If the limit is + * exceeded, a SQLException is thrown. + * + * @return the current query timeout limit in seconds; 0 = unlimited + * @exception SQLException if a database access error occurs + */ + public int getQueryTimeout() throws SQLException + { + return timeout; + } + + /** + * Sets the queryTimeout limit + * + * @param seconds - the new query timeout limit in seconds + * @exception SQLException if a database access error occurs + */ + public void setQueryTimeout(int seconds) throws SQLException + { + timeout = seconds; + } + + /** + * 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 + * define it as a no-op (i.e. you can't cancel, but there is no + * error if you try.) + * + * 6.4 introduced a cancel operation, but we have not implemented it + * yet. Sometime before 6.5, this method will be implemented. + * + * @exception SQLException only because thats the spec. + */ + public void cancel() throws SQLException + { + // No-op + } + + /** + * The first warning reported by calls on this Statement is + * returned. A Statement's execute methods clear its SQLWarning + * chain. Subsequent Statement warnings will be chained to this + * SQLWarning. + * + *

The Warning chain is automatically cleared each time a statement + * is (re)executed. + * + *

Note: If you are processing a ResultSet then any warnings + * associated with ResultSet reads will be chained on the ResultSet + * object. + * + * @return the first SQLWarning on null + * @exception SQLException if a database access error occurs + */ + public SQLWarning getWarnings() throws SQLException + { + return warnings; + } + + /** + * After this call, getWarnings returns null until a new warning + * is reported for this Statement. + * + * @exception SQLException if a database access error occurs (why?) + */ + public void clearWarnings() throws SQLException + { + warnings = null; + } + + /** + * setCursorName defines the SQL cursor name that will be used by + * subsequent execute methods. This name can then be used in SQL + * positioned update/delete statements to identify the current row + * in the ResultSet generated by this statement. If a database + * doesn't support positioned update/delete, this method is a + * no-op. + * + *

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

We throw an additional constriction. There can only be one + * cursor active at any one time. + * + * @param name the new cursor name + * @exception SQLException if a database access error occurs + */ + public void setCursorName(String name) throws SQLException + { + connection.setCursorName(name); + } + + /** + * 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 + * retrieve the result. + * + * @param sql any SQL statement + * @return true if the next result is a ResulSet, false if it is + * an update count or there are no more results + * @exception SQLException if a database access error occurs + */ + public boolean execute(String sql) throws SQLException + { + result = connection.ExecSQL(sql); + return (result != null && ((postgresql.ResultSet)result).reallyResultSet()); + } + + /** + * getResultSet returns the current result as a ResultSet. It + * should only be called once per result. + * + * @return the current result set; null if there are no more + * @exception SQLException if a database access error occurs (why?) + */ + public java.sql.ResultSet getResultSet() throws SQLException + { + return result; + } + + /** + * getUpdateCount returns the current result as an update count, + * if the result is a ResultSet or there are no more results, -1 + * is returned. It should only be called once per result. + * + * @return the current result as an update count. + * @exception SQLException if a database access error occurs + */ + public int getUpdateCount() throws SQLException + { + if (result == null) return -1; + if (((postgresql.ResultSet)result).reallyResultSet()) return -1; + return ((postgresql.ResultSet)result).getResultCount(); + } + + /** + * getMoreResults moves to a Statement's next result. If it returns + * true, this result is a ResulSet. + * + * @return true if the next ResultSet is valid + * @exception SQLException if a database access error occurs + */ + public boolean getMoreResults() throws SQLException + { + result = ((postgresql.ResultSet)result).getNext(); + return (result != null && ((postgresql.ResultSet)result).reallyResultSet()); + } + + /** + * Returns the status message from the current Result.

+ * This is used internally by the driver. + * + * @return status message from backend + */ + public String getResultStatusString() + { + if(result == null) + return null; + return ((postgresql.ResultSet)result).getStatusString(); + } + + // ** JDBC 2 Extensions ** + + public void addBatch(String sql) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void clearBatch() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public int[] executeBatch() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public java.sql.Connection getConnection() throws SQLException + { + return (java.sql.Connection)connection; + } + + public int getFetchDirection() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public int getFetchSize() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public int getKeysetSize() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public int getResultSetConcurrency() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public int getResultSetType() throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setFetchDirection(int direction) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setFetchSize(int rows) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setKeysetSize(int keys) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setResultSetConcurrency(int value) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + public void setResultSetType(int value) throws SQLException + { + throw postgresql.Driver.notImplemented(); + } + + +} diff --git a/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java b/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java index 0cab20f8e3..e2d394bf05 100644 --- a/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java +++ b/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java @@ -112,14 +112,40 @@ public class LargeObject * @return byte[] array containing data read * @exception SQLException if a database-access error occurs. */ - public byte[] read(int len) throws SQLException - { - FastpathArg args[] = new FastpathArg[2]; - args[0] = new FastpathArg(fd); - args[1] = new FastpathArg(len); - return fp.getData("loread",args); - } - + public byte[] read(int len) throws SQLException + { + // This is the original method, where the entire block (len bytes) + // is retrieved in one go. + FastpathArg args[] = new FastpathArg[2]; + args[0] = new FastpathArg(fd); + args[1] = new FastpathArg(len); + return fp.getData("loread",args); + + // This version allows us to break this down into 4k blocks + //if(len<=4048) { + //// handle as before, return the whole block in one go + //FastpathArg args[] = new FastpathArg[2]; + //args[0] = new FastpathArg(fd); + //args[1] = new FastpathArg(len); + //return fp.getData("loread",args); + //} else { + //// return in 4k blocks + //byte[] buf=new byte[len]; + //int off=0; + //while(len>0) { + //int bs=4048; + //len-=bs; + //if(len<0) { + //bs+=len; + //len=0; + //} + //read(buf,off,bs); + //off+=bs; + //} + //return buf; + //} + } + /** * Reads some data from the object into an existing array * diff --git a/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java b/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java index c7798d15a1..081b8b874a 100644 --- a/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java +++ b/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java @@ -102,7 +102,7 @@ public class LargeObjectManager // // This is an example of Fastpath.addFunctions(); // - ResultSet res = (postgresql.ResultSet)conn.createStatement().executeQuery("select proname, oid from pg_proc" + + java.sql.ResultSet res = (java.sql.ResultSet)conn.createStatement().executeQuery("select proname, oid from pg_proc" + " where proname = 'lo_open'" + " or proname = 'lo_close'" + " or proname = 'lo_creat'" +