diff options
Diffstat (limited to 'src/interfaces/jdbc/org/postgresql/jdbc1')
13 files changed, 0 insertions, 9758 deletions
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java deleted file mode 100644 index ddf6eb48a1..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java +++ /dev/null @@ -1,1843 +0,0 @@ -/*------------------------------------------------------------------------- - * - * AbstractJdbc1Connection.java - * This class defines methods of the jdbc1 specification. This class is - * extended by org.postgresql.jdbc2.AbstractJdbc2Connection which adds - * the jdbc2 methods. The real Connection class (for jdbc1) is - * org.postgresql.jdbc1.Jdbc1Connection - * - * Copyright (c) 2003, PostgreSQL Global Development Group - * - * IDENTIFICATION - * $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java,v 1.29 2003/12/18 03:27:15 davec Exp $ - * - *------------------------------------------------------------------------- - */ -package org.postgresql.jdbc1; - - -import java.io.IOException; -import java.net.ConnectException; -import java.sql.*; -import java.util.*; -import org.postgresql.Driver; -import org.postgresql.PGNotification; -import org.postgresql.core.BaseConnection; -import org.postgresql.core.BaseResultSet; -import org.postgresql.core.BaseStatement; -import org.postgresql.core.Encoding; -import org.postgresql.core.PGStream; -import org.postgresql.core.QueryExecutor; -import org.postgresql.core.StartupPacket; -import org.postgresql.fastpath.Fastpath; -import org.postgresql.largeobject.LargeObjectManager; -import org.postgresql.util.MD5Digest; -import org.postgresql.util.PGobject; -import org.postgresql.util.PSQLException; -import org.postgresql.util.PSQLState; -import org.postgresql.util.UnixCrypt; - -public abstract class AbstractJdbc1Connection implements BaseConnection -{ - // This is the network stream associated with this connection - private PGStream pgStream; - - public PGStream getPGStream() { - return pgStream; - } - - protected String PG_HOST; - protected int PG_PORT; - protected String PG_USER; - protected String PG_DATABASE; - protected boolean PG_STATUS; - protected String compatible; - protected boolean useSSL; - - // The PID an cancellation key we get from the backend process - protected int pid; - protected int ckey; - - private Vector m_notifications; - - /* - The encoding to use for this connection. - */ - private Encoding encoding = Encoding.defaultEncoding(); - - private String dbVersionNumber; - - public boolean CONNECTION_OK = true; - public boolean CONNECTION_BAD = false; - - public boolean autoCommit = true; - public boolean readOnly = false; - - public Driver this_driver; - private String this_url; - private String cursor = null; // The positioned update cursor name - - private int PGProtocolVersionMajor = 2; - private int PGProtocolVersionMinor = 0; - public int getPGProtocolVersionMajor() { return PGProtocolVersionMajor; } - public int getPGProtocolVersionMinor() { return PGProtocolVersionMinor; } - - private static final int AUTH_REQ_OK = 0; - private static final int AUTH_REQ_KRB4 = 1; - private static final int AUTH_REQ_KRB5 = 2; - private static final int AUTH_REQ_PASSWORD = 3; - private static final int AUTH_REQ_CRYPT = 4; - private static final int AUTH_REQ_MD5 = 5; - private static final int AUTH_REQ_SCM = 6; - - - // These are used to cache oids, PGTypes and SQLTypes - private static Hashtable sqlTypeCache = new Hashtable(); // oid -> SQLType - private static Hashtable pgTypeCache = new Hashtable(); // oid -> PGType - private static Hashtable typeOidCache = new Hashtable(); //PGType -> oid - - // Now handle notices as warnings, so things like "show" now work - public SQLWarning firstWarning = null; - - /* - * Cache of the current isolation level - */ - private int isolationLevel = Connection.TRANSACTION_READ_COMMITTED; - - - public abstract Statement createStatement() throws SQLException; - public abstract DatabaseMetaData getMetaData() throws SQLException; - - /* - * 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 url the URL of the connection - * @param d the Driver instantation of the connection - * @exception SQLException if a database access error occurs - */ - public void openConnection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException - { - firstWarning = null; - - // 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 - if (info.getProperty("user") == null) - throw new PSQLException("postgresql.con.user", PSQLState.CONNECTION_REJECTED); - - this_driver = (Driver)d; - this_url = url; - - PG_DATABASE = database; - PG_USER = info.getProperty("user"); - - String password = info.getProperty("password", ""); - PG_PORT = port; - - PG_HOST = host; - PG_STATUS = CONNECTION_BAD; - - if (info.getProperty("ssl") != null && Driver.sslEnabled()) - { - useSSL = true; - } - else - { - useSSL = false; - } - - if (info.getProperty("compatible") == null) - { - compatible = d.getMajorVersion() + "." + d.getMinorVersion(); - } - else - { - compatible = info.getProperty("compatible"); - } - - //Read loglevel arg and set the loglevel based on this value - //in addition to setting the log level enable output to - //standard out if no other printwriter is set - String l_logLevelProp = info.getProperty("loglevel", "0"); - int l_logLevel = 0; - try - { - l_logLevel = Integer.parseInt(l_logLevelProp); - if (l_logLevel > Driver.DEBUG || l_logLevel < Driver.INFO) - { - l_logLevel = 0; - } - } - catch (Exception l_e) - { - //invalid value for loglevel ignore - } - if (l_logLevel > 0) - { - Driver.setLogLevel(l_logLevel); - enableDriverManagerLogging(); - } - - //Print out the driver version number - if (Driver.logInfo) - Driver.info(Driver.getVersion()); - if (Driver.logDebug) { - Driver.debug(" ssl = " + useSSL); - Driver.debug(" compatible = " + compatible); - Driver.debug(" loglevel = " + l_logLevel); - } - - // Now make the initial connection - try - { - pgStream = new PGStream(host, port); - } - catch (ConnectException cex) - { - // Added by Peter Mount <peter@retep.org.uk> - // ConnectException is thrown when the connection cannot be made. - // we trap this an return a more meaningful message for the end user - throw new PSQLException ("postgresql.con.refused", PSQLState.CONNECTION_REJECTED); - } - catch (IOException e) - { - throw new PSQLException ("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e); - } - - //Now do the protocol work - if (haveMinimumCompatibleVersion("7.4")) { - openConnectionV3(host,port,info,database,url,d,password); - } else { - openConnectionV2(host,port,info,database,url,d,password); - } - } - - private void openConnectionV3(String p_host, int p_port, Properties p_info, String p_database, String p_url, Driver p_d, String p_password) throws SQLException - { - PGProtocolVersionMajor = 3; - if (Driver.logDebug) - Driver.debug("Using Protocol Version3"); - - // Now we need to construct and send an ssl startup packet - try - { - if (useSSL) { - if (Driver.logDebug) - Driver.debug("Asking server if it supports ssl"); - pgStream.SendInteger(8,4); - pgStream.SendInteger(80877103,4); - - // now flush the ssl packets to the backend - pgStream.flush(); - - // Now get the response from the backend, either an error message - // or an authentication request - int beresp = pgStream.ReceiveChar(); - if (Driver.logDebug) - Driver.debug("Server response was (S=Yes,N=No): "+(char)beresp); - switch (beresp) - { - case 'E': - // An error occured, so pass the error message to the - // user. - // - // The most common one to be thrown here is: - // "User authentication failed" - // - throw new PSQLException("postgresql.con.misc", PSQLState.CONNECTION_REJECTED, pgStream.ReceiveString(encoding)); - - case 'N': - // Server does not support ssl - throw new PSQLException("postgresql.con.sslnotsupported", PSQLState.CONNECTION_FAILURE); - - case 'S': - // Server supports ssl - if (Driver.logDebug) - Driver.debug("server does support ssl"); - Driver.makeSSL(pgStream); - break; - - default: - throw new PSQLException("postgresql.con.sslfail", PSQLState.CONNECTION_FAILURE); - } - } - } - catch (IOException e) - { - throw new PSQLException("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e); - } - - - // Now we need to construct and send a startup packet - try - { - new StartupPacket(PGProtocolVersionMajor, - PGProtocolVersionMinor, - PG_USER, - p_database).writeTo(pgStream); - - // now flush the startup packets to the backend - pgStream.flush(); - - // Now get the response from the backend, either an error message - // or an authentication request - int areq = -1; // must have a value here - do - { - int beresp = pgStream.ReceiveChar(); - String salt = null; - byte [] md5Salt = new byte[4]; - switch (beresp) - { - case 'E': - // An error occured, so pass the error message to the - // user. - // - // The most common one to be thrown here is: - // "User authentication failed" - // - int l_elen = pgStream.ReceiveIntegerR(4); - if (l_elen > 30000) { - //if the error length is > than 30000 we assume this is really a v2 protocol - //server so try again with a v2 connection - //need to create a new connection and try again - try - { - pgStream = new PGStream(p_host, p_port); - } - catch (ConnectException cex) - { - // Added by Peter Mount <peter@retep.org.uk> - // ConnectException is thrown when the connection cannot be made. - // we trap this an return a more meaningful message for the end user - throw new PSQLException ("postgresql.con.refused", PSQLState.CONNECTION_REJECTED); - } - catch (IOException e) - { - throw new PSQLException ("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e); - } - openConnectionV2(p_host, p_port, p_info, p_database, p_url, p_d, p_password); - return; - } - throw new PSQLException("postgresql.con.misc", PSQLState.CONNECTION_REJECTED, PSQLException.parseServerError(encoding.decode(pgStream.Receive(l_elen-4)))); - - case 'R': - // Get the message length - int l_msgLen = pgStream.ReceiveIntegerR(4); - // Get the type of request - areq = pgStream.ReceiveIntegerR(4); - // Get the crypt password salt if there is one - if (areq == AUTH_REQ_CRYPT) - { - byte[] rst = new byte[2]; - rst[0] = (byte)pgStream.ReceiveChar(); - rst[1] = (byte)pgStream.ReceiveChar(); - salt = new String(rst, 0, 2); - if (Driver.logDebug) - Driver.debug("Crypt salt=" + salt); - } - - // Or get the md5 password salt if there is one - if (areq == AUTH_REQ_MD5) - { - - md5Salt[0] = (byte)pgStream.ReceiveChar(); - md5Salt[1] = (byte)pgStream.ReceiveChar(); - md5Salt[2] = (byte)pgStream.ReceiveChar(); - md5Salt[3] = (byte)pgStream.ReceiveChar(); - salt = new String(md5Salt, 0, 4); - if (Driver.logDebug) - Driver.debug("MD5 salt=" + salt); - } - - // now send the auth packet - switch (areq) - { - case AUTH_REQ_OK: - break; - - case AUTH_REQ_KRB4: - if (Driver.logDebug) - Driver.debug("postgresql: KRB4"); - throw new PSQLException("postgresql.con.kerb4", PSQLState.CONNECTION_REJECTED); - - case AUTH_REQ_KRB5: - if (Driver.logDebug) - Driver.debug("postgresql: KRB5"); - throw new PSQLException("postgresql.con.kerb5", PSQLState.CONNECTION_REJECTED); - - case AUTH_REQ_SCM: - if (Driver.logDebug) - Driver.debug("postgresql: SCM"); - throw new PSQLException("postgresql.con.scm", PSQLState.CONNECTION_REJECTED); - - - case AUTH_REQ_PASSWORD: - if (Driver.logDebug) - Driver.debug("postgresql: PASSWORD"); - pgStream.SendChar('p'); - pgStream.SendInteger(5 + p_password.length(), 4); - pgStream.Send(p_password.getBytes()); - pgStream.SendChar(0); - pgStream.flush(); - break; - - case AUTH_REQ_CRYPT: - if (Driver.logDebug) - Driver.debug("postgresql: CRYPT"); - String crypted = UnixCrypt.crypt(salt, p_password); - pgStream.SendChar('p'); - pgStream.SendInteger(5 + crypted.length(), 4); - pgStream.Send(crypted.getBytes()); - pgStream.SendChar(0); - pgStream.flush(); - break; - - case AUTH_REQ_MD5: - if (Driver.logDebug) - Driver.debug("postgresql: MD5"); - byte[] digest = MD5Digest.encode(PG_USER, p_password, md5Salt); - pgStream.SendChar('p'); - pgStream.SendInteger(5 + digest.length, 4); - pgStream.Send(digest); - pgStream.SendChar(0); - pgStream.flush(); - break; - - default: - throw new PSQLException("postgresql.con.auth", PSQLState.CONNECTION_REJECTED, new Integer(areq)); - } - break; - - default: - throw new PSQLException("postgresql.con.authfail", PSQLState.CONNECTION_REJECTED); - } - } - while (areq != AUTH_REQ_OK); - - } - catch (IOException e) - { - throw new PSQLException("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e); - } - - int beresp; - do - { - beresp = pgStream.ReceiveChar(); - switch (beresp) - { - case 'Z': - //ready for query - break; - case 'K': - int l_msgLen = pgStream.ReceiveIntegerR(4); - if (l_msgLen != 12) throw new PSQLException("postgresql.con.setup", PSQLState.CONNECTION_UNABLE_TO_CONNECT); - pid = pgStream.ReceiveIntegerR(4); - ckey = pgStream.ReceiveIntegerR(4); - break; - case 'E': - int l_elen = pgStream.ReceiveIntegerR(4); - throw new PSQLException("postgresql.con.backend", PSQLState.CONNECTION_UNABLE_TO_CONNECT, PSQLException.parseServerError(encoding.decode(pgStream.Receive(l_elen-4)))); - case 'N': - int l_nlen = pgStream.ReceiveIntegerR(4); - addWarning(encoding.decode(pgStream.Receive(l_nlen-4))); - break; - case 'S': - //TODO: handle parameter status messages - int l_len = pgStream.ReceiveIntegerR(4); - String l_pStatus = encoding.decode(pgStream.Receive(l_len-4)); - if (Driver.logDebug) - Driver.debug("ParameterStatus="+ l_pStatus); - break; - default: - if (Driver.logDebug) - Driver.debug("invalid state="+ (char)beresp); - throw new PSQLException("postgresql.con.setup", PSQLState.CONNECTION_UNABLE_TO_CONNECT); - } - } - while (beresp != 'Z'); - // read ReadyForQuery - if (pgStream.ReceiveIntegerR(4) != 5) throw new PSQLException("postgresql.con.setup", PSQLState.CONNECTION_UNABLE_TO_CONNECT); - //TODO: handle transaction status - char l_tStatus = (char)pgStream.ReceiveChar(); - - // "pg_encoding_to_char(1)" will return 'EUC_JP' for a backend compiled with multibyte, - // otherwise it's hardcoded to 'SQL_ASCII'. - // If the backend doesn't know about multibyte we can't assume anything about the encoding - // used, so we denote this with 'UNKNOWN'. - //Note: begining with 7.2 we should be using pg_client_encoding() which - //is new in 7.2. However it isn't easy to conditionally call this new - //function, since we don't yet have the information as to what server - //version we are talking to. Thus we will continue to call - //getdatabaseencoding() until we drop support for 7.1 and older versions - //or until someone comes up with a conditional way to run one or - //the other function depending on server version that doesn't require - //two round trips to the server per connection - - final String encodingQuery = - "case when pg_encoding_to_char(1) = 'SQL_ASCII' then 'UNKNOWN' else getdatabaseencoding() end"; - - // Set datestyle and fetch db encoding in a single call, to avoid making - // more than one round trip to the backend during connection startup. - - - BaseResultSet resultSet - = execSQL("set datestyle to 'ISO'; select version(), " + encodingQuery + ";"); - - if (! resultSet.next()) - { - throw new PSQLException("postgresql.con.failed.bad.encoding", PSQLState.CONNECTION_UNABLE_TO_CONNECT); - } - String version = resultSet.getString(1); - dbVersionNumber = extractVersionNumber(version); - - String dbEncoding = resultSet.getString(2); - encoding = Encoding.getEncoding(dbEncoding, p_info.getProperty("charSet")); - //In 7.3 we are forced to do a second roundtrip to handle the case - //where a database may not be running in autocommit mode - //jdbc by default assumes autocommit is on until setAutoCommit(false) - //is called. Therefore we need to ensure a new connection is - //initialized to autocommit on. - //We also set the client encoding so that the driver only needs - //to deal with utf8. We can only do this in 7.3 because multibyte - //support is now always included - if (haveMinimumServerVersion("7.3")) - { - BaseResultSet acRset = - //TODO: if protocol V3 we can set the client encoding in startup - execSQL("set client_encoding = 'UNICODE'"); - //set encoding to be unicode - encoding = Encoding.getEncoding("UNICODE", null); - - } - - // Initialise object handling - initObjectTypes(); - - // Mark the connection as ok, and cleanup - PG_STATUS = CONNECTION_OK; - } - - private void openConnectionV2(String host, int port, Properties info, String database, String url, Driver d, String password) throws SQLException - { - PGProtocolVersionMajor = 2; - if (Driver.logDebug) - Driver.debug("Using Protocol Version2"); - - // Now we need to construct and send an ssl startup packet - try - { - if (useSSL) { - if (Driver.logDebug) - Driver.debug("Asking server if it supports ssl"); - pgStream.SendInteger(8,4); - pgStream.SendInteger(80877103,4); - - // now flush the ssl packets to the backend - pgStream.flush(); - - // Now get the response from the backend, either an error message - // or an authentication request - int beresp = pgStream.ReceiveChar(); - if (Driver.logDebug) - Driver.debug("Server response was (S=Yes,N=No): "+(char)beresp); - switch (beresp) - { - case 'E': - // An error occured, so pass the error message to the - // user. - // - // The most common one to be thrown here is: - // "User authentication failed" - // - throw new PSQLException("postgresql.con.misc", PSQLState.CONNECTION_REJECTED, pgStream.ReceiveString(encoding)); - - case 'N': - // Server does not support ssl - throw new PSQLException("postgresql.con.sslnotsupported", PSQLState.CONNECTION_FAILURE); - - case 'S': - // Server supports ssl - if (Driver.logDebug) - Driver.debug("server does support ssl"); - Driver.makeSSL(pgStream); - break; - - default: - throw new PSQLException("postgresql.con.sslfail", PSQLState.CONNECTION_FAILURE); - } - } - } - catch (IOException e) - { - throw new PSQLException("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e); - } - - - // Now we need to construct and send a startup packet - try - { - new StartupPacket(PGProtocolVersionMajor, - PGProtocolVersionMinor, - PG_USER, - database).writeTo(pgStream); - - // now flush the startup packets to the backend - pgStream.flush(); - - // Now get the response from the backend, either an error message - // or an authentication request - int areq = -1; // must have a value here - do - { - int beresp = pgStream.ReceiveChar(); - String salt = null; - byte [] md5Salt = new byte[4]; - switch (beresp) - { - case 'E': - // An error occured, so pass the error message to the - // user. - // - // The most common one to be thrown here is: - // "User authentication failed" - // - throw new PSQLException("postgresql.con.misc", PSQLState.CONNECTION_REJECTED, pgStream.ReceiveString(encoding)); - - case 'R': - // Get the type of request - areq = pgStream.ReceiveIntegerR(4); - // Get the crypt password salt if there is one - if (areq == AUTH_REQ_CRYPT) - { - byte[] rst = new byte[2]; - rst[0] = (byte)pgStream.ReceiveChar(); - rst[1] = (byte)pgStream.ReceiveChar(); - salt = new String(rst, 0, 2); - if (Driver.logDebug) - Driver.debug("Crypt salt=" + salt); - } - - // Or get the md5 password salt if there is one - if (areq == AUTH_REQ_MD5) - { - - md5Salt[0] = (byte)pgStream.ReceiveChar(); - md5Salt[1] = (byte)pgStream.ReceiveChar(); - md5Salt[2] = (byte)pgStream.ReceiveChar(); - md5Salt[3] = (byte)pgStream.ReceiveChar(); - salt = new String(md5Salt, 0, 4); - if (Driver.logDebug) - Driver.debug("MD5 salt=" + salt); - } - - // now send the auth packet - switch (areq) - { - case AUTH_REQ_OK: - break; - - case AUTH_REQ_KRB4: - if (Driver.logDebug) - Driver.debug("postgresql: KRB4"); - throw new PSQLException("postgresql.con.kerb4", PSQLState.CONNECTION_REJECTED); - - case AUTH_REQ_KRB5: - if (Driver.logDebug) - Driver.debug("postgresql: KRB5"); - throw new PSQLException("postgresql.con.kerb5", PSQLState.CONNECTION_REJECTED); - - case AUTH_REQ_PASSWORD: - if (Driver.logDebug) - Driver.debug("postgresql: PASSWORD"); - pgStream.SendInteger(5 + password.length(), 4); - pgStream.Send(password.getBytes()); - pgStream.SendInteger(0, 1); - pgStream.flush(); - break; - - case AUTH_REQ_CRYPT: - if (Driver.logDebug) - Driver.debug("postgresql: CRYPT"); - String crypted = UnixCrypt.crypt(salt, password); - pgStream.SendInteger(5 + crypted.length(), 4); - pgStream.Send(crypted.getBytes()); - pgStream.SendInteger(0, 1); - pgStream.flush(); - break; - - case AUTH_REQ_MD5: - if (Driver.logDebug) - Driver.debug("postgresql: MD5"); - byte[] digest = MD5Digest.encode(PG_USER, password, md5Salt); - pgStream.SendInteger(5 + digest.length, 4); - pgStream.Send(digest); - pgStream.SendInteger(0, 1); - pgStream.flush(); - break; - - default: - throw new PSQLException("postgresql.con.auth", PSQLState.CONNECTION_REJECTED, new Integer(areq)); - } - break; - - default: - throw new PSQLException("postgresql.con.authfail", PSQLState.CONNECTION_REJECTED); - } - } - while (areq != AUTH_REQ_OK); - - } - catch (IOException e) - { - //Should be passing exception as arg. - throw new PSQLException("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e); - } - - - // As of protocol version 2.0, we should now receive the cancellation key and the pid - int beresp; - do - { - beresp = pgStream.ReceiveChar(); - switch (beresp) - { - case 'K': - pid = pgStream.ReceiveIntegerR(4); - ckey = pgStream.ReceiveIntegerR(4); - break; - case 'E': - throw new PSQLException("postgresql.con.backend", PSQLState.CONNECTION_UNABLE_TO_CONNECT, pgStream.ReceiveString(encoding)); - case 'N': - addWarning(pgStream.ReceiveString(encoding)); - break; - default: - throw new PSQLException("postgresql.con.setup", PSQLState.CONNECTION_UNABLE_TO_CONNECT); - } - } - while (beresp == 'N'); - - // Expect ReadyForQuery packet - do - { - beresp = pgStream.ReceiveChar(); - switch (beresp) - { - case 'Z': - break; - case 'N': - addWarning(pgStream.ReceiveString(encoding)); - break; - case 'E': - throw new PSQLException("postgresql.con.backend", PSQLState.CONNECTION_UNABLE_TO_CONNECT, pgStream.ReceiveString(encoding)); - default: - throw new PSQLException("postgresql.con.setup", PSQLState.CONNECTION_UNABLE_TO_CONNECT); - } - } - while (beresp == 'N'); - // "pg_encoding_to_char(1)" will return 'EUC_JP' for a backend compiled with multibyte, - // otherwise it's hardcoded to 'SQL_ASCII'. - // If the backend doesn't know about multibyte we can't assume anything about the encoding - // used, so we denote this with 'UNKNOWN'. - //Note: begining with 7.2 we should be using pg_client_encoding() which - //is new in 7.2. However it isn't easy to conditionally call this new - //function, since we don't yet have the information as to what server - //version we are talking to. Thus we will continue to call - //getdatabaseencoding() until we drop support for 7.1 and older versions - //or until someone comes up with a conditional way to run one or - //the other function depending on server version that doesn't require - //two round trips to the server per connection - - final String encodingQuery = - "case when pg_encoding_to_char(1) = 'SQL_ASCII' then 'UNKNOWN' else getdatabaseencoding() end"; - - // Set datestyle and fetch db encoding in a single call, to avoid making - // more than one round trip to the backend during connection startup. - - - BaseResultSet resultSet - = execSQL("set datestyle to 'ISO'; select version(), " + encodingQuery + ";"); - - if (! resultSet.next()) - { - throw new PSQLException("postgresql.con.failed.bad.encoding", PSQLState.CONNECTION_UNABLE_TO_CONNECT); - } - String version = resultSet.getString(1); - dbVersionNumber = extractVersionNumber(version); - - String dbEncoding = resultSet.getString(2); - encoding = Encoding.getEncoding(dbEncoding, info.getProperty("charSet")); - - //TODO: remove this once the set is done as part of V3protocol connection initiation - if (haveMinimumServerVersion("7.4")) - { - BaseResultSet acRset = - execSQL("set client_encoding = 'UNICODE'"); - - //set encoding to be unicode - encoding = Encoding.getEncoding("UNICODE", null); - } - - //In 7.3 we are forced to do a second roundtrip to handle the case - //where a database may not be running in autocommit mode - //jdbc by default assumes autocommit is on until setAutoCommit(false) - //is called. Therefore we need to ensure a new connection is - //initialized to autocommit on. - //We also set the client encoding so that the driver only needs - //to deal with utf8. We can only do this in 7.3+ because multibyte - //support is now always included - if (haveMinimumServerVersion("7.3") && !haveMinimumServerVersion("7.4")) - { - BaseResultSet acRset = - execSQL("set client_encoding = 'UNICODE'; show autocommit"); - - //set encoding to be unicode - encoding = Encoding.getEncoding("UNICODE", null); - - if (!acRset.next()) - { - throw new PSQLException("postgresql.con.failed.bad.autocommit", PSQLState.CONNECTION_UNABLE_TO_CONNECT); - } - //if autocommit is currently off we need to turn it on - //note that we will be in a transaction because the select above - //will have initiated the transaction so we need a commit - //to make the setting permanent - if (acRset.getString(1).equals("off")) - { - execSQL("set autocommit = on; commit;"); - } - } - - // Initialise object handling - initObjectTypes(); - - // Mark the connection as ok, and cleanup - PG_STATUS = CONNECTION_OK; - } - - /* - * Return the instance of org.postgresql.Driver - * that created this connection - */ - public Driver getDriver() - { - return this_driver; - } - - - /* - * This adds a warning to the warning chain. - * @param msg message to add - */ - public void addWarning(String msg) - { - // Add the warning to the chain - if (firstWarning != null) - firstWarning.setNextWarning(new SQLWarning(msg)); - else - firstWarning = new SQLWarning(msg); - - // Now check for some specific messages - - // This is obsolete in 6.5, but I've left it in here so if we need to use this - // technique again, we'll know where to place it. - // - // 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;i<dateStyles.length;i+=2) - //if (msg.startsWith(dateStyles[i])) - //currentDateStyle=i+1; // this is the index of the format - //} - } - - /** Simple query execution. - */ - public BaseResultSet execSQL (String s) throws SQLException - { - final Object[] nullarr = new Object[0]; - BaseStatement stat = (BaseStatement) createStatement(); - return QueryExecutor.execute(new String[] { s }, - nullarr, - stat); - } - - /* - * 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... - */ - int lastMessage = 0; - public String getUserName() throws SQLException - { - return PG_USER; - } - - /* - * Get the character encoding to use for this connection. - */ - public Encoding getEncoding() throws SQLException - { - return encoding; - } - - /* - * This returns the Fastpath API for the current connection. - * - * <p><b>NOTE:</b> This is not part of JDBC, but allows access to - * functions on the org.postgresql backend itself. - * - * <p>It is primarily used by the LargeObject API - * - * <p>The best way to use this is as follows: - * - * <p><pre> - * import org.postgresql.fastpath.*; - * ... - * Fastpath fp = ((org.postgresql.Connection)myconn).getFastpathAPI(); - * </pre> - * - * <p>where myconn is an open Connection to org.postgresql. - * - * @return Fastpath object allowing access to functions on the org.postgresql - * backend. - * @exception SQLException by Fastpath when initialising for first time - */ - public Fastpath getFastpathAPI() throws SQLException - { - if (fastpath == null) - fastpath = new Fastpath(this, pgStream); - 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. - * - * <p><b>NOTE:</b> This is not part of JDBC, but allows access to - * functions on the org.postgresql backend itself. - * - * <p>The best way to use this is as follows: - * - * <p><pre> - * import org.postgresql.largeobject.*; - * ... - * LargeObjectManager lo = ((org.postgresql.Connection)myconn).getLargeObjectAPI(); - * </pre> - * - * <p>where myconn is an open Connection to org.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 - * org.postgresql's more unique data types. - * - * <p>It uses an internal Hashtable to get the handling class. If the - * type is not supported, then an instance of org.postgresql.util.PGobject - * is returned. - * - * You can use the getValue() or setValue() methods to handle the returned - * object. Custom objects can have their own methods. - * - * @return PGobject for this type, and set to value - * @exception SQLException if value is not correct for this type - */ - public Object getObject(String type, String value) throws SQLException - { - try - { - Object o = objectTypes.get(type); - - // If o is null, then the type is unknown. - // If o is not null, and it is a String, then its a class name that - // extends PGobject. - // - // This is used to implement the org.postgresql unique types (like lseg, - // point, etc). - if (o != null && 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; - } - } - catch (SQLException sx) - { - // rethrow the exception. Done because we capture any others next - sx.fillInStackTrace(); - throw sx; - } - catch (Exception ex) - { - throw new PSQLException("postgresql.con.creobj", PSQLState.CONNECTION_FAILURE, type, ex); - } - - // should never be reached - return null; - } - - /* - * This allows client code to add a handler for one of org.postgresql's - * more unique data types. - * - * <p><b>NOTE:</b> This is not part of JDBC, but an extension. - * - * <p>The best way to use this is as follows: - * - * <p><pre> - * ... - * ((org.postgresql.Connection)myconn).addDataType("mytype","my.class.name"); - * ... - * </pre> - * - * <p>where myconn is an open Connection to org.postgresql. - * - * <p>The handling class must extend org.postgresql.util.PGobject - * - * @see org.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", "org.postgresql.geometric.PGbox"}, - {"circle", "org.postgresql.geometric.PGcircle"}, - {"line", "org.postgresql.geometric.PGline"}, - {"lseg", "org.postgresql.geometric.PGlseg"}, - {"path", "org.postgresql.geometric.PGpath"}, - {"point", "org.postgresql.geometric.PGpoint"}, - {"polygon", "org.postgresql.geometric.PGpolygon"}, - {"money", "org.postgresql.util.PGmoney"} - }; - - // This initialises the objectTypes hashtable - private void initObjectTypes() - { - for (int i = 0;i < defaultObjectTypes.length;i++) - objectTypes.put(defaultObjectTypes[i][0], defaultObjectTypes[i][1]); - } - - /* - * 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) - * - * <B>Note:</B> 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 (getPGProtocolVersionMajor() == 3) { - closeV3(); - } else { - closeV2(); - } - } - - public void closeV3() throws SQLException - { - if (pgStream != null) - { - try - { - pgStream.SendChar('X'); - pgStream.SendInteger(4,4); - pgStream.flush(); - pgStream.close(); - } - catch (IOException e) - {} - finally - { - pgStream = null; - } - } - } - - public void closeV2() throws SQLException - { - if (pgStream != null) - { - try - { - pgStream.SendChar('X'); - pgStream.flush(); - pgStream.close(); - } - catch (IOException e) - {} - finally - { - pgStream = null; - } - } - } - - /* - * 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; - } - - /* - * The first warning reported by calls on this Connection is - * returned. - * - * <B>Note:</B> 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; - } - - - /* - * You can put a connection in read-only mode as a hunt to enable - * database optimizations - * - * <B>Note:</B> 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; - } - - /* - * 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 - { - if (haveMinimumServerVersion("7.1")) - { - execSQL("begin;" + getIsolationLevelSQL()); - } - else - { - execSQL("begin"); - execSQL(getIsolationLevelSQL()); - } - } - this.autoCommit = autoCommit; - } - - /* - * gets the current auto-commit state - * - * @return Current state of the auto-commit mode - * @see setAutoCommit - */ - public boolean getAutoCommit() - { - 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 ; - //TODO: delay starting new transaction until first command - if (haveMinimumServerVersion("7.1")) - { - execSQL("commit;begin;" + getIsolationLevelSQL()); - } - else - { - execSQL("commit"); - execSQL("begin"); - execSQL(getIsolationLevelSQL()); - } - } - - /* - * 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 ; - //TODO: delay starting transaction until first command - if (haveMinimumServerVersion("7.1")) - { - execSQL("rollback; begin;" + getIsolationLevelSQL()); - } - else - { - execSQL("rollback"); - execSQL("begin"); - execSQL(getIsolationLevelSQL()); - } - } - - /* - * 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 - { - String sql = "show transaction isolation level"; - String level = null; - if (haveMinimumServerVersion("7.3")) { - BaseResultSet rs = execSQL(sql); - if (rs.next()) { - level = rs.getString(1); - } - rs.close(); - } else { - BaseResultSet l_rs = execSQL(sql); - BaseStatement l_stat = l_rs.getPGStatement(); - SQLWarning warning = l_stat.getWarnings(); - if (warning != null) - { - level = warning.getMessage(); - } - l_rs.close(); - l_stat.close(); - } - if (level != null) { - level = level.toUpperCase(); - if (level.indexOf("READ COMMITTED") != -1) - return Connection.TRANSACTION_READ_COMMITTED; - else if (level.indexOf("READ UNCOMMITTED") != -1) - return Connection.TRANSACTION_READ_UNCOMMITTED; - else if (level.indexOf("REPEATABLE READ") != -1) - return Connection.TRANSACTION_REPEATABLE_READ; - else if (level.indexOf("SERIALIZABLE") != -1) - return Connection.TRANSACTION_SERIALIZABLE; - } - return Connection.TRANSACTION_READ_COMMITTED; - } - - /* - * You can call this method to try to change the transaction - * isolation level using one of the TRANSACTION_* values. - * - * <B>Note:</B> 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 - { - //In 7.1 and later versions of the server it is possible using - //the "set session" command to set this once for all future txns - //however in 7.0 and prior versions it is necessary to set it in - //each transaction, thus adding complexity below. - //When we decide to drop support for servers older than 7.1 - //this can be simplified - isolationLevel = level; - String isolationLevelSQL; - - if (!haveMinimumServerVersion("7.1")) - { - isolationLevelSQL = getIsolationLevelSQL(); - } - else - { - isolationLevelSQL = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL "; - switch (isolationLevel) - { - case Connection.TRANSACTION_READ_COMMITTED: - isolationLevelSQL += "READ COMMITTED"; - break; - case Connection.TRANSACTION_SERIALIZABLE: - isolationLevelSQL += "SERIALIZABLE"; - break; - default: - throw new PSQLException("postgresql.con.isolevel", PSQLState.TRANSACTION_STATE_INVALID, - new Integer(isolationLevel)); - } - } - execSQL(isolationLevelSQL); - } - - /* - * Helper method used by setTransactionIsolation(), commit(), rollback() - * and setAutoCommit(). This returns the SQL string needed to - * set the isolation level for a transaction. In 7.1 and later it - * is possible to set a default isolation level that applies to all - * future transactions, this method is only necesary for 7.0 and older - * servers, and should be removed when support for these older - * servers are dropped - */ - protected String getIsolationLevelSQL() throws SQLException - { - //7.1 and higher servers have a default specified so - //no additional SQL is required to set the isolation level - if (haveMinimumServerVersion("7.1")) - { - return ""; - } - StringBuffer sb = new StringBuffer("SET TRANSACTION ISOLATION LEVEL"); - - switch (isolationLevel) - { - case Connection.TRANSACTION_READ_COMMITTED: - sb.append(" READ COMMITTED"); - break; - - case Connection.TRANSACTION_SERIALIZABLE: - sb.append(" SERIALIZABLE"); - break; - - default: - throw new PSQLException("postgresql.con.isolevel", PSQLState.TRANSACTION_STATE_INVALID, new Integer(isolationLevel)); - } - return sb.toString(); - } - - /* - * 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 PG_DATABASE; - } - - /* - * Overides finalize(). If called, it closes the connection. - * - * This was done at the request of Rachel Greenham - * <rachel@enlarion.demon.co.uk> who hit a problem where multiple - * clients didn't close the connection, and once a fortnight enough - * clients were open to kill the org.postgres server. - */ - public void finalize() throws Throwable - { - close(); - } - - private static String extractVersionNumber(String fullVersionString) - { - StringTokenizer versionParts = new StringTokenizer(fullVersionString); - versionParts.nextToken(); /* "PostgreSQL" */ - return versionParts.nextToken(); /* "X.Y.Z" */ - } - - /* - * Get server version number - */ - public String getDBVersionNumber() - { - return dbVersionNumber; - } - - // Parse a "dirty" integer surrounded by non-numeric characters - private static int integerPart(String dirtyString) - { - int start, end; - - for (start = 0; start < dirtyString.length() && !Character.isDigit(dirtyString.charAt(start)); ++start) - ; - - for (end = start; end < dirtyString.length() && Character.isDigit(dirtyString.charAt(end)); ++end) - ; - - if (start == end) - return 0; - - return Integer.parseInt(dirtyString.substring(start, end)); - } - - /* - * Get server major version - */ - public int getServerMajorVersion() - { - try - { - StringTokenizer versionTokens = new StringTokenizer(dbVersionNumber, "."); // aaXbb.ccYdd - return integerPart(versionTokens.nextToken()); // return X - } - catch (NoSuchElementException e) - { - return 0; - } - } - - /* - * Get server minor version - */ - public int getServerMinorVersion() - { - try - { - StringTokenizer versionTokens = new StringTokenizer(dbVersionNumber, "."); // aaXbb.ccYdd - versionTokens.nextToken(); // Skip aaXbb - return integerPart(versionTokens.nextToken()); // return Y - } - catch (NoSuchElementException e) - { - return 0; - } - } - - /** - * Is the server we are connected to running at least this version? - * This comparison method will fail whenever a major or minor version - * goes to two digits (10.3.0) or (7.10.1). - */ - public boolean haveMinimumServerVersion(String ver) throws SQLException - { - return (getDBVersionNumber().compareTo(ver) >= 0); - } - - /* - * This method returns true if the compatible level set in the connection - * (which can be passed into the connection or specified in the URL) - * is at least the value passed to this method. This is used to toggle - * between different functionality as it changes across different releases - * of the jdbc driver code. The values here are versions of the jdbc client - * and not server versions. For example in 7.1 get/setBytes worked on - * LargeObject values, in 7.2 these methods were changed to work on bytea - * values. This change in functionality could be disabled by setting the - * "compatible" level to be 7.1, in which case the driver will revert to - * the 7.1 functionality. - */ - public boolean haveMinimumCompatibleVersion(String ver) throws SQLException - { - return (compatible.compareTo(ver) >= 0); - } - - - /* - * This returns the java.sql.Types type for a PG type oid - * - * @param oid PostgreSQL type oid - * @return the java.sql.Types type - * @exception SQLException if a database access error occurs - */ - public int getSQLType(int oid) throws SQLException - { - Integer sqlType = (Integer)sqlTypeCache.get(new Integer(oid)); - - // it's not in the cache, so perform a query, and add the result to the cache - if (sqlType == null) - { - String pgType; - // The opaque type does not exist in the system catalogs. - if (oid == 0) { - pgType = "opaque"; - } else { - String sql; - if (haveMinimumServerVersion("7.3")) { - sql = "SELECT typname FROM pg_catalog.pg_type WHERE oid = " +oid; - } else { - sql = "SELECT typname FROM pg_type WHERE oid = " +oid; - } - BaseResultSet result = execSQL(sql); - if (result.getColumnCount() != 1 || result.getTupleCount() != 1) { - throw new PSQLException("postgresql.unexpected", PSQLState.UNEXPECTED_ERROR); - } - result.next(); - pgType = result.getString(1); - result.close(); - } - Integer iOid = new Integer(oid); - sqlType = new Integer(getSQLType(pgType)); - sqlTypeCache.put(iOid, sqlType); - pgTypeCache.put(iOid, pgType); - } - - return sqlType.intValue(); - } - - /* - * This returns the oid for a given PG data type - * @param typeName PostgreSQL type name - * @return PostgreSQL oid value for a field of this type - */ - public int getPGType(String typeName) throws SQLException - { - int oid = -1; - if (typeName != null) - { - Integer oidValue = (Integer) typeOidCache.get(typeName); - if (oidValue != null) - { - oid = oidValue.intValue(); - } - else - { - // it's not in the cache, so perform a query, and add the result to the cache - String sql; - if (haveMinimumServerVersion("7.3")) { - sql = "SELECT oid FROM pg_catalog.pg_type WHERE typname='" + typeName + "'"; - } else { - sql = "SELECT oid FROM pg_type WHERE typname='" + typeName + "'"; - } - BaseResultSet result = execSQL(sql); - if (result.getColumnCount() != 1 || result.getTupleCount() != 1) - throw new PSQLException("postgresql.unexpected", PSQLState.UNEXPECTED_ERROR); - result.next(); - oid = Integer.parseInt(result.getString(1)); - typeOidCache.put(typeName, new Integer(oid)); - result.close(); - } - } - return oid; - } - - /* - * We also need to get the PG type name as returned by the back end. - * - * @return the String representation of the type of this field - * @exception SQLException if a database access error occurs - */ - public String getPGType(int oid) throws SQLException - { - String pgType = (String) pgTypeCache.get(new Integer(oid)); - if (pgType == null) - { - getSQLType(oid); - pgType = (String) pgTypeCache.get(new Integer(oid)); - } - return pgType; - } - - //Because the get/setLogStream methods are deprecated in JDBC2 - //we use them for JDBC1 here and override this method in the jdbc2 - //version of this class - protected void enableDriverManagerLogging() - { - if (DriverManager.getLogStream() == null) - { - DriverManager.setLogStream(System.out); - } - } - - // This is a cache of the DatabaseMetaData instance for this connection - protected java.sql.DatabaseMetaData metadata; - - - /* - * Tests to see if a Connection is closed - * - * @return the status of the connection - * @exception SQLException (why?) - */ - public boolean isClosed() throws SQLException - { - return (pgStream == null); - } - - /* - * This implemetation uses the jdbc1Types array to support the jdbc1 - * datatypes. Basically jdbc1 and jdbc2 are the same, except that - * jdbc2 adds the Array types. - */ - public int getSQLType(String pgTypeName) - { - int sqlType = Types.OTHER; // default value - for (int i = 0;i < jdbc1Types.length;i++) - { - if (pgTypeName.equals(jdbc1Types[i])) - { - sqlType = jdbc1Typei[i]; - break; - } - } - return sqlType; - } - - /* - * This table holds the org.postgresql names for the types supported. - * Any types that map to Types.OTHER (eg POINT) don't go into this table. - * They default automatically to Types.OTHER - * - * Note: This must be in the same order as below. - * - * Tip: keep these grouped together by the Types. value - */ - private static final String jdbc1Types[] = { - "int2", - "int4", "oid", - "int8", - "cash", "money", - "numeric", - "float4", - "float8", - "bpchar", "char", "char2", "char4", "char8", "char16", - "varchar", "text", "name", "filename", - "bytea", - "bool", - "bit", - "date", - "time", - "abstime", "timestamp", "timestamptz" - }; - - /* - * This table holds the JDBC type for each entry above. - * - * Note: This must be in the same order as above - * - * Tip: keep these grouped together by the Types. value - */ - private static final int jdbc1Typei[] = { - Types.SMALLINT, - Types.INTEGER, Types.INTEGER, - Types.BIGINT, - Types.DOUBLE, Types.DOUBLE, - Types.NUMERIC, - Types.REAL, - Types.DOUBLE, - Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, - Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, - Types.BINARY, - Types.BIT, - Types.BIT, - Types.DATE, - Types.TIME, - Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP - }; - - public void cancelQuery() throws SQLException - { - org.postgresql.core.PGStream cancelStream = null; - try - { - cancelStream = new org.postgresql.core.PGStream(PG_HOST, PG_PORT); - } - catch (ConnectException cex) - { - // Added by Peter Mount <peter@retep.org.uk> - // ConnectException is thrown when the connection cannot be made. - // we trap this an return a more meaningful message for the end user - throw new PSQLException ("postgresql.con.refused", PSQLState.CONNECTION_REJECTED); - } - catch (IOException e) - { - throw new PSQLException ("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e); - } - - // Now we need to construct and send a cancel packet - try - { - cancelStream.SendInteger(16, 4); - cancelStream.SendInteger(80877102, 4); - cancelStream.SendInteger(pid, 4); - cancelStream.SendInteger(ckey, 4); - cancelStream.flush(); - } - catch (IOException e) - { - throw new PSQLException("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e); - } - finally - { - try - { - if (cancelStream != null) - cancelStream.close(); - } - catch (IOException e) - {} // Ignore - } - } - - - //Methods to support postgres notifications - public void addNotification(org.postgresql.PGNotification p_notification) - { - if (m_notifications == null) - m_notifications = new Vector(); - m_notifications.addElement(p_notification); - } - - public PGNotification[] getNotifications() - { - PGNotification[] l_return = null; - if (m_notifications != null) - { - l_return = new PGNotification[m_notifications.size()]; - m_notifications.copyInto(l_return); - } - m_notifications = null; - return l_return; - } -} - - diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1DatabaseMetaData.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1DatabaseMetaData.java deleted file mode 100644 index d716193690..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1DatabaseMetaData.java +++ /dev/null @@ -1,3660 +0,0 @@ -package org.postgresql.jdbc1; - - -import java.sql.*; -import java.util.*; -import org.postgresql.core.BaseStatement; -import org.postgresql.core.Field; -import org.postgresql.core.Encoding; -import org.postgresql.util.PSQLException; -import org.postgresql.util.PSQLState; -import org.postgresql.Driver; - -public abstract class AbstractJdbc1DatabaseMetaData -{ - - private static final String keywords = "abort,acl,add,aggregate,append,archive," + - "arch_store,backward,binary,boolean,change,cluster," + - "copy,database,delimiter,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"; - - protected AbstractJdbc1Connection connection; // The connection association - protected Encoding encoding; - - // These define various OID's. Hopefully they will stay constant. - protected static final int iVarcharOid = 1043; // OID for varchar - protected static final int iBoolOid = 16; // OID for bool - protected static final int iInt2Oid = 21; // OID for int2 - protected static final int iInt4Oid = 23; // OID for int4 - protected static final int VARHDRSZ = 4; // length for int4 - - private int NAMEDATALEN = 0; // length for name datatype - private int INDEX_MAX_KEYS = 0; // maximum number of keys in an index. - - protected int getMaxIndexKeys() throws SQLException { - if (INDEX_MAX_KEYS == 0) { - String from; - if (connection.haveMinimumServerVersion("7.3")) { - from = "pg_catalog.pg_namespace n, pg_catalog.pg_type t1, pg_catalog.pg_type t2 WHERE t1.typnamespace=n.oid AND n.nspname='pg_catalog' AND "; - } else { - from = "pg_type t1, pg_type t2 WHERE "; - } - String sql = "SELECT t1.typlen/t2.typlen FROM "+from+" t1.typelem=t2.oid AND t1.typname='oidvector'"; - ResultSet rs = connection.createStatement().executeQuery(sql); - if (!rs.next()) { - throw new PSQLException("postgresql.unexpected", PSQLState.UNEXPECTED_ERROR); - } - INDEX_MAX_KEYS = rs.getInt(1); - rs.close(); - } - return INDEX_MAX_KEYS; - } - - protected int getMaxNameLength() throws SQLException { - if (NAMEDATALEN == 0) { - String sql; - if (connection.haveMinimumServerVersion("7.3")) { - sql = "SELECT t.typlen FROM pg_catalog.pg_type t, pg_catalog.pg_namespace n WHERE t.typnamespace=n.oid AND t.typname='name' AND n.nspname='pg_catalog'"; - } else { - sql = "SELECT typlen FROM pg_type WHERE typname='name'"; - } - ResultSet rs = connection.createStatement().executeQuery(sql); - if (!rs.next()) { - throw new PSQLException("postgresql.unexpected", PSQLState.UNEXPECTED_ERROR); - } - NAMEDATALEN = rs.getInt("typlen"); - rs.close(); - } - return NAMEDATALEN - 1; - } - - public AbstractJdbc1DatabaseMetaData(AbstractJdbc1Connection conn) - { - this.connection = conn; - try { - this.encoding = conn.getEncoding(); - } - catch (SQLException sqle) { - this.encoding = Encoding.defaultEncoding(); - } - - } - - /* - * 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 - { - if (Driver.logDebug) - Driver.debug("allProceduresAreCallable"); - 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 - { - if (Driver.logDebug) - Driver.debug("allTablesAreSelectable"); - 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 - { - String url = connection.getURL(); - if (Driver.logDebug) - Driver.debug("getURL " + url); - return url; - } - - /* - * 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 - { - String userName = connection.getUserName(); - if (Driver.logDebug) - Driver.debug("getUserName " + userName); - return userName; - } - - /* - * Is the database in read-only mode? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isReadOnly() throws SQLException - { - boolean isReadOnly = connection.isReadOnly(); - if (Driver.logDebug) - Driver.debug("isReadOnly " + isReadOnly); - return isReadOnly; - } - - /* - * Are NULL values sorted high? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedHigh() throws SQLException - { - boolean nullSortedHigh = connection.haveMinimumServerVersion("7.2"); - if (Driver.logDebug) - Driver.debug("nullsAreSortedHigh " + nullSortedHigh); - return nullSortedHigh; - } - - /* - * Are NULL values sorted low? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedLow() throws SQLException - { - if (Driver.logDebug) - Driver.debug("nullsAreSortedLow false"); - 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 - { - if (Driver.logDebug) - Driver.debug("nullsAreSortedAtStart false"); - 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 - { - boolean nullsAreSortedAtEnd = ! connection.haveMinimumServerVersion("7.2"); - if (Driver.logDebug) - Driver.debug("nullsAreSortedAtEnd " + nullsAreSortedAtEnd); - return nullsAreSortedAtEnd; - } - - /* - * 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 - { - if (Driver.logDebug) - Driver.debug("getDatabaseProductName PostgresSQL"); - return "PostgreSQL"; - } - - /* - * What is the version of this database product. - * - * @return the database version - * @exception SQLException if a database access error occurs - */ - public String getDatabaseProductVersion() throws SQLException - { - String versionNumber = connection.getDBVersionNumber(); - if (Driver.logDebug) - Driver.debug("getDatabaseProductVersion " + versionNumber); - return versionNumber; - } - - /* - * 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 - { - String driverName = "PostgreSQL Native Driver"; - if (Driver.logDebug) - Driver.debug("getDriverName" + driverName); - return driverName; - } - - /* - * 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 - { - String driverVersion = Driver.getVersion(); - if (Driver.logDebug) - Driver.debug("getDriverVersion " + driverVersion); - return driverVersion; - } - - /* - * What is this JDBC driver's major version number? - * - * @return the JDBC driver major version - */ - public int getDriverMajorVersion() - { - int majorVersion = connection.this_driver.getMajorVersion(); - if (Driver.logDebug) - Driver.debug("getMajorVersion " + majorVersion); - return majorVersion; - } - - /* - * What is this JDBC driver's minor version number? - * - * @return the JDBC driver minor version - */ - public int getDriverMinorVersion() - { - int minorVersion = connection.this_driver.getMinorVersion(); - if (Driver.logDebug) - Driver.debug("getMinorVersion " + minorVersion); - return minorVersion; - } - - /* - * 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 - { - if (Driver.logDebug) - Driver.debug("usesLocalFiles " + false); - 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 - { - if (Driver.logDebug) - Driver.debug("usesLocalFilePerTable " + false); - 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. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMixedCaseIdentifiers() throws SQLException - { - if (Driver.logDebug) - Driver.debug("supportsMixedCaseIdentifiers " + false); - 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 - { - if (Driver.logDebug) - Driver.debug("storesUpperCaseIdentifiers " + false); - 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 - { - if (Driver.logDebug) - Driver.debug("storesLowerCaseIdentifiers " + true); - 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 - { - if (Driver.logDebug) - Driver.debug("storesMixedCaseIdentifiers " + false); - 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. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException - { - if (Driver.logDebug) - Driver.debug("supportsMixedCaseQuotedIdentifiers " + true); - 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 - { - if (Driver.logDebug) - Driver.debug("storesUpperCaseQuotedIdentifiers " + false); - 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 - { - if (Driver.logDebug) - Driver.debug("storesLowerCaseQuotedIdentifiers " + false); - 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 - { - if (Driver.logDebug) - Driver.debug("storesMixedCaseQuotedIdentifiers " + false); - 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. - * - * @return the quoting string - * @exception SQLException if a database access error occurs - */ - public String getIdentifierQuoteString() throws SQLException - { - if (Driver.logDebug) - Driver.debug("getIdentifierQuoteString \"" ); - return "\""; - } - - /* - * Get a comma separated list of all a database's SQL keywords that - * are NOT also SQL92 keywords. - * - * <p>Within PostgreSQL, the keywords are found in - * src/backend/parser/keywords.c - * - * <p>For SQL Keywords, I took the list provided at - * <a href="http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt"> - * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt</a> - * 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 keywords; - } - - public String getNumericFunctions() throws SQLException - { - if (Driver.logDebug) - Driver.debug("getNumericFunctions"); - return ""; - } - - public String getStringFunctions() throws SQLException - { - if (Driver.logDebug) - Driver.debug("getStringFunctions"); - return ""; - } - - public String getSystemFunctions() throws SQLException - { - if (Driver.logDebug) - Driver.debug("getSystemFunctions"); - return ""; - } - - public String getTimeDateFunctions() throws SQLException - { - if (Driver.logDebug) - Driver.debug("getTimeDateFunctions"); - 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 - { - if (Driver.logDebug) - Driver.debug("getSearchStringEscape"); - return "\\"; - } - - /* - * Get all the "extra" characters that can be used in unquoted - * identifier names (those beyond a-zA-Z0-9 and _) - * - * <p>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 - { - if (Driver.logDebug) - Driver.debug("getExtraNameCharacters"); - return ""; - } - - /* - * 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 - { - if (Driver.logDebug) - Driver.debug("supportsAlterTableWithAddColumn " + true); - return true; - } - - /* - * Is "ALTER TABLE" with a drop column supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsAlterTableWithDropColumn() throws SQLException - { - boolean dropColumn = connection.haveMinimumServerVersion("7.3"); - if (Driver.logDebug) - Driver.debug("supportsAlterTableWithDropColumn " + dropColumn); - return dropColumn; - } - - /* - * Is column aliasing supported? - * - * <p>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. - * - * <p>e.g. - * - * <br><pre> - * select count(C) as C_COUNT from T group by C; - * - * </pre><br> - * 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 - { - if (Driver.logDebug) - Driver.debug("supportsColumnAliasing " + true); - 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 - { - if (Driver.logDebug) - Driver.debug("nullPlusNonNullIsNull " + true); - return true; - } - - public boolean supportsConvert() throws SQLException - { - if (Driver.logDebug) - Driver.debug("supportsConvert " + false); - return false; - } - - public boolean supportsConvert(int fromType, int toType) throws SQLException - { - if (Driver.logDebug) - Driver.debug("supportsConvert " + false); - return false; - } - - /* - * Are table correlation names supported? A JDBC Compliant - * driver always returns true. - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsTableCorrelationNames() throws SQLException - { - if (Driver.logDebug) - Driver.debug("supportsTableCorrelationNames " + true); - return true; - } - - /* - * If table correlation names are supported, are they restricted to - * be different from the names of the tables? - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsDifferentTableCorrelationNames() throws SQLException - { - if (Driver.logDebug) - Driver.debug("supportsDifferentTableCorrelationNames " + false); - return false; - } - - /* - * Are expressions in "ORDER BY" lists supported? - * - * <br>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 - { - if (Driver.logDebug) - Driver.debug("supportsExpressionsInOrderBy " + true); - return true; - } - - /* - * Can an "ORDER BY" clause use columns not in the SELECT? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOrderByUnrelated() throws SQLException - { - boolean supportsOrderByUnrelated = connection.haveMinimumServerVersion("6.4"); - if (Driver.logDebug) - Driver.debug("supportsOrderByUnrelated " + supportsOrderByUnrelated); - return supportsOrderByUnrelated; - } - - /* - * 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 - { - if (Driver.logDebug) - Driver.debug("supportsGroupBy " + true); - return true; - } - - /* - * Can a "GROUP BY" clause use columns not in the SELECT? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsGroupByUnrelated() throws SQLException - { - boolean supportsGroupByUnrelated = connection.haveMinimumServerVersion("6.4"); - if (Driver.logDebug) - Driver.debug("supportsGroupByUnrelated " + supportsGroupByUnrelated); - return supportsGroupByUnrelated; - } - - /* - * 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? - * - * (I think this is a subset of the previous function. -- petere) - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsGroupByBeyondSelect() throws SQLException - { - boolean supportsGroupByBeyondSelect = connection.haveMinimumServerVersion("6.4"); - if (Driver.logDebug) - Driver.debug("supportsGroupByUnrelated " + supportsGroupByBeyondSelect); - return supportsGroupByBeyondSelect; - } - - /* - * 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 - { - boolean supportsLikeEscapeClause = connection.haveMinimumServerVersion("7.1"); - if (Driver.logDebug) - Driver.debug("supportsLikeEscapeClause " + supportsLikeEscapeClause); - return supportsLikeEscapeClause; - } - - /* - * 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 - { - if (Driver.logDebug) - Driver.debug("supportsMultipleResultSets " + false); - 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 - { - if (Driver.logDebug) - Driver.debug("supportsMultipleTransactions " + true); - return true; - } - - /* - * Can columns be defined as non-nullable. A JDBC Compliant driver - * always returns true. - * - * <p>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 - { - if (Driver.logDebug) - Driver.debug("supportsNonNullableColumns true"); - return true; - } - - /* - * Does this driver support the minimum ODBC SQL grammar. This - * grammar is defined at: - * - * <p><a href="http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm">http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm</a> - * - * <p>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 - { - if (Driver.logDebug) - Driver.debug("supportsMinimumSQLGrammar TRUE"); - 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 - { - if (Driver.logDebug) - Driver.debug("supportsCoreSQLGrammar FALSE "); - 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 - { - if (Driver.logDebug) - Driver.debug("supportsExtendedSQLGrammar FALSE"); - return false; - } - - /* - * Does this driver support the ANSI-92 entry level SQL grammar? - * All JDBC Compliant drivers must return true. We currently - * report false until 'schema' support is added. Then this - * should be changed to return true, since we will be mostly - * compliant (probably more compliant than many other databases) - * And since this is a requirement for all JDBC drivers we - * need to get to the point where we can return true. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsANSI92EntryLevelSQL() throws SQLException - { - boolean schemas = connection.haveMinimumServerVersion("7.3"); - if (Driver.logDebug) - Driver.debug("supportsANSI92EntryLevelSQL " + schemas); - return schemas; - } - - /* - * Does this driver support the ANSI-92 intermediate level SQL - * grammar? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsANSI92IntermediateSQL() throws SQLException - { - if (Driver.logDebug) - Driver.debug("supportsANSI92IntermediateSQL false "); - 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 - { - if (Driver.logDebug) - Driver.debug("supportsANSI92FullSQL false "); - return false; - } - - /* - * Is the SQL Integrity Enhancement Facility supported? - * Our best guess is that this means support for constraints - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsIntegrityEnhancementFacility() throws SQLException - { - if (Driver.logDebug) - Driver.debug("supportsIntegrityEnhancementFacility true "); - return true; - } - - /* - * Is some form of outer join supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOuterJoins() throws SQLException - { - boolean supportsOuterJoins = connection.haveMinimumServerVersion("7.1"); - if (Driver.logDebug) - Driver.debug("supportsOuterJoins " + supportsOuterJoins); - return supportsOuterJoins; - } - - /* - * Are full nexted outer joins supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsFullOuterJoins() throws SQLException - { - boolean supportsFullOuterJoins = connection.haveMinimumServerVersion("7.1"); - if (Driver.logDebug) - Driver.debug("supportsFullOuterJoins " + supportsFullOuterJoins); - return supportsFullOuterJoins; - } - - /* - * Is there limited support for outer joins? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsLimitedOuterJoins() throws SQLException - { - boolean supportsLimitedOuterJoins = connection.haveMinimumServerVersion("7.1"); - if (Driver.logDebug) - Driver.debug("supportsFullOuterJoins " + supportsLimitedOuterJoins); - return supportsLimitedOuterJoins; - } - - /* - * What is the database vendor's preferred term for "schema"? - * PostgreSQL doesn't have schemas, but when it does, we'll use the - * term "schema". - * - * @return the vendor term - * @exception SQLException if a database access error occurs - */ - public String getSchemaTerm() throws SQLException - { - if (Driver.logDebug) - Driver.debug("getSchemaTerm schema"); - return "schema"; - } - - /* - * What is the database vendor's preferred term for "procedure"? - * Traditionally, "function" has been used. - * - * @return the vendor term - * @exception SQLException if a database access error occurs - */ - public String getProcedureTerm() throws SQLException - { - if (Driver.logDebug) - Driver.debug("getProcedureTerm function "); - return "function"; - } - - /* - * What is the database vendor's preferred term for "catalog"? - * - * @return the vendor term - * @exception SQLException if a database access error occurs - */ - public String getCatalogTerm() throws SQLException - { - if (Driver.logDebug) - Driver.debug("getCatalogTerm database "); - return "database"; - } - - /* - * 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 true here; we return false for every other catalog function - // so it won't matter what we return here D.C. - if (Driver.logDebug) - Driver.debug("isCatalogAtStart not implemented"); - return true; - } - - /* - * What is the Catalog separator. - * - * @return the catalog separator string - * @exception SQLException if a database access error occurs - */ - public String getCatalogSeparator() throws SQLException - { - // Give them something to work with here - // everything else returns false so it won't matter what we return here D.C. - if (Driver.logDebug) - Driver.debug("getCatalogSeparator not implemented "); - return "."; - } - - /* - * Can a schema name be used in a data manipulation statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInDataManipulation() throws SQLException - { - boolean schemas = connection.haveMinimumServerVersion("7.3"); - if (Driver.logDebug) - Driver.debug("supportsSchemasInDataManipulation "+schemas); - return schemas; - } - - /* - * Can a schema name be used in a procedure call statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInProcedureCalls() throws SQLException - { - boolean schemas = connection.haveMinimumServerVersion("7.3"); - if (Driver.logDebug) - Driver.debug("supportsSchemasInProcedureCalls "+schemas); - return schemas; - } - - /* - * Can a schema be used in a table definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInTableDefinitions() throws SQLException - { - boolean schemas = connection.haveMinimumServerVersion("7.3"); - - if (Driver.logDebug) - Driver.debug("supportsSchemasInTableDefinitions " + schemas); - return schemas; - } - - /* - * 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 - { - boolean schemas = connection.haveMinimumServerVersion("7.3"); - if (Driver.logDebug) - Driver.debug("supportsSchemasInIndexDefinitions "+schemas); - return schemas; - } - - /* - * 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 - { - boolean schemas = connection.haveMinimumServerVersion("7.3"); - if (Driver.logDebug) - Driver.debug("supportsSchemasInPrivilegeDefinitions "+schemas); - return schemas; - } - - /* - * 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 - { - if (Driver.logDebug) - Driver.debug("supportsCatalogsInDataManipulation false"); - 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 - { - if (Driver.logDebug) - Driver.debug("supportsCatalogsInDataManipulation false"); - 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 - { - if (Driver.logDebug) - Driver.debug("supportsCatalogsInTableDefinitions false"); - 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 - { - if (Driver.logDebug) - Driver.debug("supportsCatalogsInIndexDefinitions false"); - 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 - { - if (Driver.logDebug) - Driver.debug("supportsCatalogsInPrivilegeDefinitions false"); - 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 - { - if (Driver.logDebug) - Driver.debug("supportsPositionedDelete false"); - 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 - { - if (Driver.logDebug) - Driver.debug("supportsPositionedUpdate false"); - return false; // For now... - } - - /* - * Is SELECT for UPDATE supported? - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSelectForUpdate() throws SQLException - { - return connection.haveMinimumServerVersion("6.5"); - } - - /* - * Are stored procedure calls using the stored procedure escape - * syntax supported? - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsStoredProcedures() throws SQLException - { - return false; - } - - /* - * Are subqueries in comparison expressions supported? A JDBC - * Compliant driver always returns true. - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSubqueriesInComparisons() throws SQLException - { - return true; - } - - /* - * Are subqueries in 'exists' expressions supported? A JDBC - * Compliant driver always returns true. - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSubqueriesInExists() throws SQLException - { - return true; - } - - /* - * Are subqueries in 'in' statements supported? A JDBC - * Compliant driver always returns true. - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSubqueriesInIns() throws SQLException - { - return true; - } - - /* - * Are subqueries in quantified expressions supported? A JDBC - * Compliant driver always returns true. - * - * (No idea what this is, but we support a good deal of - * subquerying.) - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSubqueriesInQuantifieds() throws SQLException - { - return true; - } - - /* - * Are correlated subqueries supported? A JDBC Compliant driver - * always returns true. - * - * (a.k.a. subselect in from?) - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsCorrelatedSubqueries() throws SQLException - { - return connection.haveMinimumServerVersion("7.1"); - } - - /* - * Is SQL UNION supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsUnion() throws SQLException - { - return true; // since 6.3 - } - - /* - * Is SQL UNION ALL supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsUnionAll() throws SQLException - { - return connection.haveMinimumServerVersion("7.1"); - } - - /* - * 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 here, 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; // no limit - } - - /* - * 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 0; // no limit - } - - /* - * Whats the limit on column name length. - * - * @return the maximum column name length - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnNameLength() throws SQLException - { - return getMaxNameLength(); - } - - /* - * 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 0; // no limit - } - - /* - * What's the maximum number of columns allowed in an index? - * - * @return max number of columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInIndex() throws SQLException - { - return getMaxIndexKeys(); - } - - /* - * What's the maximum number of columns in an "ORDER BY clause? - * - * @return the max columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInOrderBy() throws SQLException - { - return 0; // no limit - } - - /* - * What is the maximum number of columns in a "SELECT" list? - * - * @return the max columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInSelect() throws SQLException - { - return 0; // no limit - } - - /* - * What is the maximum number of columns in a table? From the - * CREATE TABLE reference page... - * - * <p>"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 - * - * @return max cursor name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxCursorNameLength() throws SQLException - { - return getMaxNameLength(); - } - - /* - * Retrieves the maximum number of bytes for an index, including all - * of the parts of the index. - * - * @return max index length in bytes, which includes the composite - * of all the constituent parts of the index; a result of zero means - * that there is no limit or the limit is not known - * @exception SQLException if a database access error occurs - */ - public int getMaxIndexLength() throws SQLException - { - return 0; // no limit (larger than an int anyway) - } - - public int getMaxSchemaNameLength() throws SQLException - { - return getMaxNameLength(); - } - - /* - * What is the maximum length of a procedure name - * - * @return the max name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxProcedureNameLength() throws SQLException - { - return getMaxNameLength(); - } - - public int getMaxCatalogNameLength() throws SQLException - { - return getMaxNameLength(); - } - - /* - * What is the maximum length of a single row? - * - * @return max row size in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxRowSize() throws SQLException - { - if (connection.haveMinimumServerVersion("7.1")) - return 1073741824; // 1 GB - else - return 8192; // XXX could be altered - } - - /* - * 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 - { - if (connection.haveMinimumServerVersion("7.0")) - return 0; // actually whatever fits in size_t - else - return 16384; - } - - /* - * 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 - * - * @return max name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxTableNameLength() throws SQLException - { - return getMaxNameLength(); - } - - /* - * What is the maximum number of tables that can be specified - * in a SELECT? - * - * @return the maximum - * @exception SQLException if a database access error occurs - */ - public int getMaxTablesInSelect() throws SQLException - { - return 0; // no limit - } - - /* - * What is the maximum length of a user name - * - * @return the max name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxUserNameLength() throws SQLException - { - return getMaxNameLength(); - } - - - /* - * 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_READ_COMMITTED; - } - - /* - * 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 and TRANSACTION_READ_COMMITTED - * - * @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 || - level == Connection.TRANSACTION_READ_COMMITTED) - return true; - else - return false; - } - - /* - * Are both data definition and data manipulation transactions - * supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException - { - return true; - } - - /* - * 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 false; - } - - /* - * Does a data definition statement within a transaction force - * the transaction to commit? I think this means something like: - * - * <p><pre> - * 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; - * </pre><p> - * - * 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? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean dataDefinitionIgnoredInTransactions() throws SQLException - { - return false; - } - - /** - * Escape single quotes with another single quote. - */ - protected static String escapeQuotes(String s) { - StringBuffer sb = new StringBuffer(); - int length = s.length(); - char prevChar = ' '; - char prevPrevChar = ' '; - for (int i=0; i<length; i++) { - char c = s.charAt(i); - sb.append(c); - if (c == '\'' && (prevChar != '\\' || (prevChar == '\\' && prevPrevChar == '\\'))) { - sb.append("'"); - } - prevPrevChar = prevChar; - prevChar = c; - } - return sb.toString(); - } - - /* - * Get a description of stored procedures available in a catalog - * - * <p>Only procedure descriptions matching the schema and procedure - * name criteria are returned. They are ordered by PROCEDURE_SCHEM - * and PROCEDURE_NAME - * - * <p>Each procedure description has the following columns: - * <ol> - * <li><b>PROCEDURE_CAT</b> String => procedure catalog (may be null) - * <li><b>PROCEDURE_SCHEM</b> String => procedure schema (may be null) - * <li><b>PROCEDURE_NAME</b> String => procedure name - * <li><b>Field 4</b> reserved (make it null) - * <li><b>Field 5</b> reserved (make it null) - * <li><b>Field 6</b> reserved (make it null) - * <li><b>REMARKS</b> String => explanatory comment on the procedure - * <li><b>PROCEDURE_TYPE</b> short => kind of procedure - * <ul> - * <li> procedureResultUnknown - May return a result - * <li> procedureNoResult - Does not return a result - * <li> procedureReturnsResult - Returns a result - * </ul> - * </ol> - * - * @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 - { - String sql; - if (connection.haveMinimumServerVersion("7.3")) { - sql = "SELECT NULL AS PROCEDURE_CAT, n.nspname AS PROCEDURE_SCHEM, p.proname AS PROCEDURE_NAME, NULL, NULL, NULL, d.description AS REMARKS, "+java.sql.DatabaseMetaData.procedureReturnsResult+" AS PROCEDURE_TYPE "+ - " FROM pg_catalog.pg_namespace n, pg_catalog.pg_proc p "+ - " LEFT JOIN pg_catalog.pg_description d ON (p.oid=d.objoid) "+ - " LEFT JOIN pg_catalog.pg_class c ON (d.classoid=c.oid AND c.relname='pg_proc') "+ - " LEFT JOIN pg_catalog.pg_namespace pn ON (c.relnamespace=pn.oid AND pn.nspname='pg_catalog') "+ - " WHERE p.pronamespace=n.oid "; - if (schemaPattern != null && !"".equals(schemaPattern)) { - sql += " AND n.nspname LIKE '"+escapeQuotes(schemaPattern)+"' "; - } - if (procedureNamePattern != null) { - sql += " AND p.proname LIKE '"+escapeQuotes(procedureNamePattern)+"' "; - } - sql += " ORDER BY PROCEDURE_SCHEM, PROCEDURE_NAME "; - } else if (connection.haveMinimumServerVersion("7.1")) { - sql = "SELECT NULL AS PROCEDURE_CAT, NULL AS PROCEDURE_SCHEM, p.proname AS PROCEDURE_NAME, NULL, NULL, NULL, d.description AS REMARKS, "+java.sql.DatabaseMetaData.procedureReturnsResult+" AS PROCEDURE_TYPE "+ - " FROM pg_proc p "+ - " LEFT JOIN pg_description d ON (p.oid=d.objoid) "; - if (connection.haveMinimumServerVersion("7.2")) { - sql += " LEFT JOIN pg_class c ON (d.classoid=c.oid AND c.relname='pg_proc') "; - } - if (procedureNamePattern != null) { - sql += " WHERE p.proname LIKE '"+escapeQuotes(procedureNamePattern)+"' "; - } - sql += " ORDER BY PROCEDURE_NAME "; - } else { - sql = "SELECT NULL AS PROCEDURE_CAT, NULL AS PROCEDURE_SCHEM, p.proname AS PROCEDURE_NAME, NULL, NULL, NULL, NULL AS REMARKS, "+java.sql.DatabaseMetaData.procedureReturnsResult+" AS PROCEDURE_TYPE "+ - " FROM pg_proc p "; - if (procedureNamePattern != null) { - sql += " WHERE p.proname LIKE '"+escapeQuotes(procedureNamePattern)+"' "; - } - sql += " ORDER BY PROCEDURE_NAME "; - } - return connection.createStatement().executeQuery(sql); - } - - /* - * Get a description of a catalog's stored procedure parameters - * and result columns. - * - * <p>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. - * - * <p>Each row in the ResultSet is a parameter description or column - * description with the following fields: - * <ol> - * <li><b>PROCEDURE_CAT</b> String => procedure catalog (may be null) - * <li><b>PROCEDURE_SCHE</b>M String => procedure schema (may be null) - * <li><b>PROCEDURE_NAME</b> String => procedure name - * <li><b>COLUMN_NAME</b> String => column/parameter name - * <li><b>COLUMN_TYPE</b> Short => kind of column/parameter: - * <ul><li>procedureColumnUnknown - nobody knows - * <li>procedureColumnIn - IN parameter - * <li>procedureColumnInOut - INOUT parameter - * <li>procedureColumnOut - OUT parameter - * <li>procedureColumnReturn - procedure return value - * <li>procedureColumnResult - result column in ResultSet - * </ul> - * <li><b>DATA_TYPE</b> short => SQL type from java.sql.Types - * <li><b>TYPE_NAME</b> String => Data source specific type name - * <li><b>PRECISION</b> int => precision - * <li><b>LENGTH</b> int => length in bytes of data - * <li><b>SCALE</b> short => scale - * <li><b>RADIX</b> short => radix - * <li><b>NULLABLE</b> short => can it contain NULL? - * <ul><li>procedureNoNulls - does not allow NULL values - * <li>procedureNullable - allows NULL values - * <li>procedureNullableUnknown - nullability unknown - * <li><b>REMARKS</b> String => comment describing parameter/column - * </ol> - * @param catalog This is ignored in org.postgresql, advise this is set to null - * @param schemaPattern - * @param procedureNamePattern a procedure name pattern - * @param columnNamePattern a column name pattern, this is currently ignored because postgresql does not name procedure parameters. - * @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 - { - Field f[] = new Field[13]; - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, getMaxNameLength()); - f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, getMaxNameLength()); - f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, getMaxNameLength()); - f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, getMaxNameLength()); - f[4] = new Field(connection, "COLUMN_TYPE", iInt2Oid, 2); - f[5] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[6] = new Field(connection, "TYPE_NAME", iVarcharOid, getMaxNameLength()); - f[7] = new Field(connection, "PRECISION", iInt4Oid, 4); - f[8] = new Field(connection, "LENGTH", iInt4Oid, 4); - f[9] = new Field(connection, "SCALE", iInt2Oid, 2); - f[10] = new Field(connection, "RADIX", iInt2Oid, 2); - f[11] = new Field(connection, "NULLABLE", iInt2Oid, 2); - f[12] = new Field(connection, "REMARKS", iVarcharOid, getMaxNameLength()); - - String sql; - if (connection.haveMinimumServerVersion("7.3")) { - sql = "SELECT n.nspname,p.proname,p.prorettype,p.proargtypes, t.typtype,t.typrelid "+ - " FROM pg_catalog.pg_proc p,pg_catalog.pg_namespace n, pg_catalog.pg_type t "+ - " WHERE p.pronamespace=n.oid AND p.prorettype=t.oid "; - if (schemaPattern != null && !"".equals(schemaPattern)) { - sql += " AND n.nspname LIKE '"+escapeQuotes(schemaPattern)+"' "; - } - if (procedureNamePattern != null) { - sql += " AND p.proname LIKE '"+escapeQuotes(procedureNamePattern)+"' "; - } - sql += " ORDER BY n.nspname, p.proname "; - } else { - sql = "SELECT NULL AS nspname,p.proname,p.prorettype,p.proargtypes, t.typtype,t.typrelid "+ - " FROM pg_proc p,pg_type t "+ - " WHERE p.prorettype=t.oid "; - if (procedureNamePattern != null) { - sql += " AND p.proname LIKE '"+escapeQuotes(procedureNamePattern)+"' "; - } - sql += " ORDER BY p.proname "; - } - - ResultSet rs = connection.createStatement().executeQuery(sql); - while (rs.next()) { - byte schema[] = rs.getBytes("nspname"); - byte procedureName[] = rs.getBytes("proname"); - int returnType = rs.getInt("prorettype"); - String returnTypeType = rs.getString("typtype"); - int returnTypeRelid = rs.getInt("typrelid"); - String strArgTypes = rs.getString("proargtypes"); - StringTokenizer st = new StringTokenizer(strArgTypes); - Vector argTypes = new Vector(); - while (st.hasMoreTokens()) { - argTypes.addElement(new Integer(st.nextToken())); - } - - // decide if we are returning a single column result. - if (!returnTypeType.equals("c")) { - byte[][] tuple = new byte[13][]; - tuple[0] = null; - tuple[1] = schema; - tuple[2] = procedureName; - tuple[3] = encoding.encode("returnValue"); - tuple[4] = encoding.encode(Integer.toString(java.sql.DatabaseMetaData.procedureColumnReturn)); - tuple[5] = encoding.encode(Integer.toString(connection.getSQLType(returnType))); - tuple[6] = encoding.encode(connection.getPGType(returnType)); - tuple[7] = null; - tuple[8] = null; - tuple[9] = null; - tuple[10] = null; - tuple[11] = encoding.encode(Integer.toString(java.sql.DatabaseMetaData.procedureNullableUnknown)); - tuple[12] = null; - v.addElement(tuple); - } - - // Add a row for each argument. - for (int i=0; i<argTypes.size(); i++) { - int argOid = ((Integer)argTypes.elementAt(i)).intValue(); - byte[][] tuple = new byte[13][]; - tuple[0] = null; - tuple[1] = schema; - tuple[2] = procedureName; - tuple[3] = encoding.encode("$"+(i+1)); - tuple[4] = encoding.encode(Integer.toString(java.sql.DatabaseMetaData.procedureColumnIn)); - tuple[5] = encoding.encode(Integer.toString(connection.getSQLType(argOid))); - tuple[6] = encoding.encode(connection.getPGType(argOid)); - tuple[7] = null; - tuple[8] = null; - tuple[9] = null; - tuple[10] = null; - tuple[11] = encoding.encode(Integer.toString(java.sql.DatabaseMetaData.procedureNullableUnknown)); - tuple[12] = null; - v.addElement(tuple); - } - - // if we are returning a multi-column result. - if (returnTypeType.equals("c")) { - String columnsql = "SELECT a.attname,a.atttypid FROM pg_catalog.pg_attribute a WHERE a.attrelid = "+returnTypeRelid+" ORDER BY a.attnum "; - ResultSet columnrs = connection.createStatement().executeQuery(columnsql); - while (columnrs.next()) { - int columnTypeOid = columnrs.getInt("atttypid"); - byte[][] tuple = new byte[13][]; - tuple[0] = null; - tuple[1] = schema; - tuple[2] = procedureName; - tuple[3] = columnrs.getBytes("attname"); - tuple[4] = encoding.encode(Integer.toString(java.sql.DatabaseMetaData.procedureColumnResult)); - tuple[5] = encoding.encode(Integer.toString(connection.getSQLType(columnTypeOid))); - tuple[6] = encoding.encode(connection.getPGType(columnTypeOid)); - tuple[7] = null; - tuple[8] = null; - tuple[9] = null; - tuple[10] = null; - tuple[11] = encoding.encode(Integer.toString(java.sql.DatabaseMetaData.procedureNullableUnknown)); - tuple[12] = null; - v.addElement(tuple); - } - } - } - rs.close(); - - return (ResultSet) ((BaseStatement)connection.createStatement()).createResultSet(f, v, "OK", 1, 0, false); - } - - /* - * Get a description of tables available in a catalog. - * - * <p>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. - * - * <p>Each table description has the following columns: - * - * <ol> - * <li><b>TABLE_CAT</b> String => table catalog (may be null) - * <li><b>TABLE_SCHEM</b> String => table schema (may be null) - * <li><b>TABLE_NAME</b> String => table name - * <li><b>TABLE_TYPE</b> String => table type. Typical types are "TABLE", - * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL - * TEMPORARY", "ALIAS", "SYNONYM". - * <li><b>REMARKS</b> String => explanatory comment on the table - * </ol> - * - * <p>The valid values for the types parameter are: - * "TABLE", "INDEX", "SEQUENCE", "VIEW", - * "SYSTEM TABLE", "SYSTEM INDEX", "SYSTEM VIEW", - * "SYSTEM TOAST TABLE", "SYSTEM TOAST INDEX", - * "TEMPORARY TABLE", and "TEMPORARY VIEW" - * - * @param catalog a catalog name; For org.postgresql, this is ignored, and - * should be set to null - * @param schemaPattern a schema name pattern - * @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 - { - String select; - String orderby; - String useSchemas; - if (connection.haveMinimumServerVersion("7.3")) { - useSchemas = "SCHEMAS"; - select = "SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM, c.relname AS TABLE_NAME, "+ - " CASE n.nspname LIKE 'pg\\\\_%' OR n.nspname = 'information_schema' "+ - " WHEN true THEN CASE "+ - " WHEN n.nspname = 'pg_catalog' OR n.nspname = 'information_schema' THEN CASE c.relkind "+ - " WHEN 'r' THEN 'SYSTEM TABLE' "+ - " WHEN 'v' THEN 'SYSTEM VIEW' "+ - " WHEN 'i' THEN 'SYSTEM INDEX' "+ - " ELSE NULL "+ - " END "+ - " WHEN n.nspname = 'pg_toast' THEN CASE c.relkind "+ - " WHEN 'r' THEN 'SYSTEM TOAST TABLE' "+ - " WHEN 'i' THEN 'SYSTEM TOAST INDEX' "+ - " ELSE NULL "+ - " END "+ - " ELSE CASE c.relkind "+ - " WHEN 'r' THEN 'TEMPORARY TABLE' "+ - " WHEN 'i' THEN 'TEMPORARY INDEX' "+ - " ELSE NULL "+ - " END "+ - " END "+ - " WHEN false THEN CASE c.relkind "+ - " WHEN 'r' THEN 'TABLE' "+ - " WHEN 'i' THEN 'INDEX' "+ - " WHEN 'S' THEN 'SEQUENCE' "+ - " WHEN 'v' THEN 'VIEW' "+ - " ELSE NULL "+ - " END "+ - " ELSE NULL "+ - " END "+ - " AS TABLE_TYPE, d.description AS REMARKS "+ - " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class c "+ - " LEFT JOIN pg_catalog.pg_description d ON (c.oid = d.objoid AND d.objsubid = 0) "+ - " LEFT JOIN pg_catalog.pg_class dc ON (d.classoid=dc.oid AND dc.relname='pg_class') "+ - " LEFT JOIN pg_catalog.pg_namespace dn ON (dn.oid=dc.relnamespace AND dn.nspname='pg_catalog') "+ - " WHERE c.relnamespace = n.oid "; - if (schemaPattern != null && !"".equals(schemaPattern)) { - select += " AND n.nspname LIKE '"+escapeQuotes(schemaPattern)+"' "; - } - orderby = " ORDER BY TABLE_TYPE,TABLE_SCHEM,TABLE_NAME "; - } else { - useSchemas = "NOSCHEMAS"; - String tableType = ""+ - " CASE c.relname LIKE 'pg\\\\_%' "+ - " WHEN true THEN CASE c.relname LIKE 'pg\\\\_toast\\\\_%' "+ - " WHEN true THEN CASE c.relkind "+ - " WHEN 'r' THEN 'SYSTEM TOAST TABLE' "+ - " WHEN 'i' THEN 'SYSTEM TOAST INDEX' "+ - " ELSE NULL "+ - " END "+ - " WHEN false THEN CASE c.relname LIKE 'pg\\\\_temp\\\\_%' "+ - " WHEN true THEN CASE c.relkind "+ - " WHEN 'r' THEN 'TEMPORARY TABLE' "+ - " WHEN 'i' THEN 'TEMPORARY INDEX' "+ - " ELSE NULL "+ - " END "+ - " WHEN false THEN CASE c.relkind "+ - " WHEN 'r' THEN 'SYSTEM TABLE' "+ - " WHEN 'v' THEN 'SYSTEM VIEW' "+ - " WHEN 'i' THEN 'SYSTEM INDEX' "+ - " ELSE NULL "+ - " END "+ - " ELSE NULL "+ - " END "+ - " ELSE NULL "+ - " END "+ - " WHEN false THEN CASE c.relkind "+ - " WHEN 'r' THEN 'TABLE' "+ - " WHEN 'i' THEN 'INDEX' "+ - " WHEN 'S' THEN 'SEQUENCE' "+ - " WHEN 'v' THEN 'VIEW' "+ - " ELSE NULL "+ - " END "+ - " ELSE NULL "+ - " END "; - orderby = " ORDER BY TABLE_TYPE,TABLE_NAME "; - if (connection.haveMinimumServerVersion("7.2")) { - select = "SELECT NULL AS TABLE_CAT, NULL AS TABLE_SCHEM, c.relname AS TABLE_NAME, "+tableType+" AS TABLE_TYPE, d.description AS REMARKS "+ - " FROM pg_class c "+ - " LEFT JOIN pg_description d ON (c.oid=d.objoid AND d.objsubid = 0) "+ - " LEFT JOIN pg_class dc ON (d.classoid = dc.oid AND dc.relname='pg_class') "+ - " WHERE true "; - } else if (connection.haveMinimumServerVersion("7.1")) { - select = "SELECT NULL AS TABLE_CAT, NULL AS TABLE_SCHEM, c.relname AS TABLE_NAME, "+tableType+" AS TABLE_TYPE, d.description AS REMARKS "+ - " FROM pg_class c "+ - " LEFT JOIN pg_description d ON (c.oid=d.objoid) "+ - " WHERE true "; - } else { - select = "SELECT NULL AS TABLE_CAT, NULL AS TABLE_SCHEM, c.relname AS TABLE_NAME, "+tableType+" AS TABLE_TYPE, NULL AS REMARKS "+ - " FROM pg_class c "+ - " WHERE true "; - } - } - - if (types == null) { - types = defaultTableTypes; - } - if (tableNamePattern != null) { - select += " AND c.relname LIKE '"+escapeQuotes(tableNamePattern)+"' "; - } - String sql = select; - sql += " AND (false "; - for (int i=0; i<types.length; i++) { - Hashtable clauses = (Hashtable)tableTypeClauses.get(types[i]); - if (clauses != null) { - String clause = (String)clauses.get(useSchemas); - sql += " OR ( "+clause+" ) "; - } - } - sql += ") "; - sql += orderby; - - return connection.createStatement().executeQuery(sql); - } - - private static final Hashtable tableTypeClauses; - static { - tableTypeClauses = new Hashtable(); - Hashtable ht = new Hashtable(); - tableTypeClauses.put("TABLE",ht); - ht.put("SCHEMAS","c.relkind = 'r' AND n.nspname NOT LIKE 'pg\\\\_%' AND n.nspname <> 'information_schema'"); - ht.put("NOSCHEMAS","c.relkind = 'r' AND c.relname NOT LIKE 'pg\\\\_%'"); - ht = new Hashtable(); - tableTypeClauses.put("VIEW",ht); - ht.put("SCHEMAS","c.relkind = 'v' AND n.nspname <> 'pg_catalog' AND n.nspname <> 'information_schema'"); - ht.put("NOSCHEMAS","c.relkind = 'v' AND c.relname NOT LIKE 'pg\\\\_%'"); - ht = new Hashtable(); - tableTypeClauses.put("INDEX",ht); - ht.put("SCHEMAS","c.relkind = 'i' AND n.nspname NOT LIKE 'pg\\\\_%' AND n.nspname <> 'information_schema'"); - ht.put("NOSCHEMAS","c.relkind = 'i' AND c.relname NOT LIKE 'pg\\\\_%'"); - ht = new Hashtable(); - tableTypeClauses.put("SEQUENCE",ht); - ht.put("SCHEMAS","c.relkind = 'S'"); - ht.put("NOSCHEMAS","c.relkind = 'S'"); - ht = new Hashtable(); - tableTypeClauses.put("SYSTEM TABLE",ht); - ht.put("SCHEMAS","c.relkind = 'r' AND (n.nspname = 'pg_catalog' OR n.nspname = 'information_schema')"); - ht.put("NOSCHEMAS","c.relkind = 'r' AND c.relname LIKE 'pg\\\\_%' AND c.relname NOT LIKE 'pg\\\\_toast\\\\_%' AND c.relname NOT LIKE 'pg\\\\_temp\\\\_%'"); - ht = new Hashtable(); - tableTypeClauses.put("SYSTEM TOAST TABLE",ht); - ht.put("SCHEMAS","c.relkind = 'r' AND n.nspname = 'pg_toast'"); - ht.put("NOSCHEMAS","c.relkind = 'r' AND c.relname LIKE 'pg\\\\_toast\\\\_%'"); - ht = new Hashtable(); - tableTypeClauses.put("SYSTEM TOAST INDEX",ht); - ht.put("SCHEMAS","c.relkind = 'i' AND n.nspname = 'pg_toast'"); - ht.put("NOSCHEMAS","c.relkind = 'i' AND c.relname LIKE 'pg\\\\_toast\\\\_%'"); - ht = new Hashtable(); - tableTypeClauses.put("SYSTEM VIEW",ht); - ht.put("SCHEMAS","c.relkind = 'v' AND (n.nspname = 'pg_catalog' OR n.nspname = 'information_schema') "); - ht.put("NOSCHEMAS","c.relkind = 'v' AND c.relname LIKE 'pg\\\\_%'"); - ht = new Hashtable(); - tableTypeClauses.put("SYSTEM INDEX",ht); - ht.put("SCHEMAS","c.relkind = 'i' AND (n.nspname = 'pg_catalog' OR n.nspname = 'information_schema') "); - ht.put("NOSCHEMAS","c.relkind = 'v' AND c.relname LIKE 'pg\\\\_%' AND c.relname NOT LIKE 'pg\\\\_toast\\\\_%' AND c.relname NOT LIKE 'pg\\\\_temp\\\\_%'"); - ht = new Hashtable(); - tableTypeClauses.put("TEMPORARY TABLE",ht); - ht.put("SCHEMAS","c.relkind = 'r' AND n.nspname LIKE 'pg\\\\_temp\\\\_%' "); - ht.put("NOSCHEMAS","c.relkind = 'r' AND c.relname LIKE 'pg\\\\_temp\\\\_%' "); - ht = new Hashtable(); - tableTypeClauses.put("TEMPORARY INDEX",ht); - ht.put("SCHEMAS","c.relkind = 'i' AND n.nspname LIKE 'pg\\\\_temp\\\\_%' "); - ht.put("NOSCHEMAS","c.relkind = 'i' AND c.relname LIKE 'pg\\\\_temp\\\\_%' "); - } - - // 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", "VIEW", "INDEX", "SEQUENCE", "TEMPORARY TABLE" - }; - - /* - * Get the schema names available in this database. The results - * are ordered by schema name. - * - * <P>The schema column is: - * <OL> - * <LI><B>TABLE_SCHEM</B> String => schema name - * </OL> - * - * @return ResultSet each row has a single String column that is a - * schema name - */ - public java.sql.ResultSet getSchemas() throws SQLException - { - String sql; - if (connection.haveMinimumServerVersion("7.3")) { - sql = "SELECT nspname AS TABLE_SCHEM FROM pg_catalog.pg_namespace WHERE nspname <> 'pg_toast' AND nspname NOT LIKE 'pg\\\\_temp\\\\_%' ORDER BY TABLE_SCHEM"; - } else { - sql = "SELECT ''::text AS TABLE_SCHEM ORDER BY TABLE_SCHEM"; - } - return connection.createStatement().executeQuery(sql); - } - - /* - * Get the catalog names available in this database. The results - * are ordered by catalog name. - * - * <P>The catalog column is: - * <OL> - * <LI><B>TABLE_CAT</B> String => catalog name - * </OL> - * - * @return ResultSet each row has a single String column that is a - * catalog name - */ - public java.sql.ResultSet getCatalogs() throws SQLException - { - String sql; - if (connection.haveMinimumServerVersion("7.3")) { - sql = "SELECT datname AS TABLE_CAT FROM pg_catalog.pg_database ORDER BY TABLE_CAT"; - } else { - sql = "SELECT datname AS TABLE_CAT FROM pg_database ORDER BY TABLE_CAT"; - } - return connection.createStatement().executeQuery(sql); - } - - /* - * Get the table types available in this database. The results - * are ordered by table type. - * - * <P>The table type is: - * <OL> - * <LI><B>TABLE_TYPE</B> String => table type. Typical types are "TABLE", - * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", - * "LOCAL TEMPORARY", "ALIAS", "SYNONYM". - * </OL> - * - * @return ResultSet each row has a single String column that is a - * table type - */ - public java.sql.ResultSet getTableTypes() throws SQLException - { - String types[] = new String[tableTypeClauses.size()]; - Enumeration e = tableTypeClauses.keys(); - int i=0; - while (e.hasMoreElements()) { - types[i++] = (String)e.nextElement();} - sortStringArray(types); - - Field f[] = new Field[1]; - Vector v = new Vector(); - f[0] = new Field(connection, new String("TABLE_TYPE"), iVarcharOid, getMaxNameLength()); - for (i=0; i < types.length; i++) - { - byte[][] tuple = new byte[1][]; - tuple[0] = encoding.encode(types[i]); - v.addElement(tuple); - } - - return (ResultSet) ((BaseStatement)connection.createStatement()).createResultSet(f, v, "OK", 1, 0, false); - } - - /* - * Get a description of table columns available in a catalog. - * - * <P>Only column descriptions matching the catalog, schema, table - * and column name criteria are returned. They are ordered by - * TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION. - * - * <P>Each column description has the following columns: - * <OL> - * <LI><B>TABLE_CAT</B> String => table catalog (may be null) - * <LI><B>TABLE_SCHEM</B> String => table schema (may be null) - * <LI><B>TABLE_NAME</B> String => table name - * <LI><B>COLUMN_NAME</B> String => column name - * <LI><B>DATA_TYPE</B> short => SQL type from java.sql.Types - * <LI><B>TYPE_NAME</B> String => Data source dependent type name - * <LI><B>COLUMN_SIZE</B> int => column size. For char or date - * types this is the maximum number of characters, for numeric or - * decimal types this is precision. - * <LI><B>BUFFER_LENGTH</B> is not used. - * <LI><B>DECIMAL_DIGITS</B> int => the number of fractional digits - * <LI><B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2) - * <LI><B>NULLABLE</B> int => is NULL allowed? - * <UL> - * <LI> columnNoNulls - might not allow NULL values - * <LI> columnNullable - definitely allows NULL values - * <LI> columnNullableUnknown - nullability unknown - * </UL> - * <LI><B>REMARKS</B> String => comment describing column (may be null) - * <LI><B>COLUMN_DEF</B> String => default value (may be null) - * <LI><B>SQL_DATA_TYPE</B> int => unused - * <LI><B>SQL_DATETIME_SUB</B> int => unused - * <LI><B>CHAR_OCTET_LENGTH</B> int => for char types the - * maximum number of bytes in the column - * <LI><B>ORDINAL_POSITION</B> int => index of column in table - * (starting at 1) - * <LI><B>IS_NULLABLE</B> String => "NO" means column definitely - * does not allow NULL values; "YES" means the column might - * allow NULL values. An empty string means nobody knows. - * </OL> - * - * @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 - { - Vector v = new Vector(); // The new ResultSet tuple stuff - Field f[] = new Field[18]; // The field descriptors for the new ResultSet - - f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, getMaxNameLength()); - f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, getMaxNameLength()); - f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, getMaxNameLength()); - f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, getMaxNameLength()); - f[4] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[5] = new Field(connection, "TYPE_NAME", iVarcharOid, getMaxNameLength()); - f[6] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); - f[7] = new Field(connection, "BUFFER_LENGTH", iVarcharOid, getMaxNameLength()); - f[8] = new Field(connection, "DECIMAL_DIGITS", iInt4Oid, 4); - f[9] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4); - f[10] = new Field(connection, "NULLABLE", iInt4Oid, 4); - f[11] = new Field(connection, "REMARKS", iVarcharOid, getMaxNameLength()); - f[12] = new Field(connection, "COLUMN_DEF", iVarcharOid, getMaxNameLength()); - f[13] = new Field(connection, "SQL_DATA_TYPE", iInt4Oid, 4); - f[14] = new Field(connection, "SQL_DATETIME_SUB", iInt4Oid, 4); - f[15] = new Field(connection, "CHAR_OCTET_LENGTH", iVarcharOid, getMaxNameLength()); - f[16] = new Field(connection, "ORDINAL_POSITION", iInt4Oid, 4); - f[17] = new Field(connection, "IS_NULLABLE", iVarcharOid, getMaxNameLength()); - - String sql; - if (connection.haveMinimumServerVersion("7.3")) { - sql = "SELECT n.nspname,c.relname,a.attname,a.atttypid,a.attnotnull,a.atttypmod,a.attlen,a.attnum,def.adsrc,dsc.description "+ - " FROM pg_catalog.pg_namespace n "+ - " JOIN pg_catalog.pg_class c ON (c.relnamespace = n.oid) "+ - " JOIN pg_catalog.pg_attribute a ON (a.attrelid=c.oid) "+ - " LEFT JOIN pg_catalog.pg_attrdef def ON (a.attrelid=def.adrelid AND a.attnum = def.adnum) "+ - " LEFT JOIN pg_catalog.pg_description dsc ON (c.oid=dsc.objoid AND a.attnum = dsc.objsubid) "+ - " LEFT JOIN pg_catalog.pg_class dc ON (dc.oid=dsc.classoid AND dc.relname='pg_class') "+ - " LEFT JOIN pg_catalog.pg_namespace dn ON (dc.relnamespace=dn.oid AND dn.nspname='pg_catalog') "+ - " WHERE a.attnum > 0 AND NOT a.attisdropped "; - if (schemaPattern != null && !"".equals(schemaPattern)) { - sql += " AND n.nspname LIKE '"+escapeQuotes(schemaPattern)+"' "; - } - } else if (connection.haveMinimumServerVersion("7.2")) { - sql = "SELECT NULL::text AS nspname,c.relname,a.attname,a.atttypid,a.attnotnull,a.atttypmod,a.attlen,a.attnum,def.adsrc,dsc.description "+ - " FROM pg_class c "+ - " JOIN pg_attribute a ON (a.attrelid=c.oid) "+ - " LEFT JOIN pg_attrdef def ON (a.attrelid=def.adrelid AND a.attnum = def.adnum) "+ - " LEFT JOIN pg_description dsc ON (c.oid=dsc.objoid AND a.attnum = dsc.objsubid) "+ - " LEFT JOIN pg_class dc ON (dc.oid=dsc.classoid AND dc.relname='pg_class') "+ - " WHERE a.attnum > 0 "; - } else if (connection.haveMinimumServerVersion("7.1")) { - sql = "SELECT NULL::text AS nspname,c.relname,a.attname,a.atttypid,a.attnotnull,a.atttypmod,a.attlen,a.attnum,def.adsrc,dsc.description "+ - " FROM pg_class c "+ - " JOIN pg_attribute a ON (a.attrelid=c.oid) "+ - " LEFT JOIN pg_attrdef def ON (a.attrelid=def.adrelid AND a.attnum = def.adnum) "+ - " LEFT JOIN pg_description dsc ON (a.oid=dsc.objoid) "+ - " WHERE a.attnum > 0 "; - } else { - // if < 7.1 then don't get defaults or descriptions. - sql = "SELECT NULL::text AS nspname,c.relname,a.attname,a.atttypid,a.attnotnull,a.atttypmod,a.attlen,a.attnum,NULL AS adsrc,NULL AS description "+ - " FROM pg_class c, pg_attribute a "+ - " WHERE a.attrelid=c.oid AND a.attnum > 0 "; - } - - if (tableNamePattern != null && !"".equals(tableNamePattern)) { - sql += " AND c.relname LIKE '"+escapeQuotes(tableNamePattern)+"' "; - } - if (columnNamePattern != null && !"".equals(columnNamePattern)) { - sql += " AND a.attname LIKE '"+escapeQuotes(columnNamePattern)+"' "; - } - sql += " ORDER BY nspname,relname,attnum "; - - ResultSet rs = connection.createStatement().executeQuery(sql); - while (rs.next()) - { - byte[][] tuple = new byte[18][]; - int typeOid = rs.getInt("atttypid"); - - tuple[0] = null; // Catalog name, not supported - tuple[1] = rs.getBytes("nspname"); // Schema - tuple[2] = rs.getBytes("relname"); // Table name - tuple[3] = rs.getBytes("attname"); // Column name - tuple[4] = encoding.encode(Integer.toString(connection.getSQLType(typeOid))); - String pgType = connection.getPGType(typeOid); - tuple[5] = encoding.encode(pgType); // Type name - - // by default no decimal_digits - // if the type is numeric or decimal we will - // overwrite later. - tuple[8] = encoding.encode("0"); - - if (pgType.equals("bpchar") || pgType.equals("varchar")) - { - int atttypmod = rs.getInt("atttypmod"); - tuple[6] = encoding.encode(Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0)); - } - else if (pgType.equals("numeric") || pgType.equals("decimal")) - { - int attypmod = rs.getInt("atttypmod") - VARHDRSZ; - tuple[6] = encoding.encode(Integer.toString( ( attypmod >> 16 ) & 0xffff )); - tuple[8] = encoding.encode(Integer.toString(attypmod & 0xffff)); - tuple[9] = encoding.encode("10"); - } - else if (pgType.equals("bit") || pgType.equals("varbit")) { - tuple[6] = rs.getBytes("atttypmod"); - tuple[9] = encoding.encode("2"); - } - else { - tuple[6] = rs.getBytes("attlen"); - tuple[9] = encoding.encode("10"); - } - - tuple[7] = null; // Buffer length - - tuple[10] = encoding.encode(Integer.toString(rs.getBoolean("attnotnull") ? java.sql.DatabaseMetaData.columnNoNulls : java.sql.DatabaseMetaData.columnNullable)); // Nullable - tuple[11] = rs.getBytes("description"); // Description (if any) - tuple[12] = rs.getBytes("adsrc"); // 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] = rs.getBytes("attnum"); // ordinal position - tuple[17] = encoding.encode(rs.getBoolean("attnotnull") ? "NO" : "YES"); // Is nullable - - v.addElement(tuple); - } - rs.close(); - - return (ResultSet) ((BaseStatement)connection.createStatement()).createResultSet(f, v, "OK", 1, 0, false); - } - - /* - * Get a description of the access rights for a table's columns. - * - * <P>Only privileges matching the column name criteria are - * returned. They are ordered by COLUMN_NAME and PRIVILEGE. - * - * <P>Each privilige description has the following columns: - * <OL> - * <LI><B>TABLE_CAT</B> String => table catalog (may be null) - * <LI><B>TABLE_SCHEM</B> String => table schema (may be null) - * <LI><B>TABLE_NAME</B> String => table name - * <LI><B>COLUMN_NAME</B> String => column name - * <LI><B>GRANTOR</B> => grantor of access (may be null) - * <LI><B>GRANTEE</B> String => grantee of access - * <LI><B>PRIVILEGE</B> String => name of access (SELECT, - * INSERT, UPDATE, REFRENCES, ...) - * <LI><B>IS_GRANTABLE</B> String => "YES" if grantee is permitted - * to grant to others; "NO" if not; null if unknown - * </OL> - * - * @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 = "%"; - - f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, getMaxNameLength()); - f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, getMaxNameLength()); - f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, getMaxNameLength()); - f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, getMaxNameLength()); - f[4] = new Field(connection, "GRANTOR", iVarcharOid, getMaxNameLength()); - f[5] = new Field(connection, "GRANTEE", iVarcharOid, getMaxNameLength()); - f[6] = new Field(connection, "PRIVILEGE", iVarcharOid, getMaxNameLength()); - f[7] = new Field(connection, "IS_GRANTABLE", iVarcharOid, getMaxNameLength()); - - String sql; - if (connection.haveMinimumServerVersion("7.3")) { - sql = "SELECT n.nspname,c.relname,u.usename,c.relacl,a.attname "+ - " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_attribute a "+ - " WHERE c.relnamespace = n.oid "+ - " AND u.usesysid = c.relowner "+ - " AND c.oid = a.attrelid "+ - " AND c.relkind = 'r' "+ - " AND a.attnum > 0 AND NOT a.attisdropped "; - if (schema != null && !"".equals(schema)) { - sql += " AND n.nspname = '"+escapeQuotes(schema)+"' "; - } - } else { - sql = "SELECT NULL::text AS nspname,c.relname,u.usename,c.relacl,a.attname "+ - "FROM pg_class c, pg_user u,pg_attribute a "+ - " WHERE u.usesysid = c.relowner "+ - " AND c.oid = a.attrelid "+ - " AND a.attnum > 0 "+ - " AND c.relkind = 'r' "; - } - - sql += " AND c.relname = '"+escapeQuotes(table)+"' "; - if (columnNamePattern != null && !"".equals(columnNamePattern)) { - sql += " AND a.attname LIKE '"+escapeQuotes(columnNamePattern)+"' "; - } - sql += " ORDER BY attname "; - - ResultSet rs = connection.createStatement().executeQuery(sql); - while (rs.next()) { - byte schemaName[] = rs.getBytes("nspname"); - byte tableName[] = rs.getBytes("relname"); - byte column[] = rs.getBytes("attname"); - String owner = rs.getString("usename"); - String acl = rs.getString("relacl"); - Hashtable permissions = parseACL(acl, owner); - String permNames[] = new String[permissions.size()]; - Enumeration e = permissions.keys(); - int i=0; - while (e.hasMoreElements()) { - permNames[i++] = (String)e.nextElement(); - } - sortStringArray(permNames); - for (i=0; i<permNames.length; i++) { - byte[] privilege = encoding.encode(permNames[i]); - Vector grantees = (Vector)permissions.get(permNames[i]); - for (int j=0; j<grantees.size(); j++) { - String grantee = (String)grantees.elementAt(j); - String grantable = owner.equals(grantee) ? "YES" : "NO"; - byte[][] tuple = new byte[8][]; - tuple[0] = null; - tuple[1] = schemaName; - tuple[2] = tableName; - tuple[3] = column; - tuple[4] = encoding.encode(owner); - tuple[5] = encoding.encode(grantee); - tuple[6] = privilege; - tuple[7] = encoding.encode(grantable); - v.addElement(tuple); - } - } - } - rs.close(); - - return (ResultSet) ((BaseStatement)connection.createStatement()).createResultSet(f, v, "OK", 1, 0, false); - } - - /* - * Get a description of the access rights for each table available - * in a catalog. - * - * This method is currently unimplemented. - * - * <P>Only privileges matching the schema and table name - * criteria are returned. They are ordered by TABLE_SCHEM, - * TABLE_NAME, and PRIVILEGE. - * - * <P>Each privilige description has the following columns: - * <OL> - * <LI><B>TABLE_CAT</B> String => table catalog (may be null) - * <LI><B>TABLE_SCHEM</B> String => table schema (may be null) - * <LI><B>TABLE_NAME</B> String => table name - * <LI><B>GRANTOR</B> => grantor of access (may be null) - * <LI><B>GRANTEE</B> String => grantee of access - * <LI><B>PRIVILEGE</B> String => name of access (SELECT, - * INSERT, UPDATE, REFRENCES, ...) - * <LI><B>IS_GRANTABLE</B> String => "YES" if grantee is permitted - * to grant to others; "NO" if not; null if unknown - * </OL> - * - * @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 - { - Field f[] = new Field[7]; - Vector v = new Vector(); - - f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, getMaxNameLength()); - f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, getMaxNameLength()); - f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, getMaxNameLength()); - f[3] = new Field(connection, "GRANTOR", iVarcharOid, getMaxNameLength()); - f[4] = new Field(connection, "GRANTEE", iVarcharOid, getMaxNameLength()); - f[5] = new Field(connection, "PRIVILEGE", iVarcharOid, getMaxNameLength()); - f[6] = new Field(connection, "IS_GRANTABLE", iVarcharOid, getMaxNameLength()); - - String sql; - if (connection.haveMinimumServerVersion("7.3")) { - sql = "SELECT n.nspname,c.relname,u.usename,c.relacl "+ - " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class c, pg_catalog.pg_user u "+ - " WHERE c.relnamespace = n.oid "+ - " AND u.usesysid = c.relowner "+ - " AND c.relkind = 'r' "; - if (schemaPattern != null && !"".equals(schemaPattern)) { - sql += " AND n.nspname LIKE '"+escapeQuotes(schemaPattern)+"' "; - } - } else { - sql = "SELECT NULL::text AS nspname,c.relname,u.usename,c.relacl "+ - "FROM pg_class c, pg_user u "+ - " WHERE u.usesysid = c.relowner "+ - " AND c.relkind = 'r' "; - } - - if (tableNamePattern != null && !"".equals(tableNamePattern)) { - sql += " AND c.relname LIKE '"+escapeQuotes(tableNamePattern)+"' "; - } - sql += " ORDER BY nspname, relname "; - - ResultSet rs = connection.createStatement().executeQuery(sql); - while (rs.next()) { - byte schema[] = rs.getBytes("nspname"); - byte table[] = rs.getBytes("relname"); - String owner = rs.getString("usename"); - String acl = rs.getString("relacl"); - Hashtable permissions = parseACL(acl, owner); - String permNames[] = new String[permissions.size()]; - Enumeration e = permissions.keys(); - int i=0; - while (e.hasMoreElements()) { - permNames[i++] = (String)e.nextElement(); - } - sortStringArray(permNames); - for (i=0; i<permNames.length; i++) { - byte[] privilege = encoding.encode(permNames[i]); - Vector grantees = (Vector)permissions.get(permNames[i]); - for (int j=0; j<grantees.size(); j++) { - String grantee = (String)grantees.elementAt(j); - String grantable = owner.equals(grantee) ? "YES" : "NO"; - byte[][] tuple = new byte[7][]; - tuple[0] = null; - tuple[1] = schema; - tuple[2] = table; - tuple[3] = encoding.encode(owner); - tuple[4] = encoding.encode(grantee); - tuple[5] = privilege; - tuple[6] = encoding.encode(grantable); - v.addElement(tuple); - } - } - } - rs.close(); - - return (ResultSet) ((BaseStatement)connection.createStatement()).createResultSet(f, v, "OK", 1, 0, false); - } - - private static void sortStringArray(String s[]) { - for (int i=0; i<s.length-1; i++) { - for (int j=i+1; j<s.length; j++) { - if (s[i].compareTo(s[j]) > 0) { - String tmp = s[i]; - s[i] = s[j]; - s[j] = tmp; - } - } - } - } - - /** - * Parse an String of ACLs into a Vector of ACLs. - */ - private static Vector parseACLArray(String aclString) { - Vector acls = new Vector(); - if (aclString == null || aclString.length() == 0) { - return acls; - } - boolean inQuotes = false; - // start at 1 because of leading "{" - int beginIndex = 1; - char prevChar = ' '; - for (int i=beginIndex; i<aclString.length(); i++) { - - char c = aclString.charAt(i); - if (c == '"' && prevChar != '\\') { - inQuotes = !inQuotes; - } else if (c == ',' && !inQuotes) { - acls.addElement(aclString.substring(beginIndex,i)); - beginIndex = i+1; - } - prevChar = c; - } - // add last element removing the trailing "}" - acls.addElement(aclString.substring(beginIndex,aclString.length()-1)); - - // Strip out enclosing quotes, if any. - for (int i=0; i<acls.size(); i++) { - String acl = (String)acls.elementAt(i); - if (acl.startsWith("\"") && acl.endsWith("\"")) { - acl = acl.substring(1,acl.length()-1); - acls.setElementAt(acl,i); - } - } - return acls; - } - - /** - * Add the user described by the given acl to the Vectors of users - * with the privileges described by the acl. - */ - private void addACLPrivileges(String acl, Hashtable privileges) { - int equalIndex = acl.lastIndexOf("="); - String name = acl.substring(0,equalIndex); - if (name.length() == 0) { - name = "PUBLIC"; - } - String privs = acl.substring(equalIndex+1); - for (int i=0; i<privs.length(); i++) { - char c = privs.charAt(i); - String sqlpriv; - switch (c) { - case 'a': sqlpriv = "INSERT"; break; - case 'r': sqlpriv = "SELECT"; break; - case 'w': sqlpriv = "UPDATE"; break; - case 'd': sqlpriv = "DELETE"; break; - case 'R': sqlpriv = "RULE"; break; - case 'x': sqlpriv = "REFERENCES"; break; - case 't': sqlpriv = "TRIGGER"; break; - // the folloowing can't be granted to a table, but - // we'll keep them for completeness. - case 'X': sqlpriv = "EXECUTE"; break; - case 'U': sqlpriv = "USAGE"; break; - case 'C': sqlpriv = "CREATE"; break; - case 'T': sqlpriv = "CREATE TEMP"; break; - default: sqlpriv = "UNKNOWN"; - } - Vector usersWithPermission = (Vector)privileges.get(sqlpriv); - if (usersWithPermission == null) { - usersWithPermission = new Vector(); - privileges.put(sqlpriv,usersWithPermission); - } - usersWithPermission.addElement(name); - } - } - - /** - * Take the a String representing an array of ACLs and return - * a Hashtable mapping the SQL permission name to a Vector of - * usernames who have that permission. - */ - protected Hashtable parseACL(String aclArray, String owner) { - if (aclArray == null || aclArray == "") { - //null acl is a shortcut for owner having full privs - aclArray = "{" + owner + "=arwdRxt}"; - } - Vector acls = parseACLArray(aclArray); - Hashtable privileges = new Hashtable(); - for (int i=0; i<acls.size(); i++) { - String acl = (String)acls.elementAt(i); - addACLPrivileges(acl,privileges); - } - return privileges; - } - - /* - * Get a description of a table's optimal set of columns that - * uniquely identifies a row. They are ordered by SCOPE. - * - * <P>Each column description has the following columns: - * <OL> - * <LI><B>SCOPE</B> short => actual scope of result - * <UL> - * <LI> bestRowTemporary - very temporary, while using row - * <LI> bestRowTransaction - valid for remainder of current transaction - * <LI> bestRowSession - valid for remainder of current session - * </UL> - * <LI><B>COLUMN_NAME</B> String => column name - * <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types - * <LI><B>TYPE_NAME</B> String => Data source dependent type name - * <LI><B>COLUMN_SIZE</B> int => precision - * <LI><B>BUFFER_LENGTH</B> int => not used - * <LI><B>DECIMAL_DIGITS</B> short => scale - * <LI><B>PSEUDO_COLUMN</B> short => is this a pseudo column - * like an Oracle ROWID - * <UL> - * <LI> bestRowUnknown - may or may not be pseudo column - * <LI> bestRowNotPseudo - is NOT a pseudo column - * <LI> bestRowPseudo - is a pseudo column - * </UL> - * </OL> - * - * @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 - { - Field f[] = new Field[8]; - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "SCOPE", iInt2Oid, 2); - f[1] = new Field(connection, "COLUMN_NAME", iVarcharOid, getMaxNameLength()); - f[2] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[3] = new Field(connection, "TYPE_NAME", iVarcharOid, getMaxNameLength()); - f[4] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); - f[5] = new Field(connection, "BUFFER_LENGTH", iInt4Oid, 4); - f[6] = new Field(connection, "DECIMAL_DIGITS", iInt2Oid, 2); - f[7] = new Field(connection, "PSEUDO_COLUMN", iInt2Oid, 2); - - /* At the moment this simply returns a table's primary key, - * if there is one. I believe other unique indexes, ctid, - * and oid should also be considered. -KJ - */ - - String from; - String where = ""; - if (connection.haveMinimumServerVersion("7.3")) { - from = " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class ct, pg_catalog.pg_class ci, pg_catalog.pg_attribute a, pg_catalog.pg_index i "; - where = " AND ct.relnamespace = n.oid "; - if (schema != null && !"".equals(schema)) { - where += " AND n.nspname = '"+escapeQuotes(schema)+"' "; - } - } else { - from = " FROM pg_class ct, pg_class ci, pg_attribute a, pg_index i "; - } - String sql = "SELECT a.attname, a.atttypid "+ - from+ - " WHERE ct.oid=i.indrelid AND ci.oid=i.indexrelid "+ - " AND a.attrelid=ci.oid AND i.indisprimary "+ - " AND ct.relname = '"+escapeQuotes(table)+"' "+ - where+ - " ORDER BY a.attnum "; - - ResultSet rs = connection.createStatement().executeQuery(sql); - while (rs.next()) { - byte tuple[][] = new byte[8][]; - int columnTypeOid = rs.getInt("atttypid"); - tuple[0] = encoding.encode(Integer.toString(scope)); - tuple[1] = rs.getBytes("attname"); - tuple[2] = encoding.encode(Integer.toString(connection.getSQLType(columnTypeOid))); - tuple[3] = encoding.encode(connection.getPGType(columnTypeOid)); - tuple[4] = null; - tuple[5] = null; - tuple[6] = null; - tuple[7] = encoding.encode(Integer.toString(java.sql.DatabaseMetaData.bestRowNotPseudo)); - v.addElement(tuple); - } - - return (ResultSet) ((BaseStatement)connection.createStatement()).createResultSet(f, v, "OK", 1, 0, false); - } - - /* - * Get a description of a table's columns that are automatically - * updated when any value in a row is updated. They are - * unordered. - * - * <P>Each column description has the following columns: - * <OL> - * <LI><B>SCOPE</B> short => is not used - * <LI><B>COLUMN_NAME</B> String => column name - * <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types - * <LI><B>TYPE_NAME</B> String => Data source dependent type name - * <LI><B>COLUMN_SIZE</B> int => precision - * <LI><B>BUFFER_LENGTH</B> int => length of column value in bytes - * <LI><B>DECIMAL_DIGITS</B> short => scale - * <LI><B>PSEUDO_COLUMN</B> short => is this a pseudo column - * like an Oracle ROWID - * <UL> - * <LI> versionColumnUnknown - may or may not be pseudo column - * <LI> versionColumnNotPseudo - is NOT a pseudo column - * <LI> versionColumnPseudo - is a pseudo column - * </UL> - * </OL> - * - * @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 - { - Field f[] = new Field[8]; - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "SCOPE", iInt2Oid, 2); - f[1] = new Field(connection, "COLUMN_NAME", iVarcharOid, getMaxNameLength()); - f[2] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[3] = new Field(connection, "TYPE_NAME", iVarcharOid, getMaxNameLength()); - f[4] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); - f[5] = new Field(connection, "BUFFER_LENGTH", iInt4Oid, 4); - f[6] = new Field(connection, "DECIMAL_DIGITS", iInt2Oid, 2); - f[7] = new Field(connection, "PSEUDO_COLUMN", iInt2Oid, 2); - - byte tuple[][] = new byte[8][]; - - /* Postgresql does not have any column types that are - * automatically updated like some databases' timestamp type. - * We can't tell what rules or triggers might be doing, so we - * are left with the system columns that change on an update. - * An update may change all of the following system columns: - * ctid, xmax, xmin, cmax, and cmin. Depending on if we are - * in a transaction and wether we roll it back or not the - * only guaranteed change is to ctid. -KJ - */ - - tuple[0] = null; - tuple[1] = encoding.encode("ctid"); - tuple[2] = encoding.encode(Integer.toString(connection.getSQLType("tid"))); - tuple[3] = encoding.encode("tid"); - tuple[4] = null; - tuple[5] = null; - tuple[6] = null; - tuple[7] = encoding.encode(Integer.toString(java.sql.DatabaseMetaData.versionColumnPseudo)); - v.addElement(tuple); - - /* Perhaps we should check that the given - * catalog.schema.table actually exists. -KJ - */ - return (ResultSet) ((BaseStatement)connection.createStatement()).createResultSet(f, v, "OK", 1, 0, false); - } - - /* - * Get a description of a table's primary key columns. They - * are ordered by COLUMN_NAME. - * - * <P>Each column description has the following columns: - * <OL> - * <LI><B>TABLE_CAT</B> String => table catalog (may be null) - * <LI><B>TABLE_SCHEM</B> String => table schema (may be null) - * <LI><B>TABLE_NAME</B> String => table name - * <LI><B>COLUMN_NAME</B> String => column name - * <LI><B>KEY_SEQ</B> short => sequence number within primary key - * <LI><B>PK_NAME</B> String => primary key name (may be null) - * </OL> - * - * @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 - { - String select; - String from; - String where = ""; - if (connection.haveMinimumServerVersion("7.3")) { - select = "SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM, "; - from = " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class ct, pg_catalog.pg_class ci, pg_catalog.pg_attribute a, pg_catalog.pg_index i "; - where = " AND ct.relnamespace = n.oid "; - if (schema != null && !"".equals(schema)) { - where += " AND n.nspname = '"+escapeQuotes(schema)+"' "; - } - } else { - select = "SELECT NULL AS TABLE_CAT, NULL AS TABLE_SCHEM, "; - from = " FROM pg_class ct, pg_class ci, pg_attribute a, pg_index i "; - } - String sql = select+ - " ct.relname AS TABLE_NAME, "+ - " a.attname AS COLUMN_NAME, "+ - " a.attnum AS KEY_SEQ, "+ - " ci.relname AS PK_NAME "+ - from+ - " WHERE ct.oid=i.indrelid AND ci.oid=i.indexrelid "+ - " AND a.attrelid=ci.oid AND i.indisprimary "; - if (table != null && !"".equals(table)) { - sql += " AND ct.relname = '"+escapeQuotes(table)+"' "; - } - sql += where+ - " ORDER BY table_name, pk_name, key_seq"; - return connection.createStatement().executeQuery(sql); - } - - /** - * - * @param catalog - * @param schema - * @param primaryTable if provided will get the keys exported by this table - * @param foreignTable if provided will get the keys imported by this table - * @return ResultSet - * @throws SQLException - */ - - protected java.sql.ResultSet getImportedExportedKeys(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException - { - Field f[] = new Field[14]; - - f[0] = new Field(connection, "PKTABLE_CAT", iVarcharOid, getMaxNameLength()); - f[1] = new Field(connection, "PKTABLE_SCHEM", iVarcharOid, getMaxNameLength()); - f[2] = new Field(connection, "PKTABLE_NAME", iVarcharOid, getMaxNameLength()); - f[3] = new Field(connection, "PKCOLUMN_NAME", iVarcharOid, getMaxNameLength()); - f[4] = new Field(connection, "FKTABLE_CAT", iVarcharOid, getMaxNameLength()); - f[5] = new Field(connection, "FKTABLE_SCHEM", iVarcharOid, getMaxNameLength()); - f[6] = new Field(connection, "FKTABLE_NAME", iVarcharOid, getMaxNameLength()); - f[7] = new Field(connection, "FKCOLUMN_NAME", iVarcharOid, getMaxNameLength()); - f[8] = new Field(connection, "KEY_SEQ", iInt2Oid, 2); - f[9] = new Field(connection, "UPDATE_RULE", iInt2Oid, 2); - f[10] = new Field(connection, "DELETE_RULE", iInt2Oid, 2); - f[11] = new Field(connection, "FK_NAME", iVarcharOid, getMaxNameLength()); - f[12] = new Field(connection, "PK_NAME", iVarcharOid, getMaxNameLength()); - f[13] = new Field(connection, "DEFERRABILITY", iInt2Oid, 2); - - - String select; - String from; - String where = ""; - - /* - * The addition of the pg_constraint in 7.3 table should have really - * helped us out here, but it comes up just a bit short. - * - The conkey, confkey columns aren't really useful without - * contrib/array unless we want to issues separate queries. - * - Unique indexes that can support foreign keys are not necessarily - * added to pg_constraint. Also multiple unique indexes covering - * the same keys can be created which make it difficult to determine - * the PK_NAME field. - */ - - if (connection.haveMinimumServerVersion("7.4")) { - String sql = "SELECT NULL::text AS PKTABLE_CAT, pkn.nspname AS PKTABLE_SCHEMA, pkc.relname AS PKTABLE_NAME, pka.attname AS PKCOLUMN_NAME, "+ - "NULL::text AS FK_TABLE_CAT, fkn.nspname AS FKTABLE_SCHEMA, fkc.relname AS FKTABLE_NAME, fka.attname AS FKCOLUMN_NAME, "+ - "pos.n AS KEY_SEQ, "+ - "CASE con.confupdtype "+ - " WHEN 'c' THEN " + DatabaseMetaData.importedKeyCascade + - " WHEN 'n' THEN " + DatabaseMetaData.importedKeySetNull + - " WHEN 'd' THEN " + DatabaseMetaData.importedKeySetDefault + - " WHEN 'r' THEN " + DatabaseMetaData.importedKeyRestrict + - " WHEN 'a' THEN " + DatabaseMetaData.importedKeyNoAction + - " ELSE NULL END AS UPDATE_RULE, "+ - "CASE con.confdeltype "+ - " WHEN 'c' THEN " + DatabaseMetaData.importedKeyCascade + - " WHEN 'n' THEN " + DatabaseMetaData.importedKeySetNull + - " WHEN 'd' THEN " + DatabaseMetaData.importedKeySetDefault + - " WHEN 'r' THEN " + DatabaseMetaData.importedKeyRestrict + - " WHEN 'a' THEN " + DatabaseMetaData.importedKeyNoAction + - " ELSE NULL END AS DELETE_RULE, "+ - "con.conname AS FK_NAME, pkic.relname AS PK_NAME, "+ - "CASE "+ - " WHEN con.condeferrable AND con.condeferred THEN " + DatabaseMetaData.importedKeyInitiallyDeferred + - " WHEN con.condeferrable THEN " + DatabaseMetaData.importedKeyInitiallyImmediate + - " ELSE " + DatabaseMetaData.importedKeyNotDeferrable+ - " END AS DEFERRABILITY "+ - " FROM "+ - " pg_catalog.pg_namespace pkn, pg_catalog.pg_class pkc, pg_catalog.pg_attribute pka, "+ - " pg_catalog.pg_namespace fkn, pg_catalog.pg_class fkc, pg_catalog.pg_attribute fka, "+ - " pg_catalog.pg_constraint con, information_schema._pg_keypositions() pos(n), "+ - " pg_catalog.pg_depend dep, pg_catalog.pg_class pkic "+ - " WHERE pkn.oid = pkc.relnamespace AND pkc.oid = pka.attrelid AND pka.attnum = con.confkey[pos.n] AND con.confrelid = pkc.oid "+ - " AND fkn.oid = fkc.relnamespace AND fkc.oid = fka.attrelid AND fka.attnum = con.conkey[pos.n] AND con.conrelid = fkc.oid "+ - " AND con.contype = 'f' AND con.oid = dep.objid AND pkic.oid = dep.refobjid AND pkic.relkind = 'i' AND dep.classid = 'pg_constraint'::regclass::oid AND dep.refclassid = 'pg_class'::regclass::oid "; - if (primarySchema != null && !"".equals(primarySchema)) { - sql += " AND pkn.nspname = '"+escapeQuotes(primarySchema)+"' "; - } - if (foreignSchema != null && !"".equals(foreignSchema)) { - sql += " AND fkn.nspname = '"+escapeQuotes(foreignSchema)+"' "; - } - if (primaryTable != null && !"".equals(primaryTable)) { - sql += " AND pkc.relname = '"+escapeQuotes(primaryTable)+"' "; - } - if (foreignTable != null && !"".equals(foreignTable)) { - sql += " AND fkc.relname = '"+escapeQuotes(foreignTable)+"' "; - } - - if (primaryTable != null) { - sql += " ORDER BY fkn.nspname,fkc.relname,pos.n"; - } else { - sql += " ORDER BY pkn.nspname,pkc.relname,pos.n"; - } - - return connection.createStatement().executeQuery(sql); - } else if (connection.haveMinimumServerVersion("7.3")) { - select = "SELECT DISTINCT n1.nspname as pnspname,n2.nspname as fnspname, "; - from = " FROM pg_catalog.pg_namespace n1 "+ - " JOIN pg_catalog.pg_class c1 ON (c1.relnamespace = n1.oid) "+ - " JOIN pg_catalog.pg_index i ON (c1.oid=i.indrelid) "+ - " JOIN pg_catalog.pg_class ic ON (i.indexrelid=ic.oid) "+ - " JOIN pg_catalog.pg_attribute a ON (ic.oid=a.attrelid), "+ - " pg_catalog.pg_namespace n2 "+ - " JOIN pg_catalog.pg_class c2 ON (c2.relnamespace=n2.oid), "+ - " pg_catalog.pg_trigger t1 "+ - " JOIN pg_catalog.pg_proc p1 ON (t1.tgfoid=p1.oid), "+ - " pg_catalog.pg_trigger t2 "+ - " JOIN pg_catalog.pg_proc p2 ON (t2.tgfoid=p2.oid) "; - if (primarySchema != null && !"".equals(primarySchema)) { - where += " AND n1.nspname = '"+escapeQuotes(primarySchema)+"' "; - } - if (foreignSchema != null && !"".equals(foreignSchema)) { - where += " AND n2.nspname = '"+escapeQuotes(foreignSchema)+"' "; - } - } else { - select = "SELECT DISTINCT NULL::text as pnspname, NULL::text as fnspname, "; - from = " FROM pg_class c1 "+ - " JOIN pg_index i ON (c1.oid=i.indrelid) "+ - " JOIN pg_class ic ON (i.indexrelid=ic.oid) "+ - " JOIN pg_attribute a ON (ic.oid=a.attrelid), "+ - " pg_class c2, "+ - " pg_trigger t1 "+ - " JOIN pg_proc p1 ON (t1.tgfoid=p1.oid), "+ - " pg_trigger t2 "+ - " JOIN pg_proc p2 ON (t2.tgfoid=p2.oid) "; - } - - String sql = select - + "c1.relname as prelname, " - + "c2.relname as frelname, " - + "t1.tgconstrname, " - + "a.attnum as keyseq, " - + "ic.relname as fkeyname, " - + "t1.tgdeferrable, " - + "t1.tginitdeferred, " - + "t1.tgnargs,t1.tgargs, " - + "p1.proname as updaterule, " - + "p2.proname as deleterule " - + from - + "WHERE " - // isolate the update rule - + "(t1.tgrelid=c1.oid " - + "AND t1.tgisconstraint " - + "AND t1.tgconstrrelid=c2.oid " - + "AND p1.proname LIKE 'RI\\\\_FKey\\\\_%\\\\_upd') " - - + "AND " - // isolate the delete rule - + "(t2.tgrelid=c1.oid " - + "AND t2.tgisconstraint " - + "AND t2.tgconstrrelid=c2.oid " - + "AND p2.proname LIKE 'RI\\\\_FKey\\\\_%\\\\_del') " - - + "AND i.indisprimary " - + where; - - if (primaryTable != null) { - sql += "AND c1.relname='" + escapeQuotes(primaryTable) + "' "; - } - if (foreignTable != null) { - sql += "AND c2.relname='" + escapeQuotes(foreignTable) + "' "; - } - - sql += "ORDER BY "; - - // orderby is as follows getExported, orders by FKTABLE, - // getImported orders by PKTABLE - // getCrossReference orders by FKTABLE, so this should work for both, - // since when getting crossreference, primaryTable will be defined - - if (primaryTable != null) { - if (connection.haveMinimumServerVersion("7.3")) { - sql += "fnspname,"; - } - sql += "frelname"; - } else { - if (connection.haveMinimumServerVersion("7.3")) { - sql += "pnspname,"; - } - sql += "prelname"; - } - - sql += ",keyseq"; - - ResultSet rs = connection.createStatement().executeQuery(sql); - - // returns the following columns - // and some example data with a table defined as follows - - // create table people ( id int primary key); - // create table policy ( id int primary key); - // create table users ( id int primary key, people_id int references people(id), policy_id int references policy(id)) - - // prelname | frelname | tgconstrname | keyseq | fkeyName | tgdeferrable | tginitdeferred - // 1 | 2 | 3 | 4 | 5 | 6 | 7 - - // people | users | <unnamed> | 1 | people_pkey | f | f - - // | tgnargs | tgargs | updaterule | deleterule - // | 8 | 9 | 10 | 11 - // | 6 | <unnamed>\000users\000people\000UNSPECIFIED\000people_id\000id\000 | RI_FKey_noaction_upd | RI_FKey_noaction_del - - Vector tuples = new Vector(); - - while ( rs.next() ) - { - byte tuple[][] = new byte[14][]; - - tuple[1] = rs.getBytes(1); //PKTABLE_SCHEM - tuple[5] = rs.getBytes(2); //FKTABLE_SCHEM - tuple[2] = rs.getBytes(3); //PKTABLE_NAME - tuple[6] = rs.getBytes(4); //FKTABLE_NAME - String fKeyName = rs.getString(5); - String updateRule = rs.getString(12); - - if (updateRule != null ) - { - // Rules look like this RI_FKey_noaction_del so we want to pull out the part between the 'Key_' and the last '_' s - - String rule = updateRule.substring(8, updateRule.length() - 4); - - int action = java.sql.DatabaseMetaData.importedKeyNoAction; - - if ( rule == null || "noaction".equals(rule) ) - action = java.sql.DatabaseMetaData.importedKeyNoAction; - if ("cascade".equals(rule)) - action = java.sql.DatabaseMetaData.importedKeyCascade; - else if ("setnull".equals(rule)) - action = java.sql.DatabaseMetaData.importedKeySetNull; - else if ("setdefault".equals(rule)) - action = java.sql.DatabaseMetaData.importedKeySetDefault; - else if ("restrict".equals(rule)) - action = java.sql.DatabaseMetaData.importedKeyRestrict; - - tuple[9] = encoding.encode(Integer.toString(action)); - - } - - String deleteRule = rs.getString(13); - - if ( deleteRule != null ) - { - - String rule = deleteRule.substring(8, deleteRule.length() - 4); - - int action = java.sql.DatabaseMetaData.importedKeyNoAction; - if ("cascade".equals(rule)) - action = java.sql.DatabaseMetaData.importedKeyCascade; - else if ("setnull".equals(rule)) - action = java.sql.DatabaseMetaData.importedKeySetNull; - else if ("setdefault".equals(rule)) - action = java.sql.DatabaseMetaData.importedKeySetDefault; - else if ("restrict".equals(rule)) - action = java.sql.DatabaseMetaData.importedKeyRestrict; - tuple[10] = encoding.encode(Integer.toString(action)); - } - - - int keySequence = rs.getInt(6); //KEY_SEQ - - // Parse the tgargs data - String fkeyColumn = ""; - String pkeyColumn = ""; - String fkName = ""; - // Note, I am guessing at most of this, but it should be close - // if not, please correct - // the keys are in pairs and start after the first four arguments - // the arguments are seperated by \000 - - String targs = rs.getString(11); - - // args look like this - //<unnamed>\000ww\000vv\000UNSPECIFIED\000m\000a\000n\000b\000 - // we are primarily interested in the column names which are the last items in the string - - Vector tokens = tokenize(targs, "\\000"); - if (tokens.size() > 0) { - fkName = (String)tokens.elementAt(0); - } - - if (fkName.startsWith("<unnamed>")) { - fkName = targs; - } - - int element = 4 + (keySequence - 1) * 2; - if (tokens.size() > element) { - fkeyColumn = (String)tokens.elementAt(element); - } - - element++; - if (tokens.size() > element) { - pkeyColumn = (String)tokens.elementAt(element); - } - - tuple[3] = encoding.encode(pkeyColumn); //PKCOLUMN_NAME - tuple[7] = encoding.encode(fkeyColumn); //FKCOLUMN_NAME - - tuple[8] = rs.getBytes(6); //KEY_SEQ - tuple[11] = encoding.encode(fkName); //FK_NAME this will give us a unique name for the foreign key - tuple[12] = rs.getBytes(7); //PK_NAME - - // DEFERRABILITY - int deferrability = java.sql.DatabaseMetaData.importedKeyNotDeferrable; - boolean deferrable = rs.getBoolean(8); - boolean initiallyDeferred = rs.getBoolean(9); - if (deferrable) - { - if (initiallyDeferred) - deferrability = java.sql.DatabaseMetaData.importedKeyInitiallyDeferred; - else - deferrability = java.sql.DatabaseMetaData.importedKeyInitiallyImmediate; - } - tuple[13] = encoding.encode(Integer.toString(deferrability)); - - tuples.addElement(tuple); - } - - return (ResultSet) ((BaseStatement)connection.createStatement()).createResultSet(f, tuples, "OK", 1, 0, false); - } - - /* - * 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. - * - * <P>Each primary key column description has the following columns: - * <OL> - * <LI><B>PKTABLE_CAT</B> String => primary key table catalog - * being imported (may be null) - * <LI><B>PKTABLE_SCHEM</B> String => primary key table schema - * being imported (may be null) - * <LI><B>PKTABLE_NAME</B> String => primary key table name - * being imported - * <LI><B>PKCOLUMN_NAME</B> String => primary key column name - * being imported - * <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null) - * <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null) - * <LI><B>FKTABLE_NAME</B> String => foreign key table name - * <LI><B>FKCOLUMN_NAME</B> String => foreign key column name - * <LI><B>KEY_SEQ</B> short => sequence number within foreign key - * <LI><B>UPDATE_RULE</B> short => What happens to - * foreign key when primary is updated: - * <UL> - * <LI> importedKeyCascade - change imported key to agree - * with primary key update - * <LI> importedKeyRestrict - do not allow update of primary - * key if it has been imported - * <LI> importedKeySetNull - change imported key to NULL if - * its primary key has been updated - * </UL> - * <LI><B>DELETE_RULE</B> short => What happens to - * the foreign key when primary is deleted. - * <UL> - * <LI> importedKeyCascade - delete rows that import a deleted key - * <LI> importedKeyRestrict - do not allow delete of primary - * key if it has been imported - * <LI> importedKeySetNull - change imported key to NULL if - * its primary key has been deleted - * </UL> - * <LI><B>FK_NAME</B> String => foreign key name (may be null) - * <LI><B>PK_NAME</B> String => primary key name (may be null) - * </OL> - * - * @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 - { - return getImportedExportedKeys(null,null,null,catalog, schema, table); - } - - /* - * 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. - * - * This method is currently unimplemented. - * - * <P>Each foreign key column description has the following columns: - * <OL> - * <LI><B>PKTABLE_CAT</B> String => primary key table catalog (may be null) - * <LI><B>PKTABLE_SCHEM</B> String => primary key table schema (may be null) - * <LI><B>PKTABLE_NAME</B> String => primary key table name - * <LI><B>PKCOLUMN_NAME</B> String => primary key column name - * <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null) - * being exported (may be null) - * <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null) - * being exported (may be null) - * <LI><B>FKTABLE_NAME</B> String => foreign key table name - * being exported - * <LI><B>FKCOLUMN_NAME</B> String => foreign key column name - * being exported - * <LI><B>KEY_SEQ</B> short => sequence number within foreign key - * <LI><B>UPDATE_RULE</B> short => What happens to - * foreign key when primary is updated: - * <UL> - * <LI> importedKeyCascade - change imported key to agree - * with primary key update - * <LI> importedKeyRestrict - do not allow update of primary - * key if it has been imported - * <LI> importedKeySetNull - change imported key to NULL if - * its primary key has been updated - * </UL> - * <LI><B>DELETE_RULE</B> short => What happens to - * the foreign key when primary is deleted. - * <UL> - * <LI> importedKeyCascade - delete rows that import a deleted key - * <LI> importedKeyRestrict - do not allow delete of primary - * key if it has been imported - * <LI> importedKeySetNull - change imported key to NULL if - * its primary key has been deleted - * </UL> - * <LI><B>FK_NAME</B> String => foreign key identifier (may be null) - * <LI><B>PK_NAME</B> String => primary key identifier (may be null) - * </OL> - * - * @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 - { - return getImportedExportedKeys(catalog, schema, table, null,null,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. - * - * This method is currently unimplemented. - * - * <P>Each foreign key column description has the following columns: - * <OL> - * <LI><B>PKTABLE_CAT</B> String => primary key table catalog (may be null) - * <LI><B>PKTABLE_SCHEM</B> String => primary key table schema (may be null) - * <LI><B>PKTABLE_NAME</B> String => primary key table name - * <LI><B>PKCOLUMN_NAME</B> String => primary key column name - * <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null) - * being exported (may be null) - * <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null) - * being exported (may be null) - * <LI><B>FKTABLE_NAME</B> String => foreign key table name - * being exported - * <LI><B>FKCOLUMN_NAME</B> String => foreign key column name - * being exported - * <LI><B>KEY_SEQ</B> short => sequence number within foreign key - * <LI><B>UPDATE_RULE</B> short => What happens to - * foreign key when primary is updated: - * <UL> - * <LI> importedKeyCascade - change imported key to agree - * with primary key update - * <LI> importedKeyRestrict - do not allow update of primary - * key if it has been imported - * <LI> importedKeySetNull - change imported key to NULL if - * its primary key has been updated - * </UL> - * <LI><B>DELETE_RULE</B> short => What happens to - * the foreign key when primary is deleted. - * <UL> - * <LI> importedKeyCascade - delete rows that import a deleted key - * <LI> importedKeyRestrict - do not allow delete of primary - * key if it has been imported - * <LI> importedKeySetNull - change imported key to NULL if - * its primary key has been deleted - * </UL> - * <LI><B>FK_NAME</B> String => foreign key identifier (may be null) - * <LI><B>PK_NAME</B> String => primary key identifier (may be null) - * </OL> - * - * @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 - { - return getImportedExportedKeys(primaryCatalog, primarySchema, primaryTable, foreignCatalog, foreignSchema, foreignTable); - } - - /* - * 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. - * - * <P>Each type description has the following columns: - * <OL> - * <LI><B>TYPE_NAME</B> String => Type name - * <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types - * <LI><B>PRECISION</B> int => maximum precision - * <LI><B>LITERAL_PREFIX</B> String => prefix used to quote a literal - * (may be null) - * <LI><B>LITERAL_SUFFIX</B> String => suffix used to quote a literal - (may be null) - * <LI><B>CREATE_PARAMS</B> String => parameters used in creating - * the type (may be null) - * <LI><B>NULLABLE</B> short => can you use NULL for this type? - * <UL> - * <LI> typeNoNulls - does not allow NULL values - * <LI> typeNullable - allows NULL values - * <LI> typeNullableUnknown - nullability unknown - * </UL> - * <LI><B>CASE_SENSITIVE</B> boolean=> is it case sensitive? - * <LI><B>SEARCHABLE</B> short => can you use "WHERE" based on this type: - * <UL> - * <LI> typePredNone - No support - * <LI> typePredChar - Only supported with WHERE .. LIKE - * <LI> typePredBasic - Supported except for WHERE .. LIKE - * <LI> typeSearchable - Supported for all WHERE .. - * </UL> - * <LI><B>UNSIGNED_ATTRIBUTE</B> boolean => is it unsigned? - * <LI><B>FIXED_PREC_SCALE</B> boolean => can it be a money value? - * <LI><B>AUTO_INCREMENT</B> boolean => can it be used for an - * auto-increment value? - * <LI><B>LOCAL_TYPE_NAME</B> String => localized version of type name - * (may be null) - * <LI><B>MINIMUM_SCALE</B> short => minimum scale supported - * <LI><B>MAXIMUM_SCALE</B> short => maximum scale supported - * <LI><B>SQL_DATA_TYPE</B> int => unused - * <LI><B>SQL_DATETIME_SUB</B> int => unused - * <LI><B>NUM_PREC_RADIX</B> int => usually 2 or 10 - * </OL> - * - * @return ResultSet each row is a SQL type description - */ - public java.sql.ResultSet getTypeInfo() throws SQLException - { - - Field f[] = new Field[18]; - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "TYPE_NAME", iVarcharOid, getMaxNameLength()); - f[1] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[2] = new Field(connection, "PRECISION", iInt4Oid, 4); - f[3] = new Field(connection, "LITERAL_PREFIX", iVarcharOid, getMaxNameLength()); - f[4] = new Field(connection, "LITERAL_SUFFIX", iVarcharOid, getMaxNameLength()); - f[5] = new Field(connection, "CREATE_PARAMS", iVarcharOid, getMaxNameLength()); - f[6] = new Field(connection, "NULLABLE", iInt2Oid, 2); - f[7] = new Field(connection, "CASE_SENSITIVE", iBoolOid, 1); - f[8] = new Field(connection, "SEARCHABLE", iInt2Oid, 2); - f[9] = new Field(connection, "UNSIGNED_ATTRIBUTE", iBoolOid, 1); - f[10] = new Field(connection, "FIXED_PREC_SCALE", iBoolOid, 1); - f[11] = new Field(connection, "AUTO_INCREMENT", iBoolOid, 1); - f[12] = new Field(connection, "LOCAL_TYPE_NAME", iVarcharOid, getMaxNameLength()); - f[13] = new Field(connection, "MINIMUM_SCALE", iInt2Oid, 2); - f[14] = new Field(connection, "MAXIMUM_SCALE", iInt2Oid, 2); - f[15] = new Field(connection, "SQL_DATA_TYPE", iInt4Oid, 4); - f[16] = new Field(connection, "SQL_DATETIME_SUB", iInt4Oid, 4); - f[17] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4); - - String sql; - if (connection.haveMinimumServerVersion("7.3")) { - sql = "SELECT typname FROM pg_catalog.pg_type"; - } else { - sql = "SELECT typname FROM pg_type"; - } - - ResultSet rs = connection.createStatement().executeQuery(sql); - // cache some results, this will keep memory useage down, and speed - // things up a little. - byte b9[] = encoding.encode("9"); - byte b10[] = encoding.encode("10"); - byte bf[] = encoding.encode("f"); - byte bnn[] = encoding.encode(Integer.toString(java.sql.DatabaseMetaData.typeNoNulls)); - byte bts[] = encoding.encode(Integer.toString(java.sql.DatabaseMetaData.typeSearchable)); - - while (rs.next()) - { - byte[][] tuple = new byte[18][]; - String typname = rs.getString(1); - tuple[0] = encoding.encode(typname); - tuple[1] = encoding.encode(Integer.toString(connection.getSQLType(typname))); - 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 (ResultSet) ((BaseStatement)connection.createStatement()).createResultSet(f, v, "OK", 1, 0, false); - } - - /* - * Get a description of a table's indices and statistics. They are - * ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. - * - * <P>Each index column description has the following columns: - * <OL> - * <LI><B>TABLE_CAT</B> String => table catalog (may be null) - * <LI><B>TABLE_SCHEM</B> String => table schema (may be null) - * <LI><B>TABLE_NAME</B> String => table name - * <LI><B>NON_UNIQUE</B> boolean => Can index values be non-unique? - * false when TYPE is tableIndexStatistic - * <LI><B>INDEX_QUALIFIER</B> String => index catalog (may be null); - * null when TYPE is tableIndexStatistic - * <LI><B>INDEX_NAME</B> String => index name; null when TYPE is - * tableIndexStatistic - * <LI><B>TYPE</B> short => index type: - * <UL> - * <LI> tableIndexStatistic - this identifies table statistics that are - * returned in conjuction with a table's index descriptions - * <LI> tableIndexClustered - this is a clustered index - * <LI> tableIndexHashed - this is a hashed index - * <LI> tableIndexOther - this is some other style of index - * </UL> - * <LI><B>ORDINAL_POSITION</B> short => column sequence number - * within index; zero when TYPE is tableIndexStatistic - * <LI><B>COLUMN_NAME</B> String => column name; null when TYPE is - * tableIndexStatistic - * <LI><B>ASC_OR_DESC</B> String => column sort sequence, "A" => ascending - * "D" => descending, may be null if sort sequence is not supported; - * null when TYPE is tableIndexStatistic - * <LI><B>CARDINALITY</B> 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. - * <LI><B>PAGES</B> 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. - * <LI><B>FILTER_CONDITION</B> String => Filter condition, if any. - * (may be null) - * </OL> - * - * @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 tableName, boolean unique, boolean approximate) throws SQLException - { - String select; - String from; - String where = ""; - if (connection.haveMinimumServerVersion("7.3")) { - select = "SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM, "; - from = " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class ct, pg_catalog.pg_class ci, pg_catalog.pg_index i, pg_catalog.pg_attribute a, pg_catalog.pg_am am "; - where = " AND n.oid = ct.relnamespace "; - if (schema != null && ! "".equals(schema)) { - where += " AND n.nspname = '"+escapeQuotes(schema)+"' "; - } - } else { - select = "SELECT NULL AS TABLE_CAT, NULL AS TABLE_SCHEM, "; - from = " FROM pg_class ct, pg_class ci, pg_index i, pg_attribute a, pg_am am "; - } - - String sql = select+ - " ct.relname AS TABLE_NAME, NOT i.indisunique AS NON_UNIQUE, NULL AS INDEX_QUALIFIER, ci.relname AS INDEX_NAME, "+ - " CASE i.indisclustered "+ - " WHEN true THEN "+java.sql.DatabaseMetaData.tableIndexClustered+ - " ELSE CASE am.amname "+ - " WHEN 'hash' THEN "+java.sql.DatabaseMetaData.tableIndexHashed+ - " ELSE "+java.sql.DatabaseMetaData.tableIndexOther+ - " END "+ - " END AS TYPE, "+ - " a.attnum AS ORDINAL_POSITION, "+ - " a.attname AS COLUMN_NAME, "+ - " NULL AS ASC_OR_DESC, "+ - " ci.reltuples AS CARDINALITY, "+ - " ci.relpages AS PAGES, "+ - " NULL AS FILTER_CONDITION "+ - from+ - " WHERE ct.oid=i.indrelid AND ci.oid=i.indexrelid AND a.attrelid=ci.oid AND ci.relam=am.oid "+ - where+ - " AND ct.relname = '"+escapeQuotes(tableName)+"' "; - - if (unique) { - sql += " AND i.indisunique "; - } - sql += " ORDER BY NON_UNIQUE, TYPE, INDEX_NAME, ORDINAL_POSITION "; - return connection.createStatement().executeQuery(sql); - } - - /** - * Tokenize based on words not on single characters. - */ - private static Vector tokenize(String input, String delimiter) { - Vector result = new Vector(); - int start = 0; - int end = input.length(); - int delimiterSize = delimiter.length(); - - while (start < end) { - int delimiterIndex = input.indexOf(delimiter,start); - if (delimiterIndex < 0) { - result.addElement(input.substring(start)); - break; - } else { - String token = input.substring(start,delimiterIndex); - result.addElement(token); - start = delimiterIndex + delimiterSize; - } - } - return result; - } - - - -} diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java deleted file mode 100644 index 42cde40286..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java +++ /dev/null @@ -1,1255 +0,0 @@ -/*------------------------------------------------------------------------- - * - * AbstractJdbc1ResultSet.java - * This class defines methods of the jdbc1 specification. This class is - * extended by org.postgresql.jdbc2.AbstractJdbc2ResultSet which adds the - * jdbc2 methods. The real ResultSet class (for jdbc1) is - * org.postgresql.jdbc1.Jdbc1ResultSet - * - * Copyright (c) 2003, PostgreSQL Global Development Group - * - * IDENTIFICATION - * $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java,v 1.25 2003/12/12 17:58:34 davec Exp $ - * - *------------------------------------------------------------------------- - */ -package org.postgresql.jdbc1; - -import java.math.BigDecimal; -import java.io.*; -import java.sql.*; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Vector; -import org.postgresql.Driver; -import org.postgresql.core.BaseConnection; -import org.postgresql.core.BaseResultSet; -import org.postgresql.core.BaseStatement; -import org.postgresql.core.Field; -import org.postgresql.core.Encoding; -import org.postgresql.core.QueryExecutor; -import org.postgresql.largeobject.*; -import org.postgresql.util.PGbytea; -import org.postgresql.util.PGtokenizer; -import org.postgresql.util.PSQLException; -import org.postgresql.util.PSQLState; - -public abstract class AbstractJdbc1ResultSet implements BaseResultSet -{ - - protected Vector rows; // The results - protected BaseStatement statement; - protected Field fields[]; // The field descriptions - protected String status; // Status of the result - protected boolean binaryCursor = false; // is the data binary or Strings - protected int updateCount; // How many rows did we get back? - protected long insertOID; // The oid of an inserted row - protected int current_row; // Our pointer to where we are at - protected byte[][] this_row; // the current row result - protected BaseConnection 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. - protected BaseResultSet next = null; - - private StringBuffer sbuf = null; - public byte[][] rowBuffer = null; - - private SimpleDateFormat m_tsFormat = null; - private SimpleDateFormat m_tstzFormat = null; - private SimpleDateFormat m_dateFormat = null; - - private int fetchSize; // Fetch size for next read (might be 0). - private int lastFetchSize; // Fetch size of last read (might be 0). - - public abstract ResultSetMetaData getMetaData() throws SQLException; - - public AbstractJdbc1ResultSet(BaseStatement statement, - Field[] fields, - Vector tuples, - String status, - int updateCount, - long insertOID, - boolean binaryCursor) - { - this.connection = statement.getPGConnection(); - this.statement = statement; - this.fields = fields; - this.rows = tuples; - this.status = status; - this.updateCount = updateCount; - - this.insertOID = insertOID; - this.this_row = null; - this.current_row = -1; - this.binaryCursor = binaryCursor; - - this.lastFetchSize = this.fetchSize = (statement == null ? 0 : statement.getFetchSize()); - } - - public BaseStatement getPGStatement() { - return statement; - } - - public StringBuffer getStringBuffer() { - return sbuf; - } - - //This is implemented in jdbc2 - public void setStatement(BaseStatement statement) { - } - - //method to reinitialize a result set with more data - public void reInit (Field[] fields, Vector tuples, String status, - int updateCount, long insertOID, boolean binaryCursor) - { - this.fields = fields; - // on a reinit the size of this indicates how many we pulled - // back. If it's 0 then the res set has ended. - this.rows = tuples; - this.status = status; - this.updateCount = updateCount; - this.insertOID = insertOID; - this.this_row = null; - this.current_row = -1; - this.binaryCursor = binaryCursor; - } - - // - // Part of the JDBC2 support, but convenient to implement here. - // - - public void setFetchSize(int rows) throws SQLException - { - fetchSize = rows; - } - - - public int getFetchSize() throws SQLException - { - return fetchSize; - } - - public boolean next() throws SQLException - { - if (rows == null) - throw new PSQLException("postgresql.con.closed", PSQLState.CONNECTION_DOES_NOT_EXIST); - - if (++current_row >= rows.size()) - { - String cursorName = statement.getFetchingCursorName(); - if (cursorName == null || lastFetchSize == 0 || rows.size() < lastFetchSize) - return false; // Not doing a cursor-based fetch or the last fetch was the end of the query - - // Use the ref to the statement to get - // the details we need to do another cursor - // query - it will use reinit() to repopulate this - // with the right data. - - // NB: We can reach this point with fetchSize == 0 - // if the fetch size is changed halfway through reading results. - // Use "FETCH FORWARD ALL" in that case to complete the query. - String[] sql = new String[] { - fetchSize == 0 ? ("FETCH FORWARD ALL FROM " + cursorName) : - ("FETCH FORWARD " + fetchSize + " FROM " + cursorName) - }; - - QueryExecutor.execute(sql, - new String[0], - this); - - // Test the new rows array. - lastFetchSize = fetchSize; - if (rows.size() == 0) - return false; - - // Otherwise reset the counter and let it go on... - current_row = 0; - } - - this_row = (byte [][])rows.elementAt(current_row); - - rowBuffer = new byte[this_row.length][]; - System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length); - return true; - } - - public void close() throws SQLException - { - //release resources held (memory for tuples) - if (rows != null) - { - rows = null; - } - } - - public boolean wasNull() throws SQLException - { - return wasNullFlag; - } - - public String getString(int columnIndex) throws SQLException - { - checkResultSet( columnIndex ); - wasNullFlag = (this_row[columnIndex - 1] == null); - if (wasNullFlag) - return null; - - Encoding encoding = connection.getEncoding(); - return trimString(columnIndex, encoding.decode(this_row[columnIndex-1])); - } - - public boolean getBoolean(int columnIndex) throws SQLException - { - return toBoolean( getString(columnIndex) ); - } - - - public byte getByte(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null ) - { - try - { - switch(fields[columnIndex-1].getSQLType()) - { - case Types.NUMERIC: - case Types.REAL: - case Types.DOUBLE: - case Types.FLOAT: - case Types.DECIMAL: - int loc = s.indexOf("."); - if (loc!=-1 && Integer.parseInt(s.substring(loc+1,s.length()))==0) - { - s = s.substring(0,loc); - } - break; - case Types.CHAR: - s = s.trim(); - break; - } - if ( s.length() == 0 ) return 0; - return Byte.parseByte(s); - } - catch (NumberFormatException e) - { - throw new PSQLException("postgresql.res.badbyte", PSQLState.NUMERIC_VALUE_OUT_OF_RANGE, s); - } - } - return 0; // SQL NULL - } - - public short getShort(int columnIndex) throws SQLException - { - String s = getFixedString(columnIndex); - - if (s != null) - { - try - { - switch(fields[columnIndex-1].getSQLType()) - { - case Types.NUMERIC: - case Types.REAL: - case Types.DOUBLE: - case Types.FLOAT: - case Types.DECIMAL: - int loc = s.indexOf("."); - if (loc!=-1 && Integer.parseInt(s.substring(loc+1,s.length()))==0) - { - s = s.substring(0,loc); - } - break; - case Types.CHAR: - s = s.trim(); - break; - } - return Short.parseShort(s); - } - catch (NumberFormatException e) - { - throw new PSQLException("postgresql.res.badshort", PSQLState.NUMERIC_VALUE_OUT_OF_RANGE, s); - } - } - return 0; // SQL NULL - } - - public int getInt(int columnIndex) throws SQLException - { - return toInt( getFixedString(columnIndex) ); - } - - public long getLong(int columnIndex) throws SQLException - { - return toLong( getFixedString(columnIndex) ); - } - - public float getFloat(int columnIndex) throws SQLException - { - return toFloat( getFixedString(columnIndex) ); - } - - public double getDouble(int columnIndex) throws SQLException - { - return toDouble( getFixedString(columnIndex) ); - } - - public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException - { - return toBigDecimal( getFixedString(columnIndex), scale ); - } - - /* - * Get the value of a column in the current row as a Java byte array. - * - * <p>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. - * - * <p><b>Be warned</b> 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 - { - checkResultSet( columnIndex ); - wasNullFlag = (this_row[columnIndex - 1] == null); - if (!wasNullFlag) - { - if (binaryCursor) - { - //If the data is already binary then just return it - return this_row[columnIndex - 1]; - } - else if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports the bytea datatype for byte arrays - if (fields[columnIndex - 1].getPGType().equals("bytea")) - { - return trimBytes(columnIndex, PGbytea.toBytes(this_row[columnIndex - 1])); - } - else - { - return trimBytes(columnIndex, this_row[columnIndex - 1]); - } - } - else - { - //Version 7.1 and earlier supports LargeObjects for byte arrays - // Handle OID's as BLOBS - 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 trimBytes(columnIndex, buf); - } - else - { - return trimBytes(columnIndex, this_row[columnIndex - 1]); - } - } - } - return null; - } - - public java.sql.Date getDate(int columnIndex) throws SQLException - { - return toDate( getString(columnIndex) ); - } - - public Time getTime(int columnIndex) throws SQLException - { - return toTime( getString(columnIndex), this, fields[columnIndex - 1].getPGType() ); - } - - public Timestamp getTimestamp(int columnIndex) throws SQLException - { - return toTimestamp( getString(columnIndex), this, fields[columnIndex - 1].getPGType() ); - } - - public InputStream getAsciiStream(int columnIndex) throws SQLException - { - checkResultSet( columnIndex ); - wasNullFlag = (this_row[columnIndex - 1] == null); - if (wasNullFlag) - return null; - - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports AsciiStream for all the PG text types - //As the spec/javadoc for this method indicate this is to be used for - //large text values (i.e. LONGVARCHAR) PG doesn't have a separate - //long string datatype, but with toast the text datatype is capable of - //handling very large values. Thus the implementation ends up calling - //getString() since there is no current way to stream the value from the server - try - { - return new ByteArrayInputStream(getString(columnIndex).getBytes("ASCII")); - } - catch (UnsupportedEncodingException l_uee) - { - throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_uee); - } - } - else - { - // In 7.1 Handle as BLOBS so return the LargeObject input stream - return getBinaryStream(columnIndex); - } - } - - public InputStream getUnicodeStream(int columnIndex) throws SQLException - { - checkResultSet( columnIndex ); - wasNullFlag = (this_row[columnIndex - 1] == null); - if (wasNullFlag) - return null; - - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports AsciiStream for all the PG text types - //As the spec/javadoc for this method indicate this is to be used for - //large text values (i.e. LONGVARCHAR) PG doesn't have a separate - //long string datatype, but with toast the text datatype is capable of - //handling very large values. Thus the implementation ends up calling - //getString() since there is no current way to stream the value from the server - try - { - return new ByteArrayInputStream(getString(columnIndex).getBytes("UTF-8")); - } - catch (UnsupportedEncodingException l_uee) - { - throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_uee); - } - } - else - { - // In 7.1 Handle as BLOBS so return the LargeObject input stream - return getBinaryStream(columnIndex); - } - } - - public InputStream getBinaryStream(int columnIndex) throws SQLException - { - checkResultSet( columnIndex ); - wasNullFlag = (this_row[columnIndex - 1] == null); - if (wasNullFlag) - return null; - - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports BinaryStream for all PG bytea type - //As the spec/javadoc for this method indicate this is to be used for - //large binary values (i.e. LONGVARBINARY) PG doesn't have a separate - //long binary datatype, but with toast the bytea datatype is capable of - //handling very large values. Thus the implementation ends up calling - //getBytes() since there is no current way to stream the value from the server - byte b[] = getBytes(columnIndex); - if (b != null) - return new ByteArrayInputStream(b); - } - else - { - // In 7.1 Handle as BLOBS so return the LargeObject input stream - if ( fields[columnIndex - 1].getOID() == 26) - { - LargeObjectManager lom = connection.getLargeObjectAPI(); - LargeObject lob = lom.open(getInt(columnIndex)); - return lob.getInputStream(); - } - } - return null; - } - - 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)); - } - - public SQLWarning getWarnings() throws SQLException - { - return warnings; - } - - public void clearWarnings() throws SQLException - { - warnings = null; - } - - public void addWarnings(SQLWarning warnings) - { - if ( this.warnings != null ) - this.warnings.setNextWarning(warnings); - else - this.warnings = warnings; - } - - public String getCursorName() throws SQLException - { - return (connection.getCursorName()); - } - - /* - * Get the value of a column in the current row as a Java object - * - * <p>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. - * - * <p>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 PSQLException("postgresql.res.colrange", PSQLState.INVALID_PARAMETER_VALUE); - 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 getBoolean(columnIndex) ? Boolean.TRUE : Boolean.FALSE; - case Types.SMALLINT: - return new Short(getShort(columnIndex)); - case Types.INTEGER: - return new Integer(getInt(columnIndex)); - case Types.BIGINT: - return new Long(getLong(columnIndex)); - case Types.NUMERIC: - return getBigDecimal - (columnIndex, (field.getMod() == -1) ? -1 : ((field.getMod() - 4) & 0xffff)); - 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); - case Types.BINARY: - case Types.VARBINARY: - return getBytes(columnIndex); - default: - String type = field.getPGType(); - - // if the backend doesn't know the type then coerce to String - if (type.equals("unknown")) - { - return getString(columnIndex); - } - // Specialized support for ref cursors is neater. - else if (type.equals("refcursor")) - { - String cursorName = getString(columnIndex); - return statement.createRefCursorResultSet(cursorName); - } - else - { - return connection.getObject(field.getPGType(), getString(columnIndex)); - } - } - } - - public Object getObject(String columnName) throws SQLException - { - return getObject(findColumn(columnName)); - } - - /* - * Map a ResultSet column name to a ResultSet column index - */ - public int findColumn(String columnName) throws SQLException - { - int i; - - final int flen = fields.length; - for (i = 0 ; i < flen; ++i) - if (fields[i].getName().equalsIgnoreCase(columnName)) - return (i + 1); - throw new PSQLException ("postgresql.res.colname", columnName); - } - - - /* - * We at times need to know if the resultSet we are working - * with is the result of an UPDATE, DELETE or INSERT (in which - * case, we only have a row count), or of a SELECT operation - * (in which case, we have multiple fields) - this routine - * tells us. - */ - public boolean reallyResultSet() - { - return (fields != null); - } - - /* - * Since ResultSets can be chained, we need some method of - * finding the next one in the chain. The method getNext() - * returns the next one in the chain. - * - * @return the next ResultSet, or null if there are none - */ - public ResultSet getNext() - { - return (ResultSet)next; - } - - /* - * This following method allows us to add a ResultSet object - * to the end of the current chain. - */ - public void append(BaseResultSet r) - { - if (next == null) - next = r; - else - next.append(r); - } - - /* - * If we are just a place holder for results, we still need - * to get an updateCount. This method returns it. - */ - public int getResultCount() - { - return updateCount; - } - - /* - * We also need to provide a couple of auxiliary functions for - * the implementation of the ResultMetaData functions. In - * particular, we need to know the number of rows and the - * number of columns. Rows are also known as Tuples - */ - public int getTupleCount() - { - return rows.size(); - } - - /* - * getColumnCount returns the number of columns - */ - public int getColumnCount() - { - return fields.length; - } - - /* - * Returns the status message from the backend.<p> - * It is used internally by the driver. - */ - public String getStatusString() - { - return status; - } - - /* - * returns the OID of a field.<p> - * It is used internally by the driver. - */ - public int getColumnOID(int field) - { - return fields[field -1].getOID(); - } - - /* - * returns the OID of the last inserted row. Deprecated in 7.2 because - * range for OID values is greater than java signed int. - * @deprecated Replaced by getLastOID() in 7.2 - */ - public int getInsertedOID() - { - return (int) getLastOID(); - } - - - /* - * returns the OID of the last inserted row - * @since 7.2 - */ - public long getLastOID() - { - return insertOID; - } - - /* - * This is used to fix get*() methods on Money fields. It should only be - * used by those methods! - * - * It converts ($##.##) to -##.## and $##.## to ##.## - */ - public String getFixedString(int col) throws SQLException - { - String s = getString(col); - - // Handle SQL Null - wasNullFlag = (this_row[col - 1] == null); - if (wasNullFlag) - return null; - - // if we don't have at least 2 characters it can't be money. - if (s.length() < 2) - return s; - - // Handle Money - if (s.charAt(0) == '(') - { - s = "-" + PGtokenizer.removePara(s).substring(1); - } - if (s.charAt(0) == '$') - { - s = s.substring(1); - } - else if (s.charAt(0) == '-' && s.charAt(1) == '$') - { - s = "-" + s.substring(2); - } - - return s; - } - - protected void checkResultSet( int column ) throws SQLException - { - if ( this_row == null ) - throw new PSQLException("postgresql.res.nextrequired"); - if ( column < 1 || column > fields.length ) - throw new PSQLException("postgresql.res.colrange", PSQLState.INVALID_PARAMETER_VALUE ); - } - - //----------------- Formatting Methods ------------------- - - public static boolean toBoolean(String s) - { - if (s != null) - { - s = s.trim(); - - if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("t")) - return true; - - try - { - if (Double.valueOf(s).doubleValue()==1) - return true; - } - catch (NumberFormatException e) - { - } - } - return false; // SQL NULL - } - - public static int toInt(String s) throws SQLException - { - if (s != null) - { - try - { - s = s.trim(); - return Integer.parseInt(s); - } - catch (NumberFormatException e) - { - throw new PSQLException ("postgresql.res.badint", PSQLState.NUMERIC_VALUE_OUT_OF_RANGE, s); - } - } - return 0; // SQL NULL - } - - public static long toLong(String s) throws SQLException - { - if (s != null) - { - try - { - s = s.trim(); - return Long.parseLong(s); - } - catch (NumberFormatException e) - { - throw new PSQLException ("postgresql.res.badlong", PSQLState.NUMERIC_VALUE_OUT_OF_RANGE, s); - } - } - return 0; // SQL NULL - } - - public static BigDecimal toBigDecimal(String s, int scale) throws SQLException - { - BigDecimal val; - if (s != null) - { - try - { - s = s.trim(); - val = new BigDecimal(s); - } - catch (NumberFormatException e) - { - throw new PSQLException ("postgresql.res.badbigdec", PSQLState.NUMERIC_VALUE_OUT_OF_RANGE, s); - } - if (scale == -1) - return val; - try - { - return val.setScale(scale); - } - catch (ArithmeticException e) - { - throw new PSQLException ("postgresql.res.badbigdec", PSQLState.NUMERIC_VALUE_OUT_OF_RANGE, s); - } - } - return null; // SQL NULL - } - - public static float toFloat(String s) throws SQLException - { - if (s != null) - { - try - { - s = s.trim(); - return Float.valueOf(s).floatValue(); - } - catch (NumberFormatException e) - { - throw new PSQLException ("postgresql.res.badfloat", PSQLState.NUMERIC_VALUE_OUT_OF_RANGE, s); - } - } - return 0; // SQL NULL - } - - public static double toDouble(String s) throws SQLException - { - if (s != null) - { - try - { - s = s.trim(); - return Double.valueOf(s).doubleValue(); - } - catch (NumberFormatException e) - { - throw new PSQLException ("postgresql.res.baddouble", PSQLState.NUMERIC_VALUE_OUT_OF_RANGE, s); - } - } - return 0; // SQL NULL - } - - public static java.sql.Date toDate(String s) throws SQLException - { - if (s == null) - return null; - // length == 10: SQL Date - // length > 10: SQL Timestamp, assumes PGDATESTYLE=ISO - try - { - s = s.trim(); - return java.sql.Date.valueOf((s.length() == 10) ? s : s.substring(0, 10)); - } - catch (NumberFormatException e) - { - throw new PSQLException("postgresql.res.baddate",PSQLState.BAD_DATETIME_FORMAT, s); - } - } - - public static Time toTime(String s, BaseResultSet resultSet, String pgDataType) throws SQLException - { - if (s == null) - return null; // SQL NULL - try - { - s = s.trim(); - if (s.length() == 8) - { - //value is a time value - return java.sql.Time.valueOf(s); - } - else if (s.indexOf(".") == 8) - { - //value is a time value with fractional seconds - java.sql.Time l_time = java.sql.Time.valueOf(s.substring(0, 8)); - String l_strMillis = s.substring(9); - if (l_strMillis.length() > 3) - l_strMillis = l_strMillis.substring(0, 3); - int l_millis = Integer.parseInt(l_strMillis); - if (l_millis < 10) - { - l_millis = l_millis * 100; - } - else if (l_millis < 100) - { - l_millis = l_millis * 10; - } - return new java.sql.Time(l_time.getTime() + l_millis); - } - else - { - //value is a timestamp - return new java.sql.Time(toTimestamp(s, resultSet, pgDataType).getTime()); - } - } - catch (NumberFormatException e) - { - throw new PSQLException("postgresql.res.badtime", PSQLState.BAD_DATETIME_FORMAT, s); - } - } - - /** - * Parse a string and return a timestamp representing its value. - * - * The driver is set to return ISO date formated strings. We modify this - * string from the ISO format to a format that Java can understand. Java - * expects timezone info as 'GMT+09:00' where as ISO gives '+09'. - * Java also expects fractional seconds to 3 places where postgres - * will give, none, 2 or 6 depending on the time and postgres version. - * From version 7.2 postgres returns fractional seconds to 6 places. - * - * According to the Timestamp documentation, fractional digits are kept - * in the nanos field of Timestamp and not in the milliseconds of Date. - * Thus, parsing for fractional digits is entirely separated from the - * rest. - * - * The method assumes that there are no more than 9 fractional - * digits given. Undefined behavior if this is not the case. - * - * @param s The ISO formated date string to parse. - * @param resultSet The ResultSet this date is part of. - * - * @return null if s is null or a timestamp of the parsed string s. - * - * @throws SQLException if there is a problem parsing s. - **/ - public static Timestamp toTimestamp(String s, BaseResultSet resultSet, String pgDataType) - throws SQLException - { - BaseResultSet rs = resultSet; - if (s == null) - return null; - - s = s.trim(); - // We must be synchronized here incase more theads access the ResultSet - // bad practice but possible. Anyhow this is to protect sbuf and - // SimpleDateFormat objects - synchronized (rs) - { - StringBuffer l_sbuf = rs.getStringBuffer(); - SimpleDateFormat df = null; - if ( Driver.logDebug ) - Driver.debug("the data from the DB is " + s); - - // If first time, create the buffer, otherwise clear it. - if (l_sbuf == null) - l_sbuf = new StringBuffer(32); - else - { - l_sbuf.setLength(0); - } - - // Copy s into sbuf for parsing. - l_sbuf.append(s); - int slen = s.length(); - - // For a Timestamp, the fractional seconds are stored in the - // nanos field. As a DateFormat is used for parsing which can - // only parse to millisecond precision and which returns a - // Date object, the fractional second parsing is completely - // separate. - int nanos = 0; - - if (slen > 19) - { - // The len of the ISO string to the second value is 19 chars. If - // greater then 19, there may be tz info and perhaps fractional - // second info which we need to change to java to read it. - - // cut the copy to second value "2001-12-07 16:29:22" - int i = 19; - l_sbuf.setLength(i); - - char c = s.charAt(i++); - if (c == '.') - { - // Found a fractional value. - final int start = i; - while (true) - { - c = s.charAt(i++); - if (!Character.isDigit(c)) - break; - if (i == slen) - { - i++; - break; - } - } - - // The range [start, i - 1) contains all fractional digits. - final int end = i - 1; - try - { - nanos = Integer.parseInt(s.substring(start, end)); - } - catch (NumberFormatException e) - { - throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, e); - } - - // The nanos field stores nanoseconds. Adjust the parsed - // value to the correct magnitude. - for (int digitsToNano = 9 - (end - start); - digitsToNano > 0; --digitsToNano) - nanos *= 10; - } - - if (i < slen) - { - // prepend the GMT part and then add the remaining bit of - // the string. - l_sbuf.append(" GMT"); - l_sbuf.append(c); - l_sbuf.append(s.substring(i, slen)); - - // Lastly, if the tz part doesn't specify the :MM part then - // we add ":00" for java. - if (slen - i < 5) - l_sbuf.append(":00"); - - // we'll use this dateformat string to parse the result. - df = rs.getTimestampTZFormat(); - } - else - { - // Just found fractional seconds but no timezone. - //If timestamptz then we use GMT, else local timezone - if (pgDataType.equals("timestamptz")) - { - l_sbuf.append(" GMT"); - df = rs.getTimestampTZFormat(); - } - else - { - df = rs.getTimestampFormat(); - } - } - } - else if (slen == 19) - { - // No tz or fractional second info. - //If timestamptz then we use GMT, else local timezone - if (pgDataType.equals("timestamptz")) - { - l_sbuf.append(" GMT"); - df = rs.getTimestampTZFormat(); - } - else - { - df = rs.getTimestampFormat(); - } - } - else - { - if (slen == 8 && s.equals("infinity")) - //java doesn't have a concept of postgres's infinity - //so set to an arbitrary future date - s = "9999-01-01"; - if (slen == 9 && s.equals("-infinity")) - //java doesn't have a concept of postgres's infinity - //so set to an arbitrary old date - s = "0001-01-01"; - - // We must just have a date. This case is - // needed if this method is called on a date - // column - df = rs.getDateFormat(); - } - - try - { - // All that's left is to parse the string and return the ts. - if ( Driver.logDebug ) - Driver.debug("the data after parsing is " - + l_sbuf.toString() + " with " + nanos + " nanos"); - - Timestamp result = new Timestamp(df.parse(l_sbuf.toString()).getTime()); - result.setNanos(nanos); - return result; - } - catch (ParseException e) - { - throw new PSQLException("postgresql.res.badtimestamp", PSQLState.BAD_DATETIME_FORMAT, new Integer(e.getErrorOffset()), s); - } - } - } - - private boolean isColumnTrimmable(int columnIndex) throws SQLException - { - switch (fields[columnIndex-1].getSQLType()) - { - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - case Types.BINARY: - case Types.VARBINARY: - case Types.LONGVARBINARY: - return true; - } - return false; - } - - private byte[] trimBytes(int p_columnIndex, byte[] p_bytes) throws SQLException - { - int l_maxSize = statement.getMaxFieldSize(); - //we need to trim if maxsize is set and the length is greater than maxsize and the - //type of this column is a candidate for trimming - if (l_maxSize > 0 && p_bytes.length > l_maxSize && isColumnTrimmable(p_columnIndex)) - { - byte[] l_bytes = new byte[l_maxSize]; - System.arraycopy (p_bytes, 0, l_bytes, 0, l_maxSize); - return l_bytes; - } - else - { - return p_bytes; - } - } - - private String trimString(int p_columnIndex, String p_string) throws SQLException - { - int l_maxSize = statement.getMaxFieldSize(); - //we need to trim if maxsize is set and the length is greater than maxsize and the - //type of this column is a candidate for trimming - if (l_maxSize > 0 && p_string.length() > l_maxSize && isColumnTrimmable(p_columnIndex)) - { - return p_string.substring(0,l_maxSize); - } - else - { - return p_string; - } - } - - public SimpleDateFormat getTimestampTZFormat() { - if (m_tstzFormat == null) { - m_tstzFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); - } - return m_tstzFormat; - } - public SimpleDateFormat getTimestampFormat() { - if (m_tsFormat == null) { - m_tsFormat = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss"); - } - return m_tsFormat; - } - public SimpleDateFormat getDateFormat() { - if (m_dateFormat == null) { - m_dateFormat = new SimpleDateFormat("yyyy-MM-dd"); - } - return m_dateFormat; - } -} - diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSetMetaData.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSetMetaData.java deleted file mode 100644 index fc0d5ee33a..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSetMetaData.java +++ /dev/null @@ -1,462 +0,0 @@ -package org.postgresql.jdbc1; - - -import org.postgresql.core.Field; -import org.postgresql.util.PSQLException; -import org.postgresql.util.PSQLState; -import java.sql.SQLException; -import java.sql.Types; -import java.util.Vector; - -public abstract class AbstractJdbc1ResultSetMetaData -{ - - protected Vector rows; - protected 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 AbstractJdbc1ResultSetMetaData(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).getPGType(); - - return type_name.equals("cash") || type_name.equals("money"); - } - - /* - * Indicates the nullability of values in the designated column. - * - * @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 - { - /* - * TODO This needs a real implementation, taking into account columns - * defined with NOT NULL or PRIMARY KEY, CHECK constraints, views, - * functions etc. - */ - return java.sql.ResultSetMetaData.columnNullableUnknown; - } - - /* - * 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 - { - Field f = getField(column); - String type_name = f.getPGType(); - int typmod = f.getMod(); - - // I looked at other JDBC implementations and couldn't find a consistent - // interpretation of the "display size" for numeric values, so this is our's - // FIXME: currently, only types with a SQL92 or SQL3 pendant are implemented - jens@jens.de - - // fixed length data types - if (type_name.equals( "int2" )) - return 6; // -32768 to +32768 (5 digits and a sign) - if (type_name.equals( "int4" ) - || type_name.equals( "oid" )) - return 11; // -2147483648 to +2147483647 - if (type_name.equals( "int8" )) - return 20; // -9223372036854775808 to +9223372036854775807 - if (type_name.equals( "money" )) - return 12; // MONEY = DECIMAL(9,2) - if (type_name.equals( "float4" )) - return 11; // i checked it out ans wasn't able to produce more than 11 digits - if (type_name.equals( "float8" )) - return 20; // dito, 20 - if (type_name.equals( "char" )) - return 1; - if (type_name.equals( "bool" )) - return 1; - if (type_name.equals( "date" )) - return 14; // "01/01/4713 BC" - "31/12/32767 AD" - if (type_name.equals( "time" )) - return 8; // 00:00:00-23:59:59 - if (type_name.equals( "timestamp" )) - return 22; // hhmmm ... the output looks like this: 1999-08-03 22:22:08+02 - - // variable length fields - typmod -= 4; - if (type_name.equals( "bpchar" ) - || type_name.equals( "varchar" )) - return typmod; // VARHDRSZ=sizeof(int32)=4 - if (type_name.equals( "numeric" )) - return ( (typmod >> 16) & 0xffff ) - + 1 + ( typmod & 0xffff ); // DECIMAL(p,s) = (p digits).(s digits) - - // if we don't know better - return f.getLength(); - } - - /* - * 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.getName(); - 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; - case Types.NUMERIC: - Field f = getField(column); - if (f != null) - return ((0xFFFF0000)&f.getMod()) >> 16; - else - 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; - case Types.NUMERIC: - Field f = getField(column); - if (f != null) - return (((0x0000FFFF)&f.getMod()) - 4); - else - 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 org.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).getPGType(); - } - - /* - * 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 - { - return !isReadOnly(column); - } - - /* - * 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 false; - } - - // ******************************************************** - // 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 PSQLException("postgresql.res.colrange", PSQLState.INVALID_PARAMETER_VALUE); - return fields[columnIndex - 1]; - } -} - diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java deleted file mode 100644 index c8644f42ac..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java +++ /dev/null @@ -1,2309 +0,0 @@ -package org.postgresql.jdbc1; - -import org.postgresql.core.BaseConnection; -import org.postgresql.core.BaseResultSet; -import org.postgresql.core.BaseStatement; -import org.postgresql.core.Field; -import org.postgresql.core.QueryExecutor; -import org.postgresql.largeobject.LargeObject; -import org.postgresql.largeobject.LargeObjectManager; -import org.postgresql.util.PGbytea; -import org.postgresql.util.PGobject; -import org.postgresql.util.PSQLException; -import org.postgresql.util.PSQLState; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.math.BigDecimal; -import java.sql.CallableStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.Time; -import java.sql.Timestamp; -import java.sql.Types; -import java.util.Vector; - -/* $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java,v 1.44 2003/12/12 18:36:19 davec Exp $ - * This class defines methods of the jdbc1 specification. This class is - * extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2 - * methods. The real Statement class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Statement - */ -public abstract class AbstractJdbc1Statement implements BaseStatement -{ - // The connection who created us - protected BaseConnection connection; - - /** The warnings chain. */ - protected SQLWarning warnings = null; - - /** Maximum number of rows to return, 0 = unlimited */ - protected int maxrows = 0; - - /** Number of rows to get in a batch. */ - protected int fetchSize = 0; - - /** Timeout (in seconds) for a query (not used) */ - protected int timeout = 0; - - protected boolean replaceProcessingEnabled = true; - - /** The current results */ - protected BaseResultSet result = null; - - // Static variables for parsing SQL when replaceProcessing is true. - private static final short IN_SQLCODE = 0; - private static final short IN_STRING = 1; - private static final short BACKSLASH = 2; - private static final short ESC_TIMEDATE = 3; - - // Some performance caches - private StringBuffer sbuf = new StringBuffer(32); - - protected String[] m_sqlFragments; // Query fragments. - private String[] m_executeSqlFragments; // EXECUTE(...) if useServerPrepare - protected Object[] m_binds = new Object[0]; // Parameter values - - protected String[] m_bindTypes = new String[0]; // Parameter types, for PREPARE(...) - protected String m_statementName = null; // Allocated PREPARE statement name for server-prepared statements - protected String m_cursorName = null; // Allocated DECLARE cursor name for cursor-based fetch - - // Constants for allowXXX and m_isSingleStatement vars, below. - // The idea is to defer the cost of examining the query until we really need to know, - // but don't reexamine it every time thereafter. - - private static final short UNKNOWN = 0; // Don't know yet, examine the query. - private static final short NO = 1; // Don't use feature - private static final short YES = 2; // Do use feature - - private short m_isSingleDML = UNKNOWN; // Is the query a single SELECT/UPDATE/INSERT/DELETE? - private short m_isSingleSelect = UNKNOWN; // Is the query a single SELECT? - private short m_isSingleStatement = UNKNOWN; // Is the query a single statement? - - private boolean m_useServerPrepare = false; - - // m_preparedCount is used for naming of auto-cursors and must - // be synchronized so that multiple threads using the same - // connection don't stomp over each others cursors. - private static int m_preparedCount = 1; - private synchronized static int next_preparedCount() - { - return m_preparedCount++; - } - - //Used by the callablestatement style methods - private static final String JDBC_SYNTAX = "{[? =] call <some_function> ([? [,?]*]) }"; - private static final String RESULT_ALIAS = "result"; - private String originalSql = ""; - private boolean isFunction; - // functionReturnType contains the user supplied value to check - // testReturn contains a modified version to make it easier to - // check the getXXX methods.. - private int functionReturnType; - private int testReturn; - // returnTypeSet is true when a proper call to registerOutParameter has been made - private boolean returnTypeSet; - protected Object callResult; - protected int maxfieldSize = 0; - - public abstract BaseResultSet createResultSet(Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException; - - public AbstractJdbc1Statement (BaseConnection connection) - { - this.connection = connection; - } - - public AbstractJdbc1Statement (BaseConnection connection, String p_sql) throws SQLException - { - this.connection = connection; - parseSqlStmt(p_sql); // this allows Callable stmt to override - } - - public BaseConnection getPGConnection() { - return connection; - } - - public String getFetchingCursorName() { - return m_cursorName; - } - - public int getFetchSize() { - return fetchSize; - } - - protected void parseSqlStmt (String p_sql) throws SQLException - { - String l_sql = p_sql; - - l_sql = replaceProcessing(l_sql); - - if (this instanceof CallableStatement) - { - l_sql = modifyJdbcCall(l_sql); - } - - Vector v = new Vector(); - boolean inQuotes = false; - int lastParmEnd = 0, i; - - m_isSingleSelect = m_isSingleDML = UNKNOWN; - m_isSingleStatement = YES; - - for (i = 0; i < l_sql.length(); ++i) - { - int c = l_sql.charAt(i); - - if (c == '\'') - inQuotes = !inQuotes; - if (c == '?' && !inQuotes) - { - v.addElement(l_sql.substring (lastParmEnd, i)); - lastParmEnd = i + 1; - } - if (c == ';' && !inQuotes) - m_isSingleStatement = m_isSingleSelect = m_isSingleDML = NO; - } - v.addElement(l_sql.substring (lastParmEnd, l_sql.length())); - - m_sqlFragments = new String[v.size()]; - m_binds = new Object[v.size() - 1]; - m_bindTypes = new String[v.size() - 1]; - - for (i = 0 ; i < m_sqlFragments.length; ++i) - m_sqlFragments[i] = (String)v.elementAt(i); - - } - - /* - * Deallocate resources allocated for the current query - * in preparation for replacing it with a new query. - */ - private void deallocateQuery() - { - //If we have already created a server prepared statement, we need - //to deallocate the existing one - if (m_statementName != null) - { - try - { - connection.execSQL("DEALLOCATE " + m_statementName); - } - catch (Exception e) - { - } - } - - m_statementName = null; - m_cursorName = null; // automatically closed at end of txn anyway - m_executeSqlFragments = null; - m_isSingleStatement = m_isSingleSelect = m_isSingleDML = UNKNOWN; - } - - /* - * 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 p_sql) throws SQLException - { - deallocateQuery(); - - String l_sql = replaceProcessing(p_sql); - m_sqlFragments = new String[] {l_sql}; - m_binds = new Object[0]; - - return executeQuery(); - } - - /* - * 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 - { - this.execute(); - - while (result != null && !result.reallyResultSet()) - result = (BaseResultSet) result.getNext(); - if (result == null) - throw new PSQLException("postgresql.stat.noresult", PSQLState.NO_DATA); - return (ResultSet) 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 p_sql) throws SQLException - { - deallocateQuery(); - - String l_sql = replaceProcessing(p_sql); - m_sqlFragments = new String[] {l_sql}; - m_binds = new Object[0]; - - return executeUpdate(); - } - - /* - * 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 - { - this.execute(); - if (result.reallyResultSet()) - throw new PSQLException("postgresql.stat.result"); - return this.getUpdateCount(); - } - - /* - * 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 p_sql) throws SQLException - { - deallocateQuery(); - - String l_sql = replaceProcessing(p_sql); - m_sqlFragments = new String[] {l_sql}; - m_binds = new Object[0]; - - return execute(); - } - - /* - * Check if the current query is a single statement. - */ - private boolean isSingleStatement() - { - if (m_isSingleStatement != UNKNOWN) - return m_isSingleStatement == YES; - - // Crude detection of multiple statements. This could be - // improved by parsing the whole query for quotes, but is - // it worth it given that the only queries that get here are - // unparameterized queries? - - for (int i = 0; i < m_sqlFragments.length; ++i) { // a bit redundant, but .. - if (m_sqlFragments[i].indexOf(';') != -1) { - m_isSingleStatement = NO; - return false; - } - } - - m_isSingleStatement = YES; - return true; - } - - /* - * Helper for isSingleSelect() and isSingleDML(): computes values - * of m_isSingleDML and m_isSingleSelect. - */ - private void analyzeStatementType() - { - if (!isSingleStatement()) { - m_isSingleSelect = m_isSingleDML = NO; - return; - } - - String compare = m_sqlFragments[0].trim().toLowerCase(); - if (compare.startsWith("select")) { - m_isSingleSelect = m_isSingleDML = YES; - return; - } - - m_isSingleSelect = NO; - - if (!compare.startsWith("update") && - !compare.startsWith("delete") && - !compare.startsWith("insert")) { - m_isSingleDML = NO; - return; - } - - m_isSingleDML = YES; - } - - /* - * Check if the current query is a single SELECT. - */ - private boolean isSingleSelect() - { - if (m_isSingleSelect == UNKNOWN) - analyzeStatementType(); - - return m_isSingleSelect == YES; - } - - /* - * Check if the current query is a single SELECT/UPDATE/INSERT/DELETE. - */ - private boolean isSingleDML() - { - if (m_isSingleDML == UNKNOWN) - analyzeStatementType(); - - return m_isSingleDML == YES; - } - - /* - * Return the query fragments to use for a server-prepared statement. - * The first query executed will include a PREPARE and EXECUTE; - * subsequent queries will just be an EXECUTE. - */ - private String[] transformToServerPrepare() { - if (m_statementName != null) - return m_executeSqlFragments; - - // First time through. - m_statementName = "JDBC_STATEMENT_" + m_preparedCount++; - - // Set up m_executeSqlFragments - m_executeSqlFragments = new String[m_sqlFragments.length]; - m_executeSqlFragments[0] = "EXECUTE " + m_statementName; - if (m_sqlFragments.length > 1) { - m_executeSqlFragments[0] += "("; - for (int i = 1; i < m_bindTypes.length; i++) - m_executeSqlFragments[i] = ", "; - m_executeSqlFragments[m_bindTypes.length] = ")"; - } - - // Set up the PREPARE. - String[] prepareSqlFragments = new String[m_sqlFragments.length]; - System.arraycopy(m_sqlFragments, 0, prepareSqlFragments, 0, m_sqlFragments.length); - - synchronized (sbuf) { - sbuf.setLength(0); - sbuf.append("PREPARE "); - sbuf.append(m_statementName); - if (m_sqlFragments.length > 1) { - sbuf.append("("); - for (int i = 0; i < m_bindTypes.length; i++) { - if (i != 0) sbuf.append(", "); - sbuf.append(m_bindTypes[i]); - } - sbuf.append(")"); - } - sbuf.append(" AS "); - sbuf.append(m_sqlFragments[0]); - for (int i = 1; i < m_sqlFragments.length; i++) { - sbuf.append(" $"); - sbuf.append(i); - sbuf.append(" "); - sbuf.append(m_sqlFragments[i]); - } - sbuf.append("; "); - sbuf.append(m_executeSqlFragments[0]); - - prepareSqlFragments[0] = sbuf.toString(); - } - - System.arraycopy(m_executeSqlFragments, 1, prepareSqlFragments, 1, prepareSqlFragments.length - 1); - return prepareSqlFragments; - } - - /* - * Return the current query transformed into a cursor-based statement. - * This uses a new cursor on each query. - */ - private String[] transformToCursorFetch() - { - - // Pinch the prepared count for our own nefarious purposes. - m_cursorName = "JDBC_CURS_" + m_preparedCount++; - - // Create a cursor declaration and initial fetch statement from the original query. - int len = m_sqlFragments.length; - String[] cursorBasedSql = new String[len]; - System.arraycopy(m_sqlFragments, 0, cursorBasedSql, 0, len); - cursorBasedSql[0] = "DECLARE " + m_cursorName + " CURSOR FOR " + cursorBasedSql[0]; - cursorBasedSql[len-1] += "; FETCH FORWARD " + fetchSize + " FROM " + m_cursorName; - - // Make the cursor based query the one that will be used. - if (org.postgresql.Driver.logDebug) - org.postgresql.Driver.debug("using cursor based sql with cursor name " + m_cursorName); - - return cursorBasedSql; - } - - /** - * Do transformations to a query for server-side prepare or setFetchSize() cursor - * work. - * @return the query fragments to execute - */ - private String[] getQueryFragments() - { - // nb: isSingleXXX() are relatively expensive, avoid calling them unless we must. - - // We check the "mutable" bits of these conditions (which may change without - // a new query being created) here; isSingleXXX() only concern themselves with - // the query structure itself. - - // We prefer cursor-based-fetch over server-side-prepare here. - // Eventually a v3 implementation should let us do both at once. - if (fetchSize > 0 && !connection.getAutoCommit() && isSingleSelect()) - return transformToCursorFetch(); - - if (isUseServerPrepare() && isSingleDML()) - return transformToServerPrepare(); - - // Not server-prepare or cursor-fetch, just return a plain query. - return m_sqlFragments; - } - - /* - * 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 - { - if (isFunction && !returnTypeSet) - throw new PSQLException("postgresql.call.noreturntype", PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL); - if (isFunction) - { // set entry 1 to dummy entry.. - m_binds[0] = ""; // dummy entry which ensured that no one overrode - m_bindTypes[0] = PG_TEXT; - // and calls to setXXX (2,..) really went to first arg in a function call.. - } - - // New in 7.1, if we have a previous resultset then force it to close - // This brings us nearer to compliance, and helps memory management. - // Internal stuff will call ExecSQL directly, bypassing this. - - if (result != null) - { - java.sql.ResultSet rs = getResultSet(); - if (rs != null) - rs.close(); - } - - // Get the actual query fragments to run (might be a transformed version of - // the original fragments) - String[] fragments = getQueryFragments(); - - // New in 7.1, pass Statement so that ExecSQL can customise to it - result = QueryExecutor.execute(fragments, - m_binds, - this); - - //If we are executing a callable statement function set the return data - if (isFunction) - { - if (!result.reallyResultSet()) - throw new PSQLException("postgresql.call.noreturnval", PSQLState.NO_DATA); - if (!result.next ()) - throw new PSQLException ("postgresql.call.noreturnval", PSQLState.NO_DATA); - callResult = result.getObject(1); - int columnType = result.getMetaData().getColumnType(1); - if (columnType != functionReturnType) - throw new PSQLException ("postgresql.call.wrongrtntype", PSQLState.DATA_TYPE_MISMATCH, - new Object[]{ - "java.sql.Types=" + columnType, "java.sql.Types=" + functionReturnType }); - result.close (); - return true; - } - else - { - return (result != null && result.reallyResultSet()); - } - } - - /* - * 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. - * - * <p><B>Note:</B> 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. - * - * <p>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); - } - - - /* - * 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 (isFunction) - return 1; - if (result.reallyResultSet()) - return -1; - return 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 = (BaseResultSet) result.getNext(); - return (result != null && result.reallyResultSet()); - } - - - - /* - * Returns the status message from the current Result.<p> - * This is used internally by the driver. - * - * @return status message from backend - */ - public String getResultStatusString() - { - if (result == null) - return null; - return result.getStatusString(); - } - - /* - * 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 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 - { - if (max<0) throw new PSQLException("postgresql.input.rows.gt0"); - 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 - { - replaceProcessingEnabled = 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 - { - if (seconds<0) throw new PSQLException("postgresql.input.query.gt0"); - timeout = seconds; - } - - /** - * This adds a warning to the warning chain. - * @param msg message to add - */ - public void addWarning(String msg) - { - if (warnings != null) - warnings.setNextWarning(new SQLWarning(msg)); - else - warnings = new SQLWarning(msg); - } - - /* - * 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. - * - * <p>The Warning chain is automatically cleared each time a statement - * is (re)executed. - * - * <p><B>Note:</B> 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; - } - - /* - * 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 maxfieldSize; - } - - /* - * Sets the maxFieldSize - * - * @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 - { - if (max < 0) throw new PSQLException("postgresql.input.field.gt0"); - maxfieldSize = max; - } - - /* - * After this call, getWarnings returns null until a new warning - * is reported for this Statement. - * - * @exception SQLException if a database access error occurs - */ - public void clearWarnings() throws SQLException - { - warnings = null; - } - - /* - * Cancel can be used by one thread to cancel a statement that - * is being executed by another thread. - * <p> - * Not implemented, this method is a no-op. - * - * @exception SQLException only because thats the spec. - */ - public void cancel() throws SQLException - { - throw new PSQLException("postgresql.unimplemented", PSQLState.NOT_IMPLEMENTED); - } - - /* - * 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 - { - if (result != null && result.reallyResultSet()) - return (ResultSet) result; - return null; - } - - /* - * 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. - * - * <p><B>Note:</B> 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 - { - // Force the ResultSet to close - java.sql.ResultSet rs = getResultSet(); - if (rs != null) - rs.close(); - - deallocateQuery(); - - // Disasociate it from us (For Garbage Collection) - result = null; - } - - /** - * This finalizer ensures that statements that have allocated server-side - * resources free them when they become unreferenced. - */ - protected void finalize() { - try { close(); } - catch (SQLException e) {} - } - - /* - * Filter the SQL string of Java SQL Escape clauses. - * - * Currently implemented Escape clauses are those mentioned in 11.3 - * in the specification. Basically we look through the sql string for - * {d xxx}, {t xxx} or {ts xxx} in non-string sql code. When we find - * them, we just strip the escape part leaving only the xxx part. - * So, something like "select * from x where d={d '2001-10-09'}" would - * return "select * from x where d= '2001-10-09'". - */ - protected String replaceProcessing(String p_sql) - { - if (replaceProcessingEnabled) - { - // Since escape codes can only appear in SQL CODE, we keep track - // of if we enter a string or not. - StringBuffer newsql = new StringBuffer(p_sql.length()); - short state = IN_SQLCODE; - - int i = -1; - int len = p_sql.length(); - while (++i < len) - { - char c = p_sql.charAt(i); - switch (state) - { - case IN_SQLCODE: - if (c == '\'') // start of a string? - state = IN_STRING; - else if (c == '{') // start of an escape code? - if (i + 1 < len) - { - char next = p_sql.charAt(i + 1); - if (next == 'd') - { - state = ESC_TIMEDATE; - i++; - break; - } - else if (next == 't') - { - state = ESC_TIMEDATE; - i += (i + 2 < len && p_sql.charAt(i + 2) == 's') ? 2 : 1; - break; - } - } - newsql.append(c); - break; - - case IN_STRING: - if (c == '\'') // end of string? - state = IN_SQLCODE; - else if (c == '\\') // a backslash? - state = BACKSLASH; - - newsql.append(c); - break; - - case BACKSLASH: - state = IN_STRING; - - newsql.append(c); - break; - - case ESC_TIMEDATE: - if (c == '}') - state = IN_SQLCODE; // end of escape code. - else - newsql.append(c); - break; - } // end switch - } - - return newsql.toString(); - } - else - { - return p_sql; - } - } - - /* - * - * The following methods are postgres extensions and are defined - * in the interface BaseStatement - * - */ - - /* - * Returns the Last inserted/updated oid. Deprecated in 7.2 because - * range of OID values is greater than a java signed int. - * @deprecated Replaced by getLastOID in 7.2 - */ - public int getInsertedOID() throws SQLException - { - if (result == null) - return 0; - return (int) result.getLastOID(); - } - - /* - * Returns the Last inserted/updated oid. - * @return OID of last insert - * @since 7.2 - */ - public long getLastOID() throws SQLException - { - if (result == null) - return 0; - return result.getLastOID(); - } - - /* - * Set a parameter to SQL NULL - * - * <p><B>Note:</B> 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 - { - String l_pgType; - switch (sqlType) - { - case Types.INTEGER: - l_pgType = PG_INTEGER; - break; - case Types.TINYINT: - case Types.SMALLINT: - l_pgType = PG_INT2; - break; - case Types.BIGINT: - l_pgType = PG_INT8; - break; - case Types.REAL: - case Types.FLOAT: - l_pgType = PG_FLOAT; - break; - case Types.DOUBLE: - l_pgType = PG_DOUBLE; - break; - case Types.DECIMAL: - case Types.NUMERIC: - l_pgType = PG_NUMERIC; - break; - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - l_pgType = PG_TEXT; - break; - case Types.DATE: - l_pgType = PG_DATE; - break; - case Types.TIME: - l_pgType = PG_TIME; - break; - case Types.TIMESTAMP: - l_pgType = PG_TIMESTAMPTZ; - break; - case Types.BIT: - l_pgType = PG_BOOLEAN; - break; - case Types.BINARY: - case Types.VARBINARY: - case Types.LONGVARBINARY: - l_pgType = PG_BYTEA; - break; - case Types.OTHER: - l_pgType = PG_TEXT; - break; - default: - l_pgType = PG_TEXT; - } - bind(parameterIndex, "null", l_pgType); - } - - /* - * 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 - { - bind(parameterIndex, x ? "'1'" : "'0'", PG_BOOLEAN); - } - - /* - * 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 - { - bind(parameterIndex, Integer.toString(x), PG_INT2); - } - - /* - * 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 - { - bind(parameterIndex, Integer.toString(x), PG_INT2); - } - - /* - * 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 - { - bind(parameterIndex, Integer.toString(x), PG_INTEGER); - } - - /* - * 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 - { - bind(parameterIndex, Long.toString(x), PG_INT8); - } - - /* - * 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 - { - bind(parameterIndex, Float.toString(x), PG_FLOAT); - } - - /* - * 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 - { - bind(parameterIndex, Double.toString(x), PG_DOUBLE); - } - - /* - * 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 - { - if (x == null) - setNull(parameterIndex, Types.DECIMAL); - else - { - bind(parameterIndex, x.toString(), PG_NUMERIC); - } - } - - /* - * 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 - { - setString(parameterIndex, x, PG_TEXT); - } - - public void setString(int parameterIndex, String x, String type) throws SQLException - { - // if the passed string is null, then set this column to null - if (x == null) - setNull(parameterIndex, Types.VARCHAR); - else - { - // use the shared buffer object. Should never clash but this makes - // us thread safe! - synchronized (sbuf) - { - sbuf.setLength(0); - sbuf.ensureCapacity(2 + x.length() + (int)(x.length() / 10)); - sbuf.append('\''); - escapeString(x, sbuf); - sbuf.append('\''); - bind(parameterIndex, sbuf.toString(), type); - } - } - } - - private void escapeString(String p_input, StringBuffer p_output) { - for (int i = 0 ; i < p_input.length() ; ++i) - { - char c = p_input.charAt(i); - switch (c) - { - case '\\': - case '\'': - p_output.append('\\'); - p_output.append(c); - break; - case '\0': - throw new IllegalArgumentException("\\0 not allowed"); - default: - p_output.append(c); - } - } - } - - - /* - * 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. - * - * <p>Implementation note: - * <br>With org.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 - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports the bytea datatype for byte arrays - if (null == x) - { - setNull(parameterIndex, Types.VARBINARY); - } - else - { - setString(parameterIndex, PGbytea.toPGString(x), PG_BYTEA); - } - } - else - { - //Version 7.1 and earlier support done as LargeObjects - 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 - { - if (null == x) - { - setNull(parameterIndex, Types.DATE); - } - else - { - bind(parameterIndex, "'" + x.toString() + "'", PG_DATE); - } - } - - /* - * 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 - { - if (null == x) - { - setNull(parameterIndex, Types.TIME); - } - else - { - bind(parameterIndex, "'" + x.toString() + "'", PG_TIME); - } - } - - /* - * 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 - { - if (null == x) - { - setNull(parameterIndex, Types.TIMESTAMP); - } - else - { - // Use the shared StringBuffer - synchronized (sbuf) - { - sbuf.setLength(0); - sbuf.ensureCapacity(32); - sbuf.append("'"); - //format the timestamp - //we do our own formating so that we can get a format - //that works with both timestamp with time zone and - //timestamp without time zone datatypes. - //The format is '2002-01-01 23:59:59.123456-0130' - //we need to include the local time and timezone offset - //so that timestamp without time zone works correctly - int l_year = x.getYear() + 1900; - sbuf.append(l_year); - sbuf.append('-'); - int l_month = x.getMonth() + 1; - if (l_month < 10) - sbuf.append('0'); - sbuf.append(l_month); - sbuf.append('-'); - int l_day = x.getDate(); - if (l_day < 10) - sbuf.append('0'); - sbuf.append(l_day); - sbuf.append(' '); - int l_hours = x.getHours(); - if (l_hours < 10) - sbuf.append('0'); - sbuf.append(l_hours); - sbuf.append(':'); - int l_minutes = x.getMinutes(); - if (l_minutes < 10) - sbuf.append('0'); - sbuf.append(l_minutes); - sbuf.append(':'); - int l_seconds = x.getSeconds(); - if (l_seconds < 10) - sbuf.append('0'); - sbuf.append(l_seconds); - // Make decimal from nanos. - char[] l_decimal = {'0', '0', '0', '0', '0', '0', '0', '0', '0'}; - char[] l_nanos = Integer.toString(x.getNanos()).toCharArray(); - System.arraycopy(l_nanos, 0, l_decimal, l_decimal.length - l_nanos.length, l_nanos.length); - sbuf.append('.'); - if (connection.haveMinimumServerVersion("7.2")) - { - sbuf.append(l_decimal, 0, 6); - } - else - { - // Because 7.1 include bug that "hh:mm:59.999" becomes "hh:mm:60.00". - sbuf.append(l_decimal, 0, 2); - } - //add timezone offset - int l_offset = -(x.getTimezoneOffset()); - int l_houros = l_offset / 60; - if (l_houros >= 0) - { - sbuf.append('+'); - } - else - { - sbuf.append('-'); - } - if (l_houros > -10 && l_houros < 10) - sbuf.append('0'); - if (l_houros >= 0) - { - sbuf.append(l_houros); - } - else - { - sbuf.append(-l_houros); - } - int l_minos = l_offset - (l_houros * 60); - if (l_minos != 0) - { - if (l_minos > -10 && l_minos < 10) - sbuf.append('0'); - if (l_minos >= 0) - { - sbuf.append(l_minos); - } - else - { - sbuf.append(-l_minos); - } - } - sbuf.append("'"); - bind(parameterIndex, sbuf.toString(), PG_TIMESTAMPTZ); - } - - } - } - - /* - * 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. - * - * <P><B>Note:</B> 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 - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) - //As the spec/javadoc for this method indicate this is to be used for - //large String values (i.e. LONGVARCHAR) PG doesn't have a separate - //long varchar datatype, but with toast all text datatypes are capable of - //handling very large values. Thus the implementation ends up calling - //setString() since there is no current way to stream the value to the server - try - { - InputStreamReader l_inStream = new InputStreamReader(x, "ASCII"); - char[] l_chars = new char[length]; - int l_charsRead = l_inStream.read(l_chars, 0, length); - setString(parameterIndex, new String(l_chars, 0, l_charsRead), PG_TEXT); - } - catch (UnsupportedEncodingException l_uee) - { - throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_uee); - } - catch (IOException l_ioe) - { - throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_ioe); - } - } - else - { - //Version 7.1 supported only LargeObjects by treating everything - //as binary data - 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. - * - * <P><B>Note:</B> 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 - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) - //As the spec/javadoc for this method indicate this is to be used for - //large String values (i.e. LONGVARCHAR) PG doesn't have a separate - //long varchar datatype, but with toast all text datatypes are capable of - //handling very large values. Thus the implementation ends up calling - //setString() since there is no current way to stream the value to the server - try - { - InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8"); - char[] l_chars = new char[length]; - int l_charsRead = l_inStream.read(l_chars, 0, length); - setString(parameterIndex, new String(l_chars, 0, l_charsRead), PG_TEXT); - } - catch (UnsupportedEncodingException l_uee) - { - throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_uee); - } - catch (IOException l_ioe) - { - throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_ioe); - } - } - else - { - //Version 7.1 supported only LargeObjects by treating everything - //as binary data - 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. - * - * <P><B>Note:</B> 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 - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports BinaryStream for for the PG bytea type - //As the spec/javadoc for this method indicate this is to be used for - //large binary values (i.e. LONGVARBINARY) PG doesn't have a separate - //long binary datatype, but with toast the bytea datatype is capable of - //handling very large values. Thus the implementation ends up calling - //setBytes() since there is no current way to stream the value to the server - byte[] l_bytes = new byte[length]; - int l_bytesRead; - try - { - l_bytesRead = x.read(l_bytes, 0, length); - } - catch (IOException l_ioe) - { - throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_ioe); - } - if (l_bytesRead == length) - { - setBytes(parameterIndex, l_bytes); - } - // x.read will return -1 not 0 on an empty InputStream - else if (l_bytesRead == -1) - { - setBytes(parameterIndex, new byte[0]); - } - else - { - //the stream contained less data than they said - byte[] l_bytes2 = new byte[l_bytesRead]; - System.arraycopy(l_bytes, 0, l_bytes2, 0, l_bytesRead); - setBytes(parameterIndex, l_bytes2); - } - } - else - { - //Version 7.1 only supported streams for LargeObjects - //but the jdbc spec indicates that streams should be - //available for LONGVARBINARY instead - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - OutputStream los = lob.getOutputStream(); - try - { - // could be buffered, but then the OutputStream returned by LargeObject - // is buffered internally anyhow, so there would be no performance - // boost gained, if anything it would be worse! - int c = x.read(); - int p = 0; - while (c > -1 && p < length) - { - los.write(c); - c = x.read(); - p++; - } - los.close(); - } - catch (IOException se) - { - throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, se); - } - // lob is closed by the stream so don't call lob.close() - setInt(parameterIndex, oid); - } - } - - - /* - * 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 < m_binds.length ; i++) - { - m_binds[i] = null; - m_bindTypes[i] = null; - } - } - - // Helper method that extracts numeric values from an arbitary Object. - private String numericValueOf(Object x) - { - if (x instanceof Boolean) - return ((Boolean)x).booleanValue() ? "1" :"0"; - else if (x instanceof Integer || x instanceof Long || - x instanceof Double || x instanceof Short || - x instanceof Number || x instanceof Float) - return x.toString(); - else - //ensure the value is a valid numeric value to avoid - //sql injection attacks - return new BigDecimal(x.toString()).toString(); - } - - /* - * Set the value of a parameter using an object; use the java.lang - * equivalent objects for integral values. - * - * <P>The given Java object will be converted to the targetSqlType before - * being sent to the database. - * - * <P>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 - { - if (x == null) - { - setNull(parameterIndex, targetSqlType); - return ; - } - switch (targetSqlType) - { - case Types.INTEGER: - bind(parameterIndex, numericValueOf(x), PG_INTEGER); - break; - case Types.TINYINT: - case Types.SMALLINT: - bind(parameterIndex, numericValueOf(x), PG_INT2); - break; - case Types.BIGINT: - bind(parameterIndex, numericValueOf(x), PG_INT8); - break; - case Types.REAL: - case Types.FLOAT: - bind(parameterIndex, numericValueOf(x), PG_FLOAT); - break; - case Types.DOUBLE: - bind(parameterIndex, numericValueOf(x), PG_DOUBLE); - break; - case Types.DECIMAL: - case Types.NUMERIC: - bind(parameterIndex, numericValueOf(x), PG_NUMERIC); - break; - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - setString(parameterIndex, x.toString()); - break; - case Types.DATE: - if (x instanceof java.sql.Date) - setDate(parameterIndex, (java.sql.Date)x); - else - { - java.sql.Date tmpd = (x instanceof java.util.Date) ? new java.sql.Date(((java.util.Date)x).getTime()) : dateFromString(x.toString()); - setDate(parameterIndex, tmpd); - } - break; - case Types.TIME: - if (x instanceof java.sql.Time) - setTime(parameterIndex, (java.sql.Time)x); - else - { - java.sql.Time tmpt = (x instanceof java.util.Date) ? new java.sql.Time(((java.util.Date)x).getTime()) : timeFromString(x.toString()); - setTime(parameterIndex, tmpt); - } - break; - case Types.TIMESTAMP: - if (x instanceof java.sql.Timestamp) - setTimestamp(parameterIndex ,(java.sql.Timestamp)x); - else - { - java.sql.Timestamp tmpts = (x instanceof java.util.Date) ? new java.sql.Timestamp(((java.util.Date)x).getTime()) : timestampFromString(x.toString()); - setTimestamp(parameterIndex, tmpts); - } - break; - case Types.BIT: - if (x instanceof Boolean) - { - bind(parameterIndex, ((Boolean)x).booleanValue() ? "'1'" : "'0'", PG_BOOLEAN); - } - else if (x instanceof String) - { - bind(parameterIndex, Boolean.valueOf(x.toString()).booleanValue() ? "'1'" : "'0'", PG_BOOLEAN); - } - else if (x instanceof Number) - { - bind(parameterIndex, ((Number)x).intValue()!=0 ? "'1'" : "'0'", PG_BOOLEAN); - } - else - { - throw new PSQLException("postgresql.prep.type", PSQLState.INVALID_PARAMETER_TYPE); - } - break; - case Types.BINARY: - case Types.VARBINARY: - case Types.LONGVARBINARY: - setObject(parameterIndex, x); - break; - case Types.OTHER: - if (x instanceof PGobject) - setString(parameterIndex, ((PGobject)x).getValue(), ((PGobject)x).getType()); - else - throw new PSQLException("postgresql.prep.type", PSQLState.INVALID_PARAMETER_TYPE); - break; - default: - throw new PSQLException("postgresql.prep.type", PSQLState.INVALID_PARAMETER_TYPE); - } - } - - public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException - { - setObject(parameterIndex, x, targetSqlType, 0); - } - - /* - * This stores an Object into a parameter. - */ - public void setObject(int parameterIndex, Object x) throws SQLException - { - if (x == null) - { - setNull(parameterIndex, Types.OTHER); - return ; - } - 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(), PG_TEXT); - else - // Try to store as a string in database - setString(parameterIndex, x.toString(), PG_TEXT); - } - - /* - * Before executing a stored procedure call you must explicitly - * call registerOutParameter to register the java.sql.Type of each - * out parameter. - * - * <p>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. - * - * ONLY 1 RETURN PARAMETER if {?= call ..} syntax is used - * - * @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 - { - if (parameterIndex != 1) - throw new PSQLException ("postgresql.call.noinout", PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL); - if (!isFunction) - throw new PSQLException ("postgresql.call.procasfunc", PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL,originalSql); - - // functionReturnType contains the user supplied value to check - // testReturn contains a modified version to make it easier to - // check the getXXX methods.. - functionReturnType = sqlType; - testReturn = sqlType; - if (functionReturnType == Types.CHAR || - functionReturnType == Types.LONGVARCHAR) - testReturn = Types.VARCHAR; - else if (functionReturnType == Types.FLOAT) - testReturn = Types.REAL; // changes to streamline later error checking - returnTypeSet = true; - } - - /* - * You must also specify the scale for numeric/decimal types: - * - * <p>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 - { - registerOutParameter (parameterIndex, sqlType); // ignore for now.. - } - - /* - * An OUT parameter may have the value of SQL NULL; wasNull - * reports whether the last value read has this special value. - * - * <p>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 (callResult == 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 - { - checkIndex (parameterIndex, Types.VARCHAR, "String"); - return (String)callResult; - } - - - /* - * 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 - { - checkIndex (parameterIndex, Types.BIT, "Boolean"); - if (callResult == null) - return false; - return ((Boolean)callResult).booleanValue (); - } - - /* - * 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 - { - checkIndex (parameterIndex, Types.TINYINT, "Byte"); - if (callResult == null) - return 0; - return (byte)((Integer)callResult).intValue (); - } - - /* - * 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 - { - checkIndex (parameterIndex, Types.SMALLINT, "Short"); - if (callResult == null) - return 0; - return (short)((Integer)callResult).intValue (); - } - - - /* - * 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 - { - checkIndex (parameterIndex, Types.INTEGER, "Int"); - if (callResult == null) - return 0; - return ((Integer)callResult).intValue (); - } - - /* - * 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 - { - checkIndex (parameterIndex, Types.BIGINT, "Long"); - if (callResult == null) - return 0; - return ((Long)callResult).longValue (); - } - - /* - * 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 - { - checkIndex (parameterIndex, Types.REAL, "Float"); - if (callResult == null) - return 0; - return ((Float)callResult).floatValue (); - } - - /* - * 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 - { - checkIndex (parameterIndex, Types.DOUBLE, "Double"); - if (callResult == null) - return 0; - return ((Double)callResult).doubleValue (); - } - - /* - * 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. - * @deprecated in Java2.0 - */ - public BigDecimal getBigDecimal(int parameterIndex, int scale) - throws SQLException - { - checkIndex (parameterIndex, Types.NUMERIC, "BigDecimal"); - return ((BigDecimal)callResult); - } - - /* - * 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 - { - checkIndex (parameterIndex, Types.VARBINARY, Types.BINARY, "Bytes"); - return ((byte [])callResult); - } - - - /* - * 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 - { - checkIndex (parameterIndex, Types.DATE, "Date"); - return (java.sql.Date)callResult; - } - - /* - * 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 - { - checkIndex (parameterIndex, Types.TIME, "Time"); - return (java.sql.Time)callResult; - } - - /* - * 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 - { - checkIndex (parameterIndex, Types.TIMESTAMP, "Timestamp"); - return (java.sql.Timestamp)callResult; - } - - // 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. - * - * <p>This method returns a Java object whose type coresponds to the - * SQL type that was registered for this parameter using - * registerOutParameter. - * - * <P>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. - * - * <p>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 - { - checkIndex (parameterIndex); - return callResult; - } - - //This method is implemeted in jdbc2 - public int getResultSetConcurrency() throws SQLException - { - return 0; - } - - /* - * Returns the SQL statement with the current template values - * substituted. - */ - public String toString() - { - if (m_sqlFragments == null) - return super.toString(); - - synchronized (sbuf) - { - sbuf.setLength(0); - int i; - - for (i = 0 ; i < m_binds.length ; ++i) - { - sbuf.append (m_sqlFragments[i]); - if (m_binds[i] == null) - sbuf.append( '?' ); - else - sbuf.append (m_binds[i]); - } - sbuf.append(m_sqlFragments[m_binds.length]); - return sbuf.toString(); - } - } - - /* - * Note if s is a String it should be escaped by the caller to avoid SQL - * injection attacks. It is not done here for efficency reasons as - * most calls to this method do not require escaping as the source - * of the string is known safe (i.e. Integer.toString()) - */ - private void bind(int paramIndex, Object s, String type) throws SQLException - { - if (paramIndex < 1 || paramIndex > m_binds.length) - throw new PSQLException("postgresql.prep.range", PSQLState.INVALID_PARAMETER_VALUE); - if (paramIndex == 1 && isFunction) // need to registerOut instead - throw new PSQLException ("postgresql.call.funcover"); - m_binds[paramIndex - 1] = s; - m_bindTypes[paramIndex - 1] = type; - } - - /** - * this method will turn a string of the form - * {? = call <some_function> (?, [?,..]) } - * into the PostgreSQL format which is - * select <some_function> (?, [?, ...]) as result - * or select * from <some_function> (?, [?, ...]) as result (7.3) - * - */ - private String modifyJdbcCall(String p_sql) throws SQLException - { - //Check that this is actually a call which should start with a { - //if not do nothing and treat this as a standard prepared sql - if (!p_sql.trim().startsWith("{")) { - return p_sql; - } - - // syntax checking is not complete only a few basics :( - originalSql = p_sql; // save for error msgs.. - String l_sql = p_sql; - int index = l_sql.indexOf ("="); // is implied func or proc? - boolean isValid = true; - if (index > -1) - { - isFunction = true; - isValid = l_sql.indexOf ("?") < index; // ? before = - } - l_sql = l_sql.trim (); - if (l_sql.startsWith ("{") && l_sql.endsWith ("}")) - { - l_sql = l_sql.substring (1, l_sql.length() - 1); - } - else - isValid = false; - index = l_sql.indexOf ("call"); - if (index == -1 || !isValid) - throw new PSQLException ("postgresql.call.malformed",PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL, - new Object[]{l_sql, JDBC_SYNTAX}); - l_sql = l_sql.replace ('{', ' '); // replace these characters - l_sql = l_sql.replace ('}', ' '); - l_sql = l_sql.replace (';', ' '); - - // this removes the 'call' string and also puts a hidden '?' - // at the front of the line for functions, this will - // allow the registerOutParameter to work correctly - // because in the source sql there was one more ? for the return - // value that is not needed by the postgres syntax. But to make - // sure that the parameter numbers are the same as in the original - // sql we add a dummy parameter in this case - l_sql = (isFunction ? "?" : "") + l_sql.substring (index + 4); - if (connection.haveMinimumServerVersion("7.3")) { - l_sql = "select * from " + l_sql + " as " + RESULT_ALIAS + ";"; - } else { - l_sql = "select " + l_sql + " as " + RESULT_ALIAS + ";"; - } - return l_sql; - } - - /** helperfunction for the getXXX calls to check isFunction and index == 1 - * Compare BOTH type fields against the return type. - */ - protected void checkIndex (int parameterIndex, int type1, int type2, String getName) - throws SQLException - { - checkIndex (parameterIndex); - if (type1 != this.testReturn && type2 != this.testReturn) - throw new PSQLException("postgresql.call.wrongget", PSQLState.MOST_SPECIFIC_TYPE_DOES_NOT_MATCH, - new Object[]{"java.sql.Types=" + testReturn, - getName, - "java.sql.Types=" + type1}); - } - - /** helperfunction for the getXXX calls to check isFunction and index == 1 - */ - protected void checkIndex (int parameterIndex, int type, String getName) - throws SQLException - { - checkIndex (parameterIndex); - if (type != this.testReturn) - throw new PSQLException("postgresql.call.wrongget", PSQLState.MOST_SPECIFIC_TYPE_DOES_NOT_MATCH, - new Object[]{"java.sql.Types=" + testReturn, - getName, - "java.sql.Types=" + type}); - } - - /** helperfunction for the getXXX calls to check isFunction and index == 1 - * @param parameterIndex index of getXXX (index) - * check to make sure is a function and index == 1 - */ - private void checkIndex (int parameterIndex) throws SQLException - { - if (!isFunction) - throw new PSQLException("postgresql.call.noreturntype", PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL); - if (parameterIndex != 1) - throw new PSQLException("postgresql.call.noinout", PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL); - } - - - - public void setUseServerPrepare(boolean flag) throws SQLException { - //Server side prepared statements were introduced in 7.3 - if (connection.haveMinimumServerVersion("7.3")) { - if (m_useServerPrepare != flag) - deallocateQuery(); - m_useServerPrepare = flag; - } else { - //This is a pre 7.3 server so no op this method - //which means we will never turn on the flag to use server - //prepared statements and thus regular processing will continue - } - } - - public boolean isUseServerPrepare() - { - return m_useServerPrepare; - } - - private java.sql.Date dateFromString (String s) throws SQLException - { - int timezone = 0; - long millis = 0; - long localoffset = 0; - int timezoneLocation = (s.indexOf('+') == -1) ? s.lastIndexOf("-") : s.indexOf('+'); - //if the last index of '-' or '+' is past 8. we are guaranteed that it is a timezone marker - //shortest = yyyy-m-d - //longest = yyyy-mm-dd - try - { - timezone = (timezoneLocation>7) ? timezoneLocation : s.length(); - millis = java.sql.Date.valueOf(s.substring(0,timezone)).getTime(); - } - catch (Exception e) - { - throw new PSQLException("postgresql.format.baddate", PSQLState.BAD_DATETIME_FORMAT, s , "yyyy-MM-dd[-tz]"); - } - timezone = 0; - if (timezoneLocation>7 && timezoneLocation+3 == s.length()) - { - timezone = Integer.parseInt(s.substring(timezoneLocation+1,s.length())); - localoffset = java.util.Calendar.getInstance().getTimeZone().getRawOffset(); - if (java.util.Calendar.getInstance().getTimeZone().inDaylightTime(new java.sql.Date(millis))) - localoffset += 60*60*1000; - if (s.charAt(timezoneLocation)=='+') - timezone*=-1; - } - millis = millis + timezone*60*60*1000 + localoffset; - return new java.sql.Date(millis); - } - - private java.sql.Time timeFromString (String s) throws SQLException - { - int timezone = 0; - long millis = 0; - long localoffset = 0; - int timezoneLocation = (s.indexOf('+') == -1) ? s.lastIndexOf("-") : s.indexOf('+'); - //if the index of the last '-' or '+' is greater than 0 that means this time has a timezone. - //everything earlier than that position, we treat as the time and parse it as such. - try - { - timezone = (timezoneLocation==-1) ? s.length() : timezoneLocation; - millis = java.sql.Time.valueOf(s.substring(0,timezone)).getTime(); - } - catch (Exception e) - { - throw new PSQLException("postgresql.format.badtime", PSQLState.BAD_DATETIME_FORMAT, s, "HH:mm:ss[-tz]"); - } - timezone = 0; - if (timezoneLocation != -1 && timezoneLocation+3 == s.length()) - { - timezone = Integer.parseInt(s.substring(timezoneLocation+1,s.length())); - localoffset = java.util.Calendar.getInstance().getTimeZone().getRawOffset(); - if (java.util.Calendar.getInstance().getTimeZone().inDaylightTime(new java.sql.Date(millis))) - localoffset += 60*60*1000; - if (s.charAt(timezoneLocation)=='+') - timezone*=-1; - } - millis = millis + timezone*60*60*1000 + localoffset; - return new java.sql.Time(millis); - } - - private java.sql.Timestamp timestampFromString (String s) throws SQLException - { - int timezone = 0; - long millis = 0; - long localoffset = 0; - int nanosVal = 0; - int timezoneLocation = (s.indexOf('+') == -1) ? s.lastIndexOf("-") : s.indexOf('+'); - int nanospos = s.indexOf("."); - //if there is a '.', that means there are nanos info, and we take the timestamp up to that point - //if not, then we check to see if the last +/- (to indicate a timezone) is greater than 8 - //8 is because the shortest date, will have last '-' at position 7. e.g yyyy-x-x - try - { - if (nanospos != -1) - timezone = nanospos; - else if (timezoneLocation > 8) - timezone = timezoneLocation; - else - timezone = s.length(); - millis = java.sql.Timestamp.valueOf(s.substring(0,timezone)).getTime(); - } - catch (Exception e) - { - throw new PSQLException("postgresql.format.badtimestamp", PSQLState.BAD_DATETIME_FORMAT, s, "yyyy-MM-dd HH:mm:ss[.xxxxxx][-tz]"); - } - timezone = 0; - if (nanospos != -1) - { - int tmploc = (timezoneLocation > 8) ? timezoneLocation : s.length(); - nanosVal = Integer.parseInt(s.substring(nanospos+1,tmploc)); - int diff = 8-((tmploc-1)-(nanospos+1)); - for (int i=0;i<diff;i++) - nanosVal*=10; - } - if (timezoneLocation>8 && timezoneLocation+3 == s.length()) - { - timezone = Integer.parseInt(s.substring(timezoneLocation+1,s.length())); - localoffset = java.util.Calendar.getInstance().getTimeZone().getRawOffset(); - if (java.util.Calendar.getInstance().getTimeZone().inDaylightTime(new java.sql.Date(millis))) - localoffset += 60*60*1000; - if (s.charAt(timezoneLocation)=='+') - timezone*=-1; - } - millis = millis + timezone*60*60*1000 + localoffset; - java.sql.Timestamp tmpts = new java.sql.Timestamp(millis); - tmpts.setNanos(nanosVal); - return tmpts; - } - - - private static final String PG_TEXT = "text"; - private static final String PG_INTEGER = "integer"; - private static final String PG_INT2 = "int2"; - private static final String PG_INT8 = "int8"; - private static final String PG_NUMERIC = "numeric"; - private static final String PG_FLOAT = "float"; - private static final String PG_DOUBLE = "double precision"; - private static final String PG_BOOLEAN = "boolean"; - private static final String PG_DATE = "date"; - private static final String PG_TIME = "time"; - private static final String PG_TIMESTAMPTZ = "timestamptz"; - private static final String PG_BYTEA = "bytea"; - - -} diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java deleted file mode 100644 index 697a1869ab..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.postgresql.jdbc1; - - -import java.sql.*; -import java.util.Vector; -import org.postgresql.PGRefCursorResultSet; -import org.postgresql.core.BaseResultSet; -import org.postgresql.core.Field; - -public class Jdbc1CallableStatement extends AbstractJdbc1Statement implements java.sql.CallableStatement -{ - - public Jdbc1CallableStatement(Jdbc1Connection connection, String sql) throws SQLException - { - super(connection, sql); - } - - public BaseResultSet createResultSet (Field[] fields, java.util.Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException - { - return new Jdbc1ResultSet(this, fields, tuples, status, updateCount, insertOID, binaryCursor); - } - - public PGRefCursorResultSet createRefCursorResultSet (String cursorName) throws SQLException - { - return new Jdbc1RefCursorResultSet(this, cursorName); - } -} - diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java deleted file mode 100644 index 15d11f5a28..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.postgresql.jdbc1; - - -import java.util.Vector; -import java.sql.*; -import org.postgresql.core.Field; -import org.postgresql.util.PSQLException; - -/* $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java,v 1.8 2003/11/29 19:52:10 pgsql Exp $ - * This class implements the java.sql.Connection interface for JDBC1. - * However most of the implementation is really done in - * org.postgresql.jdbc1.AbstractJdbc1Connection - */ -public class Jdbc1Connection extends org.postgresql.jdbc1.AbstractJdbc1Connection implements java.sql.Connection -{ - - public java.sql.Statement createStatement() throws SQLException - { - return new org.postgresql.jdbc1.Jdbc1Statement(this); - } - - public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException - { - return new org.postgresql.jdbc1.Jdbc1PreparedStatement(this, sql); - } - - public java.sql.CallableStatement prepareCall(String sql) throws SQLException - { - return new org.postgresql.jdbc1.Jdbc1CallableStatement(this, sql); - } - - public java.sql.DatabaseMetaData getMetaData() throws SQLException - { - if (metadata == null) - metadata = new org.postgresql.jdbc1.Jdbc1DatabaseMetaData(this); - return metadata; - } - -} - - diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1DatabaseMetaData.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1DatabaseMetaData.java deleted file mode 100644 index fde36f5511..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1DatabaseMetaData.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.postgresql.jdbc1; - - -import java.sql.*; -import java.util.*; -import org.postgresql.core.Field; -import org.postgresql.util.PSQLException; - -public class Jdbc1DatabaseMetaData extends AbstractJdbc1DatabaseMetaData implements java.sql.DatabaseMetaData -{ - public Jdbc1DatabaseMetaData(Jdbc1Connection conn) - { - super(conn); - } - -} diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1PreparedStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1PreparedStatement.java deleted file mode 100644 index a80928ebdd..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1PreparedStatement.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.postgresql.jdbc1; - - -import java.sql.*; -import org.postgresql.PGRefCursorResultSet; -import org.postgresql.core.BaseResultSet; -import org.postgresql.core.Field; - -public class Jdbc1PreparedStatement extends AbstractJdbc1Statement implements PreparedStatement -{ - - public Jdbc1PreparedStatement(Jdbc1Connection connection, String sql) throws SQLException - { - super(connection, sql); - } - - public BaseResultSet createResultSet (Field[] fields, java.util.Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException - { - return new Jdbc1ResultSet(this, fields, tuples, status, updateCount, insertOID, binaryCursor); - } - - public PGRefCursorResultSet createRefCursorResultSet (String cursorName) throws SQLException - { - return new Jdbc1RefCursorResultSet(this, cursorName); - } -} diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1RefCursorResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1RefCursorResultSet.java deleted file mode 100644 index 7b83bf6742..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1RefCursorResultSet.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.postgresql.jdbc1; - -import java.sql.SQLException; -import org.postgresql.core.QueryExecutor; -import org.postgresql.core.BaseStatement; -import org.postgresql.PGRefCursorResultSet; - -/** A real result set based on a ref cursor. - * - * @author Nic Ferrier <nferrier@tapsellferrier.co.uk> - */ -public class Jdbc1RefCursorResultSet extends Jdbc1ResultSet - implements PGRefCursorResultSet -{ - - // The name of the cursor being used. - String refCursorHandle; - - // Indicates when the result set has activaly bound to the cursor. - boolean isInitialized = false; - - - Jdbc1RefCursorResultSet(BaseStatement statement, String refCursorName) - { - super(statement, null, null, null, -1, 0L, false); - this.refCursorHandle = refCursorName; - } - - public String getRefCursor () - { - return refCursorHandle; - } - - public boolean next () throws SQLException - { - if (isInitialized) - return super.next(); - // Initialize this res set with the rows from the cursor. - String[] toExec = { "FETCH ALL IN \"" + refCursorHandle + "\";" }; - QueryExecutor.execute(toExec, new String[0], this); - isInitialized = true; - return super.next(); - } -} diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java deleted file mode 100644 index 4e3dc05dad..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.postgresql.jdbc1; - - -import java.sql.*; -import java.util.Vector; -import org.postgresql.core.BaseStatement; -import org.postgresql.core.Field; - -/* $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java,v 1.7 2003/11/29 19:52:10 pgsql Exp $ - * This class implements the java.sql.ResultSet interface for JDBC1. - * However most of the implementation is really done in - * org.postgresql.jdbc1.AbstractJdbc1ResultSet - */ -public class Jdbc1ResultSet extends org.postgresql.jdbc1.AbstractJdbc1ResultSet implements java.sql.ResultSet -{ - - public Jdbc1ResultSet(BaseStatement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) - { - super(statement, fields, tuples, status, updateCount, insertOID, binaryCursor); - } - - public java.sql.ResultSetMetaData getMetaData() throws SQLException - { - return new Jdbc1ResultSetMetaData(rows, fields); - } - -} - diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSetMetaData.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSetMetaData.java deleted file mode 100644 index 39748d6710..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSetMetaData.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.postgresql.jdbc1; - -import java.util.Vector; -import org.postgresql.core.Field; - -public class Jdbc1ResultSetMetaData extends AbstractJdbc1ResultSetMetaData implements java.sql.ResultSetMetaData -{ - public Jdbc1ResultSetMetaData(Vector rows, Field[] fields) - { - super(rows, fields); - } - -} - diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Statement.java deleted file mode 100644 index 0cfc4a6142..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Statement.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.postgresql.jdbc1; - - -import java.sql.*; -import java.util.Vector; -import org.postgresql.PGRefCursorResultSet; -import org.postgresql.core.BaseResultSet; -import org.postgresql.core.Field; - -/* $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Statement.java,v 1.7 2003/11/29 19:52:10 pgsql Exp $ - * This class implements the java.sql.Statement interface for JDBC1. - * However most of the implementation is really done in - * org.postgresql.jdbc1.AbstractJdbc1Statement - */ -public class Jdbc1Statement extends org.postgresql.jdbc1.AbstractJdbc1Statement implements java.sql.Statement -{ - - public Jdbc1Statement (Jdbc1Connection c) - { - super(c); - } - - public BaseResultSet createResultSet (Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException - { - return new Jdbc1ResultSet(this, fields, tuples, status, updateCount, insertOID, binaryCursor); - } - - public PGRefCursorResultSet createRefCursorResultSet (String cursorName) throws SQLException - { - return new Jdbc1RefCursorResultSet(this, cursorName); - } -} |
