~ubuntu-branches/ubuntu/oneiric/libpgjava/oneiric

« back to all changes in this revision

Viewing changes to src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java

  • Committer: Bazaar Package Importer
  • Author(s): Arnaud Vandyck
  • Date: 2005-04-21 14:25:11 UTC
  • mfrom: (1.2.1 upstream) (2.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20050421142511-wibh5vc31fkrorx7
Tags: 7.4.7-3
Built with sources...

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * AbstractJdbc1Connection.java
 
4
 *     This class defines methods of the jdbc1 specification.  This class is
 
5
 *     extended by org.postgresql.jdbc2.AbstractJdbc2Connection which adds 
 
6
 *     the jdbc2 methods.  The real Connection class (for jdbc1) is 
 
7
 *     org.postgresql.jdbc1.Jdbc1Connection
 
8
 *
 
9
 * Copyright (c) 2003, PostgreSQL Global Development Group
 
10
 *
 
11
 * IDENTIFICATION
 
12
 *        $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Connection.java,v 1.27.2.4 2004/08/11 06:56:00 jurka Exp $
 
13
 *
 
14
 *-------------------------------------------------------------------------
 
15
 */
 
16
package org.postgresql.jdbc1;
 
17
 
 
18
 
 
19
import java.io.IOException;
 
20
import java.net.ConnectException;
 
21
import java.sql.*;
 
22
import java.util.*;
 
23
import org.postgresql.Driver;
 
24
import org.postgresql.PGNotification;
 
25
import org.postgresql.core.BaseConnection;
 
26
import org.postgresql.core.BaseResultSet;
 
27
import org.postgresql.core.BaseStatement;
 
28
import org.postgresql.core.Encoding;
 
29
import org.postgresql.core.PGStream;
 
30
import org.postgresql.core.QueryExecutor;
 
31
import org.postgresql.core.StartupPacket;
 
32
import org.postgresql.fastpath.Fastpath;
 
33
import org.postgresql.largeobject.LargeObjectManager;
 
34
import org.postgresql.util.MD5Digest;
 
35
import org.postgresql.util.PGobject;
 
36
import org.postgresql.util.PSQLException;
 
37
import org.postgresql.util.PSQLState;
 
38
import org.postgresql.util.UnixCrypt;
 
39
 
 
40
public abstract class AbstractJdbc1Connection implements BaseConnection
 
41
{
 
42
        // This is the network stream associated with this connection
 
43
        private PGStream pgStream;
 
44
 
 
45
        public PGStream getPGStream() {
 
46
                return pgStream;
 
47
        }
 
48
  
 
49
        protected String PG_HOST;
 
50
        protected int PG_PORT;
 
51
        protected String PG_USER;
 
52
        protected String PG_DATABASE;
 
53
        protected boolean PG_STATUS;
 
54
        protected String compatible;
 
55
        protected boolean useSSL;
 
56
 
 
57
        // The PID an cancellation key we get from the backend process
 
58
        protected int pid;
 
59
        protected int ckey;
 
60
 
 
61
        private Vector m_notifications;
 
62
 
 
63
        /*
 
64
         The encoding to use for this connection.
 
65
         */
 
66
        private Encoding encoding = Encoding.defaultEncoding();
 
67
 
 
68
        private String dbVersionNumber;
 
69
 
 
70
        public boolean CONNECTION_OK = true;
 
71
        public boolean CONNECTION_BAD = false;
 
72
 
 
73
        public boolean autoCommit = true;
 
74
        public boolean readOnly = false;
 
75
 
 
76
        public Driver this_driver;
 
77
        private String this_url;
 
78
        private String cursor = null;   // The positioned update cursor name
 
79
 
 
80
        private int PGProtocolVersionMajor = 2;
 
81
        private int PGProtocolVersionMinor = 0;
 
82
        public int getPGProtocolVersionMajor() { return PGProtocolVersionMajor; }
 
83
        public int getPGProtocolVersionMinor() { return PGProtocolVersionMinor; }
 
84
 
 
85
        private static final int AUTH_REQ_OK = 0;
 
86
        private static final int AUTH_REQ_KRB4 = 1;
 
87
        private static final int AUTH_REQ_KRB5 = 2;
 
88
        private static final int AUTH_REQ_PASSWORD = 3;
 
89
        private static final int AUTH_REQ_CRYPT = 4;
 
90
        private static final int AUTH_REQ_MD5 = 5;
 
91
        private static final int AUTH_REQ_SCM = 6;
 
92
 
 
93
 
 
94
        // These are used to cache oids, PGTypes and SQLTypes
 
95
        private static Hashtable sqlTypeCache = new Hashtable();  // oid -> SQLType
 
96
        private static Hashtable pgTypeCache = new Hashtable();  // oid -> PGType
 
97
        private static Hashtable typeOidCache = new Hashtable();  //PGType -> oid
 
98
 
 
99
        // Now handle notices as warnings, so things like "show" now work
 
100
        public SQLWarning firstWarning = null;
 
101
 
 
102
        /*
 
103
         * Cache of the current isolation level
 
104
         */
 
105
        private int isolationLevel = Connection.TRANSACTION_READ_COMMITTED;
 
106
 
 
107
 
 
108
        public abstract Statement createStatement() throws SQLException;
 
109
        public abstract DatabaseMetaData getMetaData() throws SQLException;
 
110
 
 
111
        /*
 
112
         * This method actually opens the connection. It is called by Driver.
 
113
         *
 
114
         * @param host the hostname of the database back end
 
115
         * @param port the port number of the postmaster process
 
116
         * @param info a Properties[] thing of the user and password
 
117
         * @param database the database to connect to
 
118
         * @param url the URL of the connection
 
119
         * @param d the Driver instantation of the connection
 
120
         * @exception SQLException if a database access error occurs
 
121
         */
 
122
        public void openConnection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException
 
123
          {
 
124
                firstWarning = null;
 
125
 
 
126
                // Throw an exception if the user or password properties are missing
 
127
                // This occasionally occurs when the client uses the properties version
 
128
                // of getConnection(), and is a common question on the email lists
 
129
                if (info.getProperty("user") == null)
 
130
                        throw new PSQLException("postgresql.con.user", PSQLState.CONNECTION_REJECTED);
 
131
 
 
132
                this_driver = (Driver)d;
 
133
                this_url = url;
 
134
 
 
135
                PG_DATABASE = database;
 
136
                PG_USER = info.getProperty("user");
 
137
 
 
138
                String password = info.getProperty("password", "");
 
139
                PG_PORT = port;
 
140
 
 
141
                PG_HOST = host;
 
142
                PG_STATUS = CONNECTION_BAD;
 
143
 
 
144
                if (info.getProperty("ssl") != null && Driver.sslEnabled())
 
145
                {
 
146
                        useSSL = true;
 
147
                }
 
148
                else
 
149
                {
 
150
                        useSSL = false;
 
151
                }
 
152
 
 
153
                if (info.getProperty("compatible") == null)
 
154
                {
 
155
                        compatible = d.getMajorVersion() + "." + d.getMinorVersion();
 
156
                }
 
157
                else
 
158
                {
 
159
                        compatible = info.getProperty("compatible");
 
160
                }
 
161
 
 
162
                //Read loglevel arg and set the loglevel based on this value
 
163
                //in addition to setting the log level enable output to
 
164
                //standard out if no other printwriter is set
 
165
                String l_logLevelProp = info.getProperty("loglevel", "0");
 
166
                int l_logLevel = 0;
 
167
                try
 
168
                {
 
169
                        l_logLevel = Integer.parseInt(l_logLevelProp);
 
170
                        if (l_logLevel > Driver.DEBUG || l_logLevel < Driver.INFO)
 
171
                        {
 
172
                                l_logLevel = 0;
 
173
                        }
 
174
                }
 
175
                catch (Exception l_e)
 
176
                {
 
177
                        //invalid value for loglevel ignore
 
178
                }
 
179
                if (l_logLevel > 0)
 
180
                {
 
181
                        Driver.setLogLevel(l_logLevel);
 
182
                        enableDriverManagerLogging();
 
183
                }
 
184
 
 
185
                //Print out the driver version number
 
186
                if (Driver.logInfo)
 
187
                        Driver.info(Driver.getVersion());
 
188
                if (Driver.logDebug) {
 
189
                        Driver.debug("    ssl = " + useSSL);
 
190
                        Driver.debug("    compatible = " + compatible);
 
191
                        Driver.debug("    loglevel = " + l_logLevel);
 
192
                }
 
193
 
 
194
                // Now make the initial connection
 
195
                try
 
196
                {
 
197
                        pgStream = new PGStream(host, port);
 
198
                }
 
199
                catch (ConnectException cex)
 
200
                {
 
201
                        // Added by Peter Mount <peter@retep.org.uk>
 
202
                        // ConnectException is thrown when the connection cannot be made.
 
203
                        // we trap this an return a more meaningful message for the end user
 
204
                        throw new PSQLException ("postgresql.con.refused", PSQLState.CONNECTION_REJECTED);
 
205
                }
 
206
                catch (IOException e)
 
207
                {
 
208
                        throw new PSQLException ("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e);
 
209
                }
 
210
 
 
211
                try {
 
212
                        //Now do the protocol work
 
213
                        if (haveMinimumCompatibleVersion("7.4")) {
 
214
                                openConnectionV3(host,port,info,database,url,d,password);
 
215
                        } else {
 
216
                                openConnectionV2(host,port,info,database,url,d,password);
 
217
                        }
 
218
                } catch (SQLException sqle) {
 
219
                        // if we fail to completely establish a connection,
 
220
                        // close down the socket to not leak resources.
 
221
                        try {
 
222
                                pgStream.close();
 
223
                        } catch (IOException ioe) { }
 
224
 
 
225
                        throw sqle;
 
226
                }
 
227
          }
 
228
 
 
229
        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
 
230
          {
 
231
                PGProtocolVersionMajor = 3;
 
232
                if (Driver.logDebug)
 
233
                        Driver.debug("Using Protocol Version3");
 
234
 
 
235
                // Now we need to construct and send an ssl startup packet
 
236
                try
 
237
                {
 
238
                        if (useSSL) {
 
239
                                if (Driver.logDebug)
 
240
                                        Driver.debug("Asking server if it supports ssl");
 
241
                                pgStream.SendInteger(8,4);
 
242
                                pgStream.SendInteger(80877103,4);
 
243
 
 
244
                                // now flush the ssl packets to the backend
 
245
                                pgStream.flush();
 
246
 
 
247
                                // Now get the response from the backend, either an error message
 
248
                                // or an authentication request
 
249
                                int beresp = pgStream.ReceiveChar();
 
250
                                if (Driver.logDebug)
 
251
                                        Driver.debug("Server response was (S=Yes,N=No): "+(char)beresp);
 
252
                                switch (beresp)
 
253
                                        {
 
254
                                        case 'E':
 
255
                                                // An error occured, so pass the error message to the
 
256
                                                // user.
 
257
                                                //
 
258
                                                // The most common one to be thrown here is:
 
259
                                                // "User authentication failed"
 
260
                                                //
 
261
                                                throw new PSQLException("postgresql.con.misc", PSQLState.CONNECTION_REJECTED, pgStream.ReceiveString(encoding));
 
262
                                                
 
263
                                        case 'N':
 
264
                                                // Server does not support ssl
 
265
                                                throw new PSQLException("postgresql.con.sslnotsupported", PSQLState.CONNECTION_FAILURE);
 
266
                                                
 
267
                                        case 'S':
 
268
                                                // Server supports ssl
 
269
                                                if (Driver.logDebug)
 
270
                                                        Driver.debug("server does support ssl");
 
271
                                                Driver.makeSSL(pgStream);
 
272
                                                break;
 
273
 
 
274
                                        default:
 
275
                                                throw new PSQLException("postgresql.con.sslfail", PSQLState.CONNECTION_FAILURE);
 
276
                                        }
 
277
                        }
 
278
                }
 
279
                catch (IOException e)
 
280
                {
 
281
                        throw new PSQLException("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e);
 
282
                }
 
283
 
 
284
 
 
285
                // Now we need to construct and send a startup packet
 
286
                try
 
287
                {
 
288
                        new StartupPacket(PGProtocolVersionMajor,
 
289
                                                          PGProtocolVersionMinor,
 
290
                                                          PG_USER,
 
291
                                                          p_database).writeTo(pgStream);
 
292
 
 
293
                        // now flush the startup packets to the backend
 
294
                        pgStream.flush();
 
295
 
 
296
                        // Now get the response from the backend, either an error message
 
297
                        // or an authentication request
 
298
                        int areq = -1; // must have a value here
 
299
                        do
 
300
                        {
 
301
                                int beresp = pgStream.ReceiveChar();
 
302
                                String salt = null;
 
303
                                byte [] md5Salt = new byte[4];
 
304
                                switch (beresp)
 
305
                                {
 
306
                                        case 'E':
 
307
                                                // An error occured, so pass the error message to the
 
308
                                                // user.
 
309
                                                //
 
310
                                                // The most common one to be thrown here is:
 
311
                                                // "User authentication failed"
 
312
                                                //
 
313
                                                int l_elen = pgStream.ReceiveIntegerR(4);
 
314
                                                if (l_elen > 30000) {
 
315
                                                        //if the error length is > than 30000 we assume this is really a v2 protocol 
 
316
                                                        //server so try again with a v2 connection
 
317
                                                        //need to create a new connection and try again
 
318
                                                        pgStream.close();
 
319
                                                        try
 
320
                                                        {
 
321
                                                                pgStream = new PGStream(p_host, p_port);
 
322
                                                        }
 
323
                                                        catch (ConnectException cex)
 
324
                                                        {
 
325
                                                                // Added by Peter Mount <peter@retep.org.uk>
 
326
                                                                // ConnectException is thrown when the connection cannot be made.
 
327
                                                                // we trap this an return a more meaningful message for the end user
 
328
                                                                throw new PSQLException ("postgresql.con.refused", PSQLState.CONNECTION_REJECTED);
 
329
                                                        }
 
330
                                                        catch (IOException e)
 
331
                                                        {
 
332
                                                                throw new PSQLException ("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e);
 
333
                                                        }
 
334
                                                        openConnectionV2(p_host, p_port, p_info, p_database, p_url, p_d, p_password);
 
335
                                                        return;
 
336
                                                }
 
337
                                                throw new PSQLException("postgresql.con.misc", PSQLState.CONNECTION_REJECTED, PSQLException.parseServerError(encoding.decode(pgStream.Receive(l_elen-4))));
 
338
 
 
339
                                        case 'R':
 
340
                                                // Get the message length
 
341
                                                int l_msgLen = pgStream.ReceiveIntegerR(4);
 
342
                                                // Get the type of request
 
343
                                                areq = pgStream.ReceiveIntegerR(4);
 
344
                                                // Get the crypt password salt if there is one
 
345
                                                if (areq == AUTH_REQ_CRYPT)
 
346
                                                {
 
347
                                                        byte[] rst = new byte[2];
 
348
                                                        rst[0] = (byte)pgStream.ReceiveChar();
 
349
                                                        rst[1] = (byte)pgStream.ReceiveChar();
 
350
                                                        salt = new String(rst, 0, 2);
 
351
                                                        if (Driver.logDebug)
 
352
                                                                Driver.debug("Crypt salt=" + salt);
 
353
                                                }
 
354
 
 
355
                                                // Or get the md5 password salt if there is one
 
356
                                                if (areq == AUTH_REQ_MD5)
 
357
                                                {
 
358
 
 
359
                                                        md5Salt[0] = (byte)pgStream.ReceiveChar();
 
360
                                                        md5Salt[1] = (byte)pgStream.ReceiveChar();
 
361
                                                        md5Salt[2] = (byte)pgStream.ReceiveChar();
 
362
                                                        md5Salt[3] = (byte)pgStream.ReceiveChar();
 
363
                                                        if (Driver.logDebug) {
 
364
                                                                String md5SaltString = "";
 
365
                                                                for (int i=0; i<md5Salt.length; i++) {
 
366
                                                                        md5SaltString += " " + md5Salt[i];
 
367
                                                                }
 
368
                                                                Driver.debug("MD5 salt=" + md5SaltString);
 
369
                                                        }
 
370
                                                }
 
371
 
 
372
                                                // now send the auth packet
 
373
                                                switch (areq)
 
374
                                                {
 
375
                                                        case AUTH_REQ_OK:
 
376
                                                                break;
 
377
 
 
378
                                                        case AUTH_REQ_KRB4:
 
379
                                                                if (Driver.logDebug)
 
380
                                                                        Driver.debug("postgresql: KRB4");
 
381
                                                                throw new PSQLException("postgresql.con.kerb4", PSQLState.CONNECTION_REJECTED);
 
382
 
 
383
                                                        case AUTH_REQ_KRB5:
 
384
                                                                if (Driver.logDebug)
 
385
                                                                        Driver.debug("postgresql: KRB5");
 
386
                                                                throw new PSQLException("postgresql.con.kerb5", PSQLState.CONNECTION_REJECTED);
 
387
 
 
388
                                                        case AUTH_REQ_SCM:
 
389
                                                                if (Driver.logDebug)
 
390
                                                                        Driver.debug("postgresql: SCM");
 
391
                                                                throw new PSQLException("postgresql.con.scm", PSQLState.CONNECTION_REJECTED);
 
392
 
 
393
 
 
394
                                                        case AUTH_REQ_PASSWORD:
 
395
                                                                if (Driver.logDebug)
 
396
                                                                        Driver.debug("postgresql: PASSWORD");
 
397
                                                                pgStream.SendChar('p');
 
398
                                                                pgStream.SendInteger(5 + p_password.length(), 4);
 
399
                                                                pgStream.Send(p_password.getBytes());
 
400
                                                                pgStream.SendChar(0);
 
401
                                                                pgStream.flush();
 
402
                                                                break;
 
403
 
 
404
                                                        case AUTH_REQ_CRYPT:
 
405
                                                                if (Driver.logDebug)
 
406
                                                                        Driver.debug("postgresql: CRYPT");
 
407
                                                                String crypted = UnixCrypt.crypt(salt, p_password);
 
408
                                                                pgStream.SendChar('p');
 
409
                                                                pgStream.SendInteger(5 + crypted.length(), 4);
 
410
                                                                pgStream.Send(crypted.getBytes());
 
411
                                                                pgStream.SendChar(0);
 
412
                                                                pgStream.flush();
 
413
                                                                break;
 
414
 
 
415
                                                        case AUTH_REQ_MD5:
 
416
                                                                if (Driver.logDebug)
 
417
                                                                        Driver.debug("postgresql: MD5");
 
418
                                                                byte[] digest = MD5Digest.encode(PG_USER, p_password, md5Salt);
 
419
                                                                pgStream.SendChar('p');
 
420
                                                                pgStream.SendInteger(5 + digest.length, 4);
 
421
                                                                pgStream.Send(digest);
 
422
                                                                pgStream.SendChar(0);
 
423
                                                                pgStream.flush();
 
424
                                                                break;
 
425
 
 
426
                                                        default:
 
427
                                                                throw new PSQLException("postgresql.con.auth", PSQLState.CONNECTION_REJECTED, new Integer(areq));
 
428
                                                }
 
429
                                                break;
 
430
 
 
431
                                        default:
 
432
                                                throw new PSQLException("postgresql.con.authfail", PSQLState.CONNECTION_REJECTED);
 
433
                                }
 
434
                        }
 
435
                        while (areq != AUTH_REQ_OK);
 
436
 
 
437
                }
 
438
                catch (IOException e)
 
439
                {
 
440
                        throw new PSQLException("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e);
 
441
                }
 
442
 
 
443
                int beresp;
 
444
                do
 
445
                {
 
446
                        beresp = pgStream.ReceiveChar();
 
447
                        switch (beresp)
 
448
                        {
 
449
                            case 'Z':
 
450
                                        //ready for query
 
451
                                        break;
 
452
                                case 'K':
 
453
                                        int l_msgLen = pgStream.ReceiveIntegerR(4);
 
454
                                        if (l_msgLen != 12) throw new PSQLException("postgresql.con.setup", PSQLState.CONNECTION_UNABLE_TO_CONNECT);
 
455
                                        pid = pgStream.ReceiveIntegerR(4);
 
456
                                        ckey = pgStream.ReceiveIntegerR(4);
 
457
                                        break;
 
458
                                case 'E':
 
459
                                        int l_elen = pgStream.ReceiveIntegerR(4);
 
460
                                        throw new PSQLException("postgresql.con.backend", PSQLState.CONNECTION_UNABLE_TO_CONNECT, PSQLException.parseServerError(encoding.decode(pgStream.Receive(l_elen-4))));
 
461
                                case 'N':
 
462
                                        int l_nlen = pgStream.ReceiveIntegerR(4);
 
463
                                        PSQLException notify = PSQLException.parseServerError(encoding.decode(pgStream.Receive(l_nlen-4)));
 
464
                                        addWarning(notify.getMessage());
 
465
                                        break;
 
466
                            case 'S':
 
467
                                        //TODO: handle parameter status messages
 
468
                                        int l_len = pgStream.ReceiveIntegerR(4);
 
469
                                        String l_pStatus = encoding.decode(pgStream.Receive(l_len-4));
 
470
                                        if (Driver.logDebug)
 
471
                                                Driver.debug("ParameterStatus="+ l_pStatus);
 
472
                                        break;
 
473
                                default:
 
474
                                        if (Driver.logDebug)
 
475
                                                Driver.debug("invalid state="+ (char)beresp);
 
476
                                        throw new PSQLException("postgresql.con.setup", PSQLState.CONNECTION_UNABLE_TO_CONNECT);
 
477
                        }
 
478
                }
 
479
                while (beresp != 'Z');
 
480
                // read ReadyForQuery
 
481
                if (pgStream.ReceiveIntegerR(4) != 5) throw new PSQLException("postgresql.con.setup", PSQLState.CONNECTION_UNABLE_TO_CONNECT); 
 
482
                //TODO: handle transaction status
 
483
                char l_tStatus = (char)pgStream.ReceiveChar();
 
484
 
 
485
                // "pg_encoding_to_char(1)" will return 'EUC_JP' for a backend compiled with multibyte,
 
486
                // otherwise it's hardcoded to 'SQL_ASCII'.
 
487
                // If the backend doesn't know about multibyte we can't assume anything about the encoding
 
488
                // used, so we denote this with 'UNKNOWN'.
 
489
                //Note: begining with 7.2 we should be using pg_client_encoding() which
 
490
                //is new in 7.2.  However it isn't easy to conditionally call this new
 
491
                //function, since we don't yet have the information as to what server
 
492
                //version we are talking to.  Thus we will continue to call
 
493
                //getdatabaseencoding() until we drop support for 7.1 and older versions
 
494
                //or until someone comes up with a conditional way to run one or
 
495
                //the other function depending on server version that doesn't require
 
496
                //two round trips to the server per connection
 
497
 
 
498
                final String encodingQuery =
 
499
                        "case when pg_encoding_to_char(1) = 'SQL_ASCII' then 'UNKNOWN' else getdatabaseencoding() end";
 
500
 
 
501
                // Set datestyle and fetch db encoding in a single call, to avoid making
 
502
                // more than one round trip to the backend during connection startup.
 
503
 
 
504
 
 
505
                BaseResultSet resultSet
 
506
                        = execSQL("set datestyle to 'ISO'; select version(), " + encodingQuery + ";");
 
507
                
 
508
                if (! resultSet.next())
 
509
                {
 
510
                        throw new PSQLException("postgresql.con.failed.bad.encoding", PSQLState.CONNECTION_UNABLE_TO_CONNECT);
 
511
                }
 
512
                String version = resultSet.getString(1);
 
513
                dbVersionNumber = extractVersionNumber(version);
 
514
 
 
515
                String dbEncoding = resultSet.getString(2);
 
516
                encoding = Encoding.getEncoding(dbEncoding, p_info.getProperty("charSet"));
 
517
                //In 7.3 we are forced to do a second roundtrip to handle the case 
 
518
                //where a database may not be running in autocommit mode
 
519
                //jdbc by default assumes autocommit is on until setAutoCommit(false)
 
520
                //is called.  Therefore we need to ensure a new connection is 
 
521
                //initialized to autocommit on.
 
522
                //We also set the client encoding so that the driver only needs 
 
523
                //to deal with utf8.  We can only do this in 7.3 because multibyte 
 
524
                //support is now always included
 
525
                if (haveMinimumServerVersion("7.3")) 
 
526
                {
 
527
                        BaseResultSet acRset =
 
528
                        //TODO: if protocol V3 we can set the client encoding in startup
 
529
                        execSQL("set client_encoding = 'UNICODE'");
 
530
                        //set encoding to be unicode
 
531
                        encoding = Encoding.getEncoding("UNICODE", null);
 
532
 
 
533
                }
 
534
 
 
535
                // Initialise object handling
 
536
                initObjectTypes();
 
537
 
 
538
                // Mark the connection as ok, and cleanup
 
539
                PG_STATUS = CONNECTION_OK;
 
540
        }
 
541
 
 
542
        private void openConnectionV2(String host, int port, Properties info, String database, String url, Driver d, String password) throws SQLException
 
543
          {
 
544
                PGProtocolVersionMajor = 2;
 
545
                if (Driver.logDebug)
 
546
                        Driver.debug("Using Protocol Version2");
 
547
 
 
548
                // Now we need to construct and send an ssl startup packet
 
549
                try
 
550
                {
 
551
                        if (useSSL) {
 
552
                                if (Driver.logDebug)
 
553
                                        Driver.debug("Asking server if it supports ssl");
 
554
                                pgStream.SendInteger(8,4);
 
555
                                pgStream.SendInteger(80877103,4);
 
556
 
 
557
                                // now flush the ssl packets to the backend
 
558
                                pgStream.flush();
 
559
 
 
560
                                // Now get the response from the backend, either an error message
 
561
                                // or an authentication request
 
562
                                int beresp = pgStream.ReceiveChar();
 
563
                                if (Driver.logDebug)
 
564
                                        Driver.debug("Server response was (S=Yes,N=No): "+(char)beresp);
 
565
                                switch (beresp)
 
566
                                        {
 
567
                                        case 'E':
 
568
                                                // An error occured, so pass the error message to the
 
569
                                                // user.
 
570
                                                //
 
571
                                                // The most common one to be thrown here is:
 
572
                                                // "User authentication failed"
 
573
                                                //
 
574
                                                throw new PSQLException("postgresql.con.misc", PSQLState.CONNECTION_REJECTED, pgStream.ReceiveString(encoding));
 
575
                                                
 
576
                                        case 'N':
 
577
                                                // Server does not support ssl
 
578
                                                throw new PSQLException("postgresql.con.sslnotsupported", PSQLState.CONNECTION_FAILURE);
 
579
                                                
 
580
                                        case 'S':
 
581
                                                // Server supports ssl
 
582
                                                if (Driver.logDebug)
 
583
                                                        Driver.debug("server does support ssl");
 
584
                                                Driver.makeSSL(pgStream);
 
585
                                                break;
 
586
 
 
587
                                        default:
 
588
                                                throw new PSQLException("postgresql.con.sslfail", PSQLState.CONNECTION_FAILURE);
 
589
                                        }
 
590
                        }
 
591
                }
 
592
                catch (IOException e)
 
593
                {
 
594
                        throw new PSQLException("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e);
 
595
                }
 
596
 
 
597
 
 
598
                // Now we need to construct and send a startup packet
 
599
                try
 
600
                {
 
601
                        new StartupPacket(PGProtocolVersionMajor,
 
602
                                                          PGProtocolVersionMinor,
 
603
                                                          PG_USER,
 
604
                                                          database).writeTo(pgStream);
 
605
 
 
606
                        // now flush the startup packets to the backend
 
607
                        pgStream.flush();
 
608
 
 
609
                        // Now get the response from the backend, either an error message
 
610
                        // or an authentication request
 
611
                        int areq = -1; // must have a value here
 
612
                        do
 
613
                        {
 
614
                                int beresp = pgStream.ReceiveChar();
 
615
                                String salt = null;
 
616
                                byte [] md5Salt = new byte[4];
 
617
                                switch (beresp)
 
618
                                {
 
619
                                        case 'E':
 
620
                                                // An error occured, so pass the error message to the
 
621
                                                // user.
 
622
                                                //
 
623
                                                // The most common one to be thrown here is:
 
624
                                                // "User authentication failed"
 
625
                                                //
 
626
                                                throw new PSQLException("postgresql.con.misc", PSQLState.CONNECTION_REJECTED, pgStream.ReceiveString(encoding));
 
627
 
 
628
                                        case 'R':
 
629
                                                // Get the type of request
 
630
                                                areq = pgStream.ReceiveIntegerR(4);
 
631
                                                // Get the crypt password salt if there is one
 
632
                                                if (areq == AUTH_REQ_CRYPT)
 
633
                                                {
 
634
                                                        byte[] rst = new byte[2];
 
635
                                                        rst[0] = (byte)pgStream.ReceiveChar();
 
636
                                                        rst[1] = (byte)pgStream.ReceiveChar();
 
637
                                                        salt = new String(rst, 0, 2);
 
638
                                                        if (Driver.logDebug)
 
639
                                                                Driver.debug("Crypt salt=" + salt);
 
640
                                                }
 
641
 
 
642
                                                // Or get the md5 password salt if there is one
 
643
                                                if (areq == AUTH_REQ_MD5)
 
644
                                                {
 
645
 
 
646
                                                        md5Salt[0] = (byte)pgStream.ReceiveChar();
 
647
                                                        md5Salt[1] = (byte)pgStream.ReceiveChar();
 
648
                                                        md5Salt[2] = (byte)pgStream.ReceiveChar();
 
649
                                                        md5Salt[3] = (byte)pgStream.ReceiveChar();
 
650
                                                        if (Driver.logDebug) {
 
651
                                                                String md5SaltString = "";
 
652
                                                                for (int i=0; i<md5Salt.length; i++) {
 
653
                                                                        md5SaltString += " " + md5Salt[i];
 
654
                                                                }
 
655
                                                                Driver.debug("MD5 salt=" + md5SaltString);
 
656
                                                        }
 
657
                                                }
 
658
 
 
659
                                                // now send the auth packet
 
660
                                                switch (areq)
 
661
                                                {
 
662
                                                        case AUTH_REQ_OK:
 
663
                                                                break;
 
664
 
 
665
                                                        case AUTH_REQ_KRB4:
 
666
                                                                if (Driver.logDebug)
 
667
                                                                        Driver.debug("postgresql: KRB4");
 
668
                                                                throw new PSQLException("postgresql.con.kerb4", PSQLState.CONNECTION_REJECTED);
 
669
 
 
670
                                                        case AUTH_REQ_KRB5:
 
671
                                                                if (Driver.logDebug)
 
672
                                                                        Driver.debug("postgresql: KRB5");
 
673
                                                                throw new PSQLException("postgresql.con.kerb5", PSQLState.CONNECTION_REJECTED);
 
674
 
 
675
                                                        case AUTH_REQ_PASSWORD:
 
676
                                                                if (Driver.logDebug)
 
677
                                                                        Driver.debug("postgresql: PASSWORD");
 
678
                                                                pgStream.SendInteger(5 + password.length(), 4);
 
679
                                                                pgStream.Send(password.getBytes());
 
680
                                                                pgStream.SendInteger(0, 1);
 
681
                                                                pgStream.flush();
 
682
                                                                break;
 
683
 
 
684
                                                        case AUTH_REQ_CRYPT:
 
685
                                                                if (Driver.logDebug)
 
686
                                                                        Driver.debug("postgresql: CRYPT");
 
687
                                                                String crypted = UnixCrypt.crypt(salt, password);
 
688
                                                                pgStream.SendInteger(5 + crypted.length(), 4);
 
689
                                                                pgStream.Send(crypted.getBytes());
 
690
                                                                pgStream.SendInteger(0, 1);
 
691
                                                                pgStream.flush();
 
692
                                                                break;
 
693
 
 
694
                                                        case AUTH_REQ_MD5:
 
695
                                                                if (Driver.logDebug)
 
696
                                                                        Driver.debug("postgresql: MD5");
 
697
                                                                byte[] digest = MD5Digest.encode(PG_USER, password, md5Salt);
 
698
                                                                pgStream.SendInteger(5 + digest.length, 4);
 
699
                                                                pgStream.Send(digest);
 
700
                                                                pgStream.SendInteger(0, 1);
 
701
                                                                pgStream.flush();
 
702
                                                                break;
 
703
 
 
704
                                                        default:
 
705
                                                                throw new PSQLException("postgresql.con.auth", PSQLState.CONNECTION_REJECTED, new Integer(areq));
 
706
                                                }
 
707
                                                break;
 
708
 
 
709
                                        default:
 
710
                                                throw new PSQLException("postgresql.con.authfail", PSQLState.CONNECTION_REJECTED);
 
711
                                }
 
712
                        }
 
713
                        while (areq != AUTH_REQ_OK);
 
714
 
 
715
                }
 
716
                catch (IOException e)
 
717
                {
 
718
                        //Should be passing exception as arg.
 
719
                        throw new PSQLException("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e);
 
720
                }
 
721
 
 
722
 
 
723
                // As of protocol version 2.0, we should now receive the cancellation key and the pid
 
724
                int beresp;
 
725
                do
 
726
                {
 
727
                        beresp = pgStream.ReceiveChar();
 
728
                        switch (beresp)
 
729
                        {
 
730
                                case 'K':
 
731
                                        pid = pgStream.ReceiveIntegerR(4);
 
732
                                        ckey = pgStream.ReceiveIntegerR(4);
 
733
                                        break;
 
734
                                case 'E':
 
735
                                        throw new PSQLException("postgresql.con.backend", PSQLState.CONNECTION_UNABLE_TO_CONNECT, pgStream.ReceiveString(encoding));
 
736
                                case 'N':
 
737
                                        addWarning(pgStream.ReceiveString(encoding));
 
738
                                        break;
 
739
                                default:
 
740
                                        throw new PSQLException("postgresql.con.setup", PSQLState.CONNECTION_UNABLE_TO_CONNECT);
 
741
                        }
 
742
                }
 
743
                while (beresp == 'N');
 
744
 
 
745
                // Expect ReadyForQuery packet
 
746
                do
 
747
                {
 
748
                        beresp = pgStream.ReceiveChar();
 
749
                        switch (beresp)
 
750
                        {
 
751
                                case 'Z':
 
752
                                        break;
 
753
                                case 'N':
 
754
                                        addWarning(pgStream.ReceiveString(encoding));
 
755
                                        break;
 
756
                                case 'E':
 
757
                                        throw new PSQLException("postgresql.con.backend", PSQLState.CONNECTION_UNABLE_TO_CONNECT, pgStream.ReceiveString(encoding));
 
758
                                default:
 
759
                                        throw new PSQLException("postgresql.con.setup", PSQLState.CONNECTION_UNABLE_TO_CONNECT);
 
760
                        }
 
761
                }
 
762
                while (beresp == 'N');
 
763
                // "pg_encoding_to_char(1)" will return 'EUC_JP' for a backend compiled with multibyte,
 
764
                // otherwise it's hardcoded to 'SQL_ASCII'.
 
765
                // If the backend doesn't know about multibyte we can't assume anything about the encoding
 
766
                // used, so we denote this with 'UNKNOWN'.
 
767
                //Note: begining with 7.2 we should be using pg_client_encoding() which
 
768
                //is new in 7.2.  However it isn't easy to conditionally call this new
 
769
                //function, since we don't yet have the information as to what server
 
770
                //version we are talking to.  Thus we will continue to call
 
771
                //getdatabaseencoding() until we drop support for 7.1 and older versions
 
772
                //or until someone comes up with a conditional way to run one or
 
773
                //the other function depending on server version that doesn't require
 
774
                //two round trips to the server per connection
 
775
 
 
776
                final String encodingQuery =
 
777
                        "case when pg_encoding_to_char(1) = 'SQL_ASCII' then 'UNKNOWN' else getdatabaseencoding() end";
 
778
 
 
779
                // Set datestyle and fetch db encoding in a single call, to avoid making
 
780
                // more than one round trip to the backend during connection startup.
 
781
 
 
782
 
 
783
                BaseResultSet resultSet
 
784
                        = execSQL("set datestyle to 'ISO'; select version(), " + encodingQuery + ";");
 
785
                
 
786
                if (! resultSet.next())
 
787
                {
 
788
                        throw new PSQLException("postgresql.con.failed.bad.encoding", PSQLState.CONNECTION_UNABLE_TO_CONNECT);
 
789
                }
 
790
                String version = resultSet.getString(1);
 
791
                dbVersionNumber = extractVersionNumber(version);
 
792
 
 
793
                String dbEncoding = resultSet.getString(2);
 
794
                encoding = Encoding.getEncoding(dbEncoding, info.getProperty("charSet"));
 
795
                
 
796
                //TODO: remove this once the set is done as part of V3protocol connection initiation
 
797
                if (haveMinimumServerVersion("7.4")) 
 
798
                {
 
799
                        BaseResultSet acRset =
 
800
                                execSQL("set client_encoding = 'UNICODE'");
 
801
 
 
802
                        //set encoding to be unicode
 
803
                        encoding = Encoding.getEncoding("UNICODE", null);
 
804
                }
 
805
 
 
806
                //In 7.3 we are forced to do a second roundtrip to handle the case 
 
807
                //where a database may not be running in autocommit mode
 
808
                //jdbc by default assumes autocommit is on until setAutoCommit(false)
 
809
                //is called.  Therefore we need to ensure a new connection is 
 
810
                //initialized to autocommit on.
 
811
                //We also set the client encoding so that the driver only needs 
 
812
                //to deal with utf8.  We can only do this in 7.3+ because multibyte 
 
813
                //support is now always included
 
814
                if (haveMinimumServerVersion("7.3")  && !haveMinimumServerVersion("7.4")) 
 
815
                {
 
816
                        BaseResultSet acRset =
 
817
                                execSQL("set client_encoding = 'UNICODE'; show autocommit");
 
818
 
 
819
                        //set encoding to be unicode
 
820
                        encoding = Encoding.getEncoding("UNICODE", null);
 
821
 
 
822
                        if (!acRset.next())
 
823
                        {
 
824
                                throw new PSQLException("postgresql.con.failed.bad.autocommit", PSQLState.CONNECTION_UNABLE_TO_CONNECT);
 
825
                        }
 
826
                        //if autocommit is currently off we need to turn it on
 
827
                        //note that we will be in a transaction because the select above
 
828
                        //will have initiated the transaction so we need a commit
 
829
                        //to make the setting permanent
 
830
                        if (acRset.getString(1).equals("off"))
 
831
                        {
 
832
                                execSQL("set autocommit = on; commit;");
 
833
                        }
 
834
                }
 
835
 
 
836
                // Initialise object handling
 
837
                initObjectTypes();
 
838
 
 
839
                // Mark the connection as ok, and cleanup
 
840
                PG_STATUS = CONNECTION_OK;
 
841
        }
 
842
 
 
843
        /*
 
844
         * Return the instance of org.postgresql.Driver
 
845
         * that created this connection
 
846
         */
 
847
        public Driver getDriver()
 
848
        {
 
849
                return this_driver;
 
850
        }
 
851
 
 
852
 
 
853
        /*
 
854
         * This adds a warning to the warning chain.
 
855
         * @param msg message to add
 
856
         */
 
857
        public void addWarning(String msg)
 
858
        {
 
859
                // Add the warning to the chain
 
860
                if (firstWarning != null)
 
861
                        firstWarning.setNextWarning(new SQLWarning(msg));
 
862
                else
 
863
                        firstWarning = new SQLWarning(msg);
 
864
 
 
865
                // Now check for some specific messages
 
866
 
 
867
                // This is obsolete in 6.5, but I've left it in here so if we need to use this
 
868
                // technique again, we'll know where to place it.
 
869
                //
 
870
                // This is generated by the SQL "show datestyle"
 
871
                //if (msg.startsWith("NOTICE:") && msg.indexOf("DateStyle")>0) {
 
872
                //// 13 is the length off "DateStyle is "
 
873
                //msg = msg.substring(msg.indexOf("DateStyle is ")+13);
 
874
                //
 
875
                //for(int i=0;i<dateStyles.length;i+=2)
 
876
                //if (msg.startsWith(dateStyles[i]))
 
877
                //currentDateStyle=i+1; // this is the index of the format
 
878
                //}
 
879
        }
 
880
 
 
881
        /** Simple query execution.
 
882
         */
 
883
        public BaseResultSet execSQL (String s) throws SQLException
 
884
        {
 
885
                final Object[] nullarr = new Object[0];
 
886
                BaseStatement stat = (BaseStatement) createStatement();
 
887
                return QueryExecutor.execute(new String[] { s }, 
 
888
                                                                         nullarr, 
 
889
                                                                         stat);
 
890
        }
 
891
 
 
892
        /*
 
893
         * In SQL, a result table can be retrieved through a cursor that
 
894
         * is named.  The current row of a result can be updated or deleted
 
895
         * using a positioned update/delete statement that references the
 
896
         * cursor name.
 
897
         *
 
898
         * We support one cursor per connection.
 
899
         *
 
900
         * setCursorName sets the cursor name.
 
901
         *
 
902
         * @param cursor the cursor name
 
903
         * @exception SQLException if a database access error occurs
 
904
         */
 
905
        public void setCursorName(String cursor) throws SQLException
 
906
        {
 
907
                this.cursor = cursor;
 
908
        }
 
909
 
 
910
        /*
 
911
         * getCursorName gets the cursor name.
 
912
         *
 
913
         * @return the current cursor name
 
914
         * @exception SQLException if a database access error occurs
 
915
         */
 
916
        public String getCursorName() throws SQLException
 
917
        {
 
918
                return cursor;
 
919
        }
 
920
 
 
921
        /*
 
922
         * We are required to bring back certain information by
 
923
         * the DatabaseMetaData class.  These functions do that.
 
924
         *
 
925
         * Method getURL() brings back the URL (good job we saved it)
 
926
         *
 
927
         * @return the url
 
928
         * @exception SQLException just in case...
 
929
         */
 
930
        public String getURL() throws SQLException
 
931
        {
 
932
                return this_url;
 
933
        }
 
934
 
 
935
        /*
 
936
         * Method getUserName() brings back the User Name (again, we
 
937
         * saved it)
 
938
         *
 
939
         * @return the user name
 
940
         * @exception SQLException just in case...
 
941
         */
 
942
        int lastMessage = 0;
 
943
        public String getUserName() throws SQLException
 
944
        {
 
945
                return PG_USER;
 
946
        }
 
947
 
 
948
        /*
 
949
         * Get the character encoding to use for this connection.
 
950
         */
 
951
        public Encoding getEncoding() throws SQLException
 
952
        {
 
953
                return encoding;
 
954
        }
 
955
 
 
956
        /*
 
957
         * This returns the Fastpath API for the current connection.
 
958
         *
 
959
         * <p><b>NOTE:</b> This is not part of JDBC, but allows access to
 
960
         * functions on the org.postgresql backend itself.
 
961
         *
 
962
         * <p>It is primarily used by the LargeObject API
 
963
         *
 
964
         * <p>The best way to use this is as follows:
 
965
         *
 
966
         * <p><pre>
 
967
         * import org.postgresql.fastpath.*;
 
968
         * ...
 
969
         * Fastpath fp = ((org.postgresql.Connection)myconn).getFastpathAPI();
 
970
         * </pre>
 
971
         *
 
972
         * <p>where myconn is an open Connection to org.postgresql.
 
973
         *
 
974
         * @return Fastpath object allowing access to functions on the org.postgresql
 
975
         * backend.
 
976
         * @exception SQLException by Fastpath when initialising for first time
 
977
         */
 
978
        public Fastpath getFastpathAPI() throws SQLException
 
979
        {
 
980
                if (fastpath == null)
 
981
                        fastpath = new Fastpath(this, pgStream);
 
982
                return fastpath;
 
983
        }
 
984
 
 
985
        // This holds a reference to the Fastpath API if already open
 
986
        private Fastpath fastpath = null;
 
987
 
 
988
        /*
 
989
         * This returns the LargeObject API for the current connection.
 
990
         *
 
991
         * <p><b>NOTE:</b> This is not part of JDBC, but allows access to
 
992
         * functions on the org.postgresql backend itself.
 
993
         *
 
994
         * <p>The best way to use this is as follows:
 
995
         *
 
996
         * <p><pre>
 
997
         * import org.postgresql.largeobject.*;
 
998
         * ...
 
999
         * LargeObjectManager lo = ((org.postgresql.Connection)myconn).getLargeObjectAPI();
 
1000
         * </pre>
 
1001
         *
 
1002
         * <p>where myconn is an open Connection to org.postgresql.
 
1003
         *
 
1004
         * @return LargeObject object that implements the API
 
1005
         * @exception SQLException by LargeObject when initialising for first time
 
1006
         */
 
1007
        public LargeObjectManager getLargeObjectAPI() throws SQLException
 
1008
        {
 
1009
                if (largeobject == null)
 
1010
                        largeobject = new LargeObjectManager(this);
 
1011
                return largeobject;
 
1012
        }
 
1013
 
 
1014
        // This holds a reference to the LargeObject API if already open
 
1015
        private LargeObjectManager largeobject = null;
 
1016
 
 
1017
        /*
 
1018
         * This method is used internally to return an object based around
 
1019
         * org.postgresql's more unique data types.
 
1020
         *
 
1021
         * <p>It uses an internal Hashtable to get the handling class. If the
 
1022
         * type is not supported, then an instance of org.postgresql.util.PGobject
 
1023
         * is returned.
 
1024
         *
 
1025
         * You can use the getValue() or setValue() methods to handle the returned
 
1026
         * object. Custom objects can have their own methods.
 
1027
         *
 
1028
         * @return PGobject for this type, and set to value
 
1029
         * @exception SQLException if value is not correct for this type
 
1030
         */
 
1031
        public Object getObject(String type, String value) throws SQLException
 
1032
        {
 
1033
                try
 
1034
                {
 
1035
                        Object o = objectTypes.get(type);
 
1036
 
 
1037
                        // If o is null, then the type is unknown.
 
1038
                        // If o is not null, and it is a String, then its a class name that
 
1039
                        // extends PGobject.
 
1040
                        //
 
1041
                        // This is used to implement the org.postgresql unique types (like lseg,
 
1042
                        // point, etc).
 
1043
                        if (o != null && o instanceof String)
 
1044
                        {
 
1045
                                // 6.3 style extending PG_Object
 
1046
                                PGobject obj = null;
 
1047
                                obj = (PGobject)(Class.forName((String)o).newInstance());
 
1048
                                obj.setType(type);
 
1049
                                obj.setValue(value);
 
1050
                                return (Object)obj;
 
1051
                        }
 
1052
                }
 
1053
                catch (SQLException sx)
 
1054
                {
 
1055
                        // rethrow the exception. Done because we capture any others next
 
1056
                        sx.fillInStackTrace();
 
1057
                        throw sx;
 
1058
                }
 
1059
                catch (Exception ex)
 
1060
                {
 
1061
                        throw new PSQLException("postgresql.con.creobj", PSQLState.CONNECTION_FAILURE, type, ex);
 
1062
                }
 
1063
 
 
1064
                // should never be reached
 
1065
                return null;
 
1066
        }
 
1067
 
 
1068
        /*
 
1069
         * This allows client code to add a handler for one of org.postgresql's
 
1070
         * more unique data types.
 
1071
         *
 
1072
         * <p><b>NOTE:</b> This is not part of JDBC, but an extension.
 
1073
         *
 
1074
         * <p>The best way to use this is as follows:
 
1075
         *
 
1076
         * <p><pre>
 
1077
         * ...
 
1078
         * ((org.postgresql.Connection)myconn).addDataType("mytype","my.class.name");
 
1079
         * ...
 
1080
         * </pre>
 
1081
         *
 
1082
         * <p>where myconn is an open Connection to org.postgresql.
 
1083
         *
 
1084
         * <p>The handling class must extend org.postgresql.util.PGobject
 
1085
         *
 
1086
         * @see org.postgresql.util.PGobject
 
1087
         */
 
1088
        public void addDataType(String type, String name)
 
1089
        {
 
1090
                objectTypes.put(type, name);
 
1091
        }
 
1092
 
 
1093
        // This holds the available types
 
1094
        private Hashtable objectTypes = new Hashtable();
 
1095
 
 
1096
        // This array contains the types that are supported as standard.
 
1097
        //
 
1098
        // The first entry is the types name on the database, the second
 
1099
        // the full class name of the handling class.
 
1100
        //
 
1101
        private static final String defaultObjectTypes[][] = {
 
1102
                                {"box", "org.postgresql.geometric.PGbox"},
 
1103
                                {"circle", "org.postgresql.geometric.PGcircle"},
 
1104
                                {"line", "org.postgresql.geometric.PGline"},
 
1105
                                {"lseg", "org.postgresql.geometric.PGlseg"},
 
1106
                                {"path", "org.postgresql.geometric.PGpath"},
 
1107
                                {"point", "org.postgresql.geometric.PGpoint"},
 
1108
                                {"polygon", "org.postgresql.geometric.PGpolygon"},
 
1109
                                {"money", "org.postgresql.util.PGmoney"}
 
1110
                        };
 
1111
 
 
1112
        // This initialises the objectTypes hashtable
 
1113
        private void initObjectTypes()
 
1114
        {
 
1115
                for (int i = 0;i < defaultObjectTypes.length;i++)
 
1116
                        objectTypes.put(defaultObjectTypes[i][0], defaultObjectTypes[i][1]);
 
1117
        }
 
1118
 
 
1119
        /*
 
1120
         * In some cases, it is desirable to immediately release a Connection's
 
1121
         * database and JDBC resources instead of waiting for them to be
 
1122
         * automatically released (cant think why off the top of my head)
 
1123
         *
 
1124
         * <B>Note:</B> A Connection is automatically closed when it is
 
1125
         * garbage collected.  Certain fatal errors also result in a closed
 
1126
         * connection.
 
1127
         *
 
1128
         * @exception SQLException if a database access error occurs
 
1129
         */
 
1130
        public void close() throws SQLException
 
1131
        {
 
1132
                if (getPGProtocolVersionMajor() == 3) {
 
1133
                        closeV3();
 
1134
                } else {
 
1135
                        closeV2();
 
1136
                }
 
1137
        }
 
1138
 
 
1139
        public void closeV3() throws SQLException
 
1140
        {
 
1141
                if (pgStream != null)
 
1142
                {
 
1143
                        try
 
1144
                        {
 
1145
                                pgStream.SendChar('X');
 
1146
                                pgStream.SendInteger(4,4);
 
1147
                                pgStream.flush();
 
1148
                                pgStream.close();
 
1149
                        }
 
1150
                        catch (IOException e)
 
1151
                        {}
 
1152
                        finally
 
1153
                        {
 
1154
                                pgStream = null;
 
1155
                        }
 
1156
                }
 
1157
        }
 
1158
 
 
1159
        public void closeV2() throws SQLException
 
1160
        {
 
1161
                if (pgStream != null)
 
1162
                {
 
1163
                        try
 
1164
                        {
 
1165
                                pgStream.SendChar('X');
 
1166
                                pgStream.flush();
 
1167
                                pgStream.close();
 
1168
                        }
 
1169
                        catch (IOException e)
 
1170
                        {}
 
1171
                        finally
 
1172
                        {
 
1173
                                pgStream = null;
 
1174
                        }
 
1175
                }
 
1176
        }
 
1177
 
 
1178
        /*
 
1179
         * A driver may convert the JDBC sql grammar into its system's
 
1180
         * native SQL grammar prior to sending it; nativeSQL returns the
 
1181
         * native form of the statement that the driver would have sent.
 
1182
         *
 
1183
         * @param sql a SQL statement that may contain one or more '?'
 
1184
         *      parameter placeholders
 
1185
         * @return the native form of this statement
 
1186
         * @exception SQLException if a database access error occurs
 
1187
         */
 
1188
        public String nativeSQL(String sql) throws SQLException
 
1189
        {
 
1190
                return sql;
 
1191
        }
 
1192
 
 
1193
        /*
 
1194
         * The first warning reported by calls on this Connection is
 
1195
         * returned.
 
1196
         *
 
1197
         * <B>Note:</B> Sebsequent warnings will be changed to this
 
1198
         * SQLWarning
 
1199
         *
 
1200
         * @return the first SQLWarning or null
 
1201
         * @exception SQLException if a database access error occurs
 
1202
         */
 
1203
        public SQLWarning getWarnings() throws SQLException
 
1204
        {
 
1205
                return firstWarning;
 
1206
        }
 
1207
 
 
1208
        /*
 
1209
         * After this call, getWarnings returns null until a new warning
 
1210
         * is reported for this connection.
 
1211
         *
 
1212
         * @exception SQLException if a database access error occurs
 
1213
         */
 
1214
        public void clearWarnings() throws SQLException
 
1215
        {
 
1216
                firstWarning = null;
 
1217
        }
 
1218
 
 
1219
 
 
1220
        /*
 
1221
         * You can put a connection in read-only mode as a hunt to enable
 
1222
         * database optimizations
 
1223
         *
 
1224
         * <B>Note:</B> setReadOnly cannot be called while in the middle
 
1225
         * of a transaction
 
1226
         *
 
1227
         * @param readOnly - true enables read-only mode; false disables it
 
1228
         * @exception SQLException if a database access error occurs
 
1229
         */
 
1230
        public void setReadOnly(boolean readOnly) throws SQLException
 
1231
        {
 
1232
                this.readOnly = readOnly;
 
1233
        }
 
1234
 
 
1235
        /*
 
1236
         * Tests to see if the connection is in Read Only Mode.  Note that
 
1237
         * we cannot really put the database in read only mode, but we pretend
 
1238
         * we can by returning the value of the readOnly flag
 
1239
         *
 
1240
         * @return true if the connection is read only
 
1241
         * @exception SQLException if a database access error occurs
 
1242
         */
 
1243
        public boolean isReadOnly() throws SQLException
 
1244
        {
 
1245
                return readOnly;
 
1246
        }
 
1247
 
 
1248
        /*
 
1249
         * If a connection is in auto-commit mode, than all its SQL
 
1250
         * statements will be executed and committed as individual
 
1251
         * transactions.  Otherwise, its SQL statements are grouped
 
1252
         * into transactions that are terminated by either commit()
 
1253
         * or rollback().  By default, new connections are in auto-
 
1254
         * commit mode.  The commit occurs when the statement completes
 
1255
         * or the next execute occurs, whichever comes first.  In the
 
1256
         * case of statements returning a ResultSet, the statement
 
1257
         * completes when the last row of the ResultSet has been retrieved
 
1258
         * or the ResultSet has been closed.  In advanced cases, a single
 
1259
         * statement may return multiple results as well as output parameter
 
1260
         * values.      Here the commit occurs when all results and output param
 
1261
         * values have been retrieved.
 
1262
         *
 
1263
         * @param autoCommit - true enables auto-commit; false disables it
 
1264
         * @exception SQLException if a database access error occurs
 
1265
         */
 
1266
        public void setAutoCommit(boolean autoCommit) throws SQLException
 
1267
        {
 
1268
                if (this.autoCommit == autoCommit)
 
1269
                        return ;
 
1270
                if (autoCommit)
 
1271
                {
 
1272
                                execSQL("end");                         
 
1273
                }
 
1274
                else
 
1275
                {
 
1276
                        if (haveMinimumServerVersion("7.1"))
 
1277
                        {
 
1278
                                execSQL("begin;" + getIsolationLevelSQL());
 
1279
                        }
 
1280
                        else
 
1281
                        {
 
1282
                                execSQL("begin");
 
1283
                                execSQL(getIsolationLevelSQL());
 
1284
                        }
 
1285
                }
 
1286
                this.autoCommit = autoCommit;
 
1287
        }
 
1288
 
 
1289
        /*
 
1290
         * gets the current auto-commit state
 
1291
         *
 
1292
         * @return Current state of the auto-commit mode
 
1293
         * @see setAutoCommit
 
1294
         */
 
1295
        public boolean getAutoCommit()
 
1296
        {
 
1297
                return this.autoCommit;
 
1298
        }
 
1299
 
 
1300
        /*
 
1301
         * The method commit() makes all changes made since the previous
 
1302
         * commit/rollback permanent and releases any database locks currently
 
1303
         * held by the Connection.      This method should only be used when
 
1304
         * auto-commit has been disabled.  (If autoCommit == true, then we
 
1305
         * just return anyhow)
 
1306
         *
 
1307
         * @exception SQLException if a database access error occurs
 
1308
         * @see setAutoCommit
 
1309
         */
 
1310
        public void commit() throws SQLException
 
1311
        {
 
1312
                if (autoCommit)
 
1313
                        return ;
 
1314
                //TODO: delay starting new transaction until first command
 
1315
                if (haveMinimumServerVersion("7.1"))
 
1316
                {
 
1317
                        execSQL("commit;begin;" + getIsolationLevelSQL());
 
1318
                }
 
1319
                else
 
1320
                {
 
1321
                        execSQL("commit");
 
1322
                        execSQL("begin");
 
1323
                        execSQL(getIsolationLevelSQL());
 
1324
                }
 
1325
        }
 
1326
 
 
1327
        /*
 
1328
         * The method rollback() drops all changes made since the previous
 
1329
         * commit/rollback and releases any database locks currently held by
 
1330
         * the Connection.
 
1331
         *
 
1332
         * @exception SQLException if a database access error occurs
 
1333
         * @see commit
 
1334
         */
 
1335
        public void rollback() throws SQLException
 
1336
        {
 
1337
                if (autoCommit)
 
1338
                        return ;
 
1339
                //TODO: delay starting transaction until first command
 
1340
                if (haveMinimumServerVersion("7.1"))
 
1341
                {
 
1342
                        execSQL("rollback; begin;" + getIsolationLevelSQL());
 
1343
                }
 
1344
                else
 
1345
                {
 
1346
                        execSQL("rollback");
 
1347
                        execSQL("begin");
 
1348
                        execSQL(getIsolationLevelSQL());
 
1349
                }
 
1350
        }
 
1351
 
 
1352
        /*
 
1353
         * Get this Connection's current transaction isolation mode.
 
1354
         *
 
1355
         * @return the current TRANSACTION_* mode value
 
1356
         * @exception SQLException if a database access error occurs
 
1357
         */
 
1358
        public int getTransactionIsolation() throws SQLException
 
1359
        {
 
1360
                String sql = "show transaction isolation level";
 
1361
                String level = null;
 
1362
                if (haveMinimumServerVersion("7.3")) {
 
1363
                        BaseResultSet rs = execSQL(sql);
 
1364
                        if (rs.next()) {
 
1365
                                level = rs.getString(1);
 
1366
                        }
 
1367
                        rs.close();
 
1368
                } else {
 
1369
                        BaseResultSet l_rs = execSQL(sql);
 
1370
                        BaseStatement l_stat = l_rs.getPGStatement();
 
1371
                        SQLWarning warning = l_stat.getWarnings();
 
1372
                        if (warning != null)
 
1373
                        {
 
1374
                                level = warning.getMessage();
 
1375
                        }
 
1376
                        l_rs.close();
 
1377
                        l_stat.close();
 
1378
                }
 
1379
                if (level != null) {
 
1380
                        level = level.toUpperCase();
 
1381
                        if (level.indexOf("READ COMMITTED") != -1)
 
1382
                                return Connection.TRANSACTION_READ_COMMITTED;
 
1383
                        else if (level.indexOf("READ UNCOMMITTED") != -1)
 
1384
                                return Connection.TRANSACTION_READ_UNCOMMITTED;
 
1385
                        else if (level.indexOf("REPEATABLE READ") != -1)
 
1386
                                return Connection.TRANSACTION_REPEATABLE_READ;
 
1387
                        else if (level.indexOf("SERIALIZABLE") != -1)
 
1388
                                return Connection.TRANSACTION_SERIALIZABLE;
 
1389
                }
 
1390
                return Connection.TRANSACTION_READ_COMMITTED;
 
1391
        }
 
1392
 
 
1393
        /*
 
1394
         * You can call this method to try to change the transaction
 
1395
         * isolation level using one of the TRANSACTION_* values.
 
1396
         *
 
1397
         * <B>Note:</B> setTransactionIsolation cannot be called while
 
1398
         * in the middle of a transaction
 
1399
         *
 
1400
         * @param level one of the TRANSACTION_* isolation values with
 
1401
         *      the exception of TRANSACTION_NONE; some databases may
 
1402
         *      not support other values
 
1403
         * @exception SQLException if a database access error occurs
 
1404
         * @see java.sql.DatabaseMetaData#supportsTransactionIsolationLevel
 
1405
         */
 
1406
        public void setTransactionIsolation(int level) throws SQLException
 
1407
        {
 
1408
                //In 7.1 and later versions of the server it is possible using
 
1409
                //the "set session" command to set this once for all future txns
 
1410
                //however in 7.0 and prior versions it is necessary to set it in
 
1411
                //each transaction, thus adding complexity below.
 
1412
                //When we decide to drop support for servers older than 7.1
 
1413
                //this can be simplified
 
1414
                isolationLevel = level;
 
1415
                String isolationLevelSQL;
 
1416
 
 
1417
                if (!haveMinimumServerVersion("7.1"))
 
1418
                {
 
1419
                        isolationLevelSQL = getIsolationLevelSQL();
 
1420
                }
 
1421
                else
 
1422
                {
 
1423
                        isolationLevelSQL = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ";
 
1424
                        switch (isolationLevel)
 
1425
                        {
 
1426
                                case Connection.TRANSACTION_READ_COMMITTED:
 
1427
                                        isolationLevelSQL += "READ COMMITTED";
 
1428
                                        break;
 
1429
                                case Connection.TRANSACTION_SERIALIZABLE:
 
1430
                                        isolationLevelSQL += "SERIALIZABLE";
 
1431
                                        break;
 
1432
                                default:
 
1433
                                        throw new PSQLException("postgresql.con.isolevel", PSQLState.TRANSACTION_STATE_INVALID,
 
1434
                                                                                        new Integer(isolationLevel));
 
1435
                        }
 
1436
                }
 
1437
                execSQL(isolationLevelSQL);
 
1438
        }
 
1439
 
 
1440
        /*
 
1441
         * Helper method used by setTransactionIsolation(), commit(), rollback()
 
1442
         * and setAutoCommit(). This returns the SQL string needed to
 
1443
         * set the isolation level for a transaction.  In 7.1 and later it
 
1444
         * is possible to set a default isolation level that applies to all
 
1445
         * future transactions, this method is only necesary for 7.0 and older
 
1446
         * servers, and should be removed when support for these older
 
1447
         * servers are dropped
 
1448
         */
 
1449
        protected String getIsolationLevelSQL() throws SQLException
 
1450
        {
 
1451
                //7.1 and higher servers have a default specified so
 
1452
                //no additional SQL is required to set the isolation level
 
1453
                if (haveMinimumServerVersion("7.1"))
 
1454
                {
 
1455
                        return "";
 
1456
                }
 
1457
                StringBuffer sb = new StringBuffer("SET TRANSACTION ISOLATION LEVEL");
 
1458
 
 
1459
                switch (isolationLevel)
 
1460
                {
 
1461
                        case Connection.TRANSACTION_READ_COMMITTED:
 
1462
                                sb.append(" READ COMMITTED");
 
1463
                                break;
 
1464
 
 
1465
                        case Connection.TRANSACTION_SERIALIZABLE:
 
1466
                                sb.append(" SERIALIZABLE");
 
1467
                                break;
 
1468
 
 
1469
                        default:
 
1470
                                throw new PSQLException("postgresql.con.isolevel", PSQLState.TRANSACTION_STATE_INVALID, new Integer(isolationLevel));
 
1471
                }
 
1472
                return sb.toString();
 
1473
        }
 
1474
 
 
1475
        /*
 
1476
         * A sub-space of this Connection's database may be selected by
 
1477
         * setting a catalog name.      If the driver does not support catalogs,
 
1478
         * it will silently ignore this request
 
1479
         *
 
1480
         * @exception SQLException if a database access error occurs
 
1481
         */
 
1482
        public void setCatalog(String catalog) throws SQLException
 
1483
        {
 
1484
                //no-op
 
1485
        }
 
1486
 
 
1487
        /*
 
1488
         * Return the connections current catalog name, or null if no
 
1489
         * catalog name is set, or we dont support catalogs.
 
1490
         *
 
1491
         * @return the current catalog name or null
 
1492
         * @exception SQLException if a database access error occurs
 
1493
         */
 
1494
        public String getCatalog() throws SQLException
 
1495
        {
 
1496
                return PG_DATABASE;
 
1497
        }
 
1498
 
 
1499
        /*
 
1500
         * Overides finalize(). If called, it closes the connection.
 
1501
         *
 
1502
         * This was done at the request of Rachel Greenham
 
1503
         * <rachel@enlarion.demon.co.uk> who hit a problem where multiple
 
1504
         * clients didn't close the connection, and once a fortnight enough
 
1505
         * clients were open to kill the org.postgres server.
 
1506
         */
 
1507
        public void finalize() throws Throwable
 
1508
        {
 
1509
                close();
 
1510
        }
 
1511
 
 
1512
        private static String extractVersionNumber(String fullVersionString)
 
1513
        {
 
1514
                StringTokenizer versionParts = new StringTokenizer(fullVersionString);
 
1515
                versionParts.nextToken(); /* "PostgreSQL" */
 
1516
                return versionParts.nextToken(); /* "X.Y.Z" */
 
1517
        }
 
1518
 
 
1519
        /*
 
1520
         * Get server version number
 
1521
         */
 
1522
        public String getDBVersionNumber()
 
1523
        {
 
1524
                return dbVersionNumber;
 
1525
        }
 
1526
 
 
1527
        // Parse a "dirty" integer surrounded by non-numeric characters
 
1528
        private static int integerPart(String dirtyString)
 
1529
        {
 
1530
                int start, end;
 
1531
 
 
1532
                for (start = 0; start < dirtyString.length() && !Character.isDigit(dirtyString.charAt(start)); ++start)
 
1533
                        ;
 
1534
                
 
1535
                for (end = start; end < dirtyString.length() && Character.isDigit(dirtyString.charAt(end)); ++end)
 
1536
                        ;
 
1537
 
 
1538
                if (start == end)
 
1539
                        return 0;
 
1540
 
 
1541
                return Integer.parseInt(dirtyString.substring(start, end));
 
1542
        }
 
1543
 
 
1544
        /*
 
1545
         * Get server major version
 
1546
         */
 
1547
        public int getServerMajorVersion()
 
1548
        {        
 
1549
                try
 
1550
                {
 
1551
                        StringTokenizer versionTokens = new StringTokenizer(dbVersionNumber, ".");  // aaXbb.ccYdd       
 
1552
                        return integerPart(versionTokens.nextToken()); // return X
 
1553
                }
 
1554
                catch (NoSuchElementException e)
 
1555
                {
 
1556
                        return 0;
 
1557
                }
 
1558
        }
 
1559
 
 
1560
        /*
 
1561
         * Get server minor version
 
1562
         */
 
1563
        public int getServerMinorVersion()
 
1564
        {        
 
1565
                try
 
1566
                {
 
1567
                        StringTokenizer versionTokens = new StringTokenizer(dbVersionNumber, ".");  // aaXbb.ccYdd
 
1568
                        versionTokens.nextToken(); // Skip aaXbb
 
1569
                        return integerPart(versionTokens.nextToken()); // return Y
 
1570
                }
 
1571
                catch (NoSuchElementException e)
 
1572
                {
 
1573
                        return 0;
 
1574
                }
 
1575
        }
 
1576
        
 
1577
        /**
 
1578
         * Is the server we are connected to running at least this version?
 
1579
         * This comparison method will fail whenever a major or minor version
 
1580
         * goes to two digits (10.3.0) or (7.10.1).
 
1581
         */
 
1582
        public boolean haveMinimumServerVersion(String ver) throws SQLException
 
1583
        {
 
1584
                return (getDBVersionNumber().compareTo(ver) >= 0);
 
1585
        }
 
1586
 
 
1587
        /*
 
1588
         * This method returns true if the compatible level set in the connection
 
1589
         * (which can be passed into the connection or specified in the URL)
 
1590
         * is at least the value passed to this method.  This is used to toggle
 
1591
         * between different functionality as it changes across different releases
 
1592
         * of the jdbc driver code.  The values here are versions of the jdbc client
 
1593
         * and not server versions.  For example in 7.1 get/setBytes worked on
 
1594
         * LargeObject values, in 7.2 these methods were changed to work on bytea
 
1595
         * values.      This change in functionality could be disabled by setting the
 
1596
         * "compatible" level to be 7.1, in which case the driver will revert to
 
1597
         * the 7.1 functionality.
 
1598
         */
 
1599
        public boolean haveMinimumCompatibleVersion(String ver) throws SQLException
 
1600
        {
 
1601
                return (compatible.compareTo(ver) >= 0);
 
1602
        }
 
1603
 
 
1604
 
 
1605
        /*
 
1606
         * This returns the java.sql.Types type for a PG type oid
 
1607
         *
 
1608
         * @param oid PostgreSQL type oid
 
1609
         * @return the java.sql.Types type
 
1610
         * @exception SQLException if a database access error occurs
 
1611
         */
 
1612
        public int getSQLType(int oid) throws SQLException
 
1613
        {
 
1614
                Integer sqlType = (Integer)sqlTypeCache.get(new Integer(oid));
 
1615
 
 
1616
                // it's not in the cache, so perform a query, and add the result to the cache
 
1617
                if (sqlType == null)
 
1618
                {
 
1619
                        String pgType;
 
1620
                        // The opaque type does not exist in the system catalogs.
 
1621
                        if (oid == 0) {
 
1622
                                pgType = "opaque";
 
1623
                        } else {
 
1624
                                String sql;
 
1625
                                if (haveMinimumServerVersion("7.3")) {
 
1626
                                        sql = "SELECT typname FROM pg_catalog.pg_type WHERE oid = " +oid;
 
1627
                                } else {
 
1628
                                        sql = "SELECT typname FROM pg_type WHERE oid = " +oid;
 
1629
                                }
 
1630
                                BaseResultSet result = execSQL(sql);
 
1631
                                if (result.getColumnCount() != 1 || result.getTupleCount() != 1) {
 
1632
                                        throw new PSQLException("postgresql.unexpected", PSQLState.UNEXPECTED_ERROR);
 
1633
                                }
 
1634
                                result.next();
 
1635
                                pgType = result.getString(1);
 
1636
                                result.close();
 
1637
                        }
 
1638
                        Integer iOid = new Integer(oid);
 
1639
                        sqlType = new Integer(getSQLType(pgType));
 
1640
                        sqlTypeCache.put(iOid, sqlType);
 
1641
                        pgTypeCache.put(iOid, pgType);
 
1642
                }
 
1643
 
 
1644
                return sqlType.intValue();
 
1645
        }
 
1646
 
 
1647
        /*
 
1648
         * This returns the oid for a given PG data type
 
1649
         * @param typeName PostgreSQL type name
 
1650
         * @return PostgreSQL oid value for a field of this type
 
1651
         */
 
1652
        public int getPGType(String typeName) throws SQLException
 
1653
        {
 
1654
                int oid = -1;
 
1655
                if (typeName != null)
 
1656
                {
 
1657
                        Integer oidValue = (Integer) typeOidCache.get(typeName);
 
1658
                        if (oidValue != null)
 
1659
                        {
 
1660
                                oid = oidValue.intValue();
 
1661
                        }
 
1662
                        else
 
1663
                        {
 
1664
                                // it's not in the cache, so perform a query, and add the result to the cache
 
1665
                                String sql;
 
1666
                                if (haveMinimumServerVersion("7.3")) {
 
1667
                                        sql = "SELECT oid FROM pg_catalog.pg_type WHERE typname='" + typeName + "'";
 
1668
                                } else {
 
1669
                                        sql = "SELECT oid FROM pg_type WHERE typname='" + typeName + "'";
 
1670
                                }
 
1671
                                BaseResultSet result = execSQL(sql);
 
1672
                                if (result.getColumnCount() != 1 || result.getTupleCount() != 1)
 
1673
                                        throw new PSQLException("postgresql.unexpected", PSQLState.UNEXPECTED_ERROR);
 
1674
                                result.next();
 
1675
                                oid = Integer.parseInt(result.getString(1));
 
1676
                                typeOidCache.put(typeName, new Integer(oid));
 
1677
                                result.close();
 
1678
                        }
 
1679
                }
 
1680
                return oid;
 
1681
        }
 
1682
 
 
1683
        /*
 
1684
         * We also need to get the PG type name as returned by the back end.
 
1685
         *
 
1686
         * @return the String representation of the type of this field
 
1687
         * @exception SQLException if a database access error occurs
 
1688
         */
 
1689
        public String getPGType(int oid) throws SQLException
 
1690
        {
 
1691
                String pgType = (String) pgTypeCache.get(new Integer(oid));
 
1692
                if (pgType == null)
 
1693
                {
 
1694
                        getSQLType(oid);
 
1695
                        pgType = (String) pgTypeCache.get(new Integer(oid));
 
1696
                }
 
1697
                return pgType;
 
1698
        }
 
1699
 
 
1700
        //Because the get/setLogStream methods are deprecated in JDBC2
 
1701
        //we use them for JDBC1 here and override this method in the jdbc2
 
1702
        //version of this class
 
1703
        protected void enableDriverManagerLogging()
 
1704
        {
 
1705
                if (DriverManager.getLogStream() == null)
 
1706
                {
 
1707
                        DriverManager.setLogStream(System.out);
 
1708
                }
 
1709
        }
 
1710
 
 
1711
        // This is a cache of the DatabaseMetaData instance for this connection
 
1712
        protected java.sql.DatabaseMetaData metadata;
 
1713
 
 
1714
 
 
1715
        /*
 
1716
         * Tests to see if a Connection is closed
 
1717
         *
 
1718
         * @return the status of the connection
 
1719
         * @exception SQLException (why?)
 
1720
         */
 
1721
        public boolean isClosed() throws SQLException
 
1722
        {
 
1723
                return (pgStream == null);
 
1724
        }
 
1725
 
 
1726
        /*
 
1727
         * This implemetation uses the jdbc1Types array to support the jdbc1
 
1728
         * datatypes.  Basically jdbc1 and jdbc2 are the same, except that
 
1729
         * jdbc2 adds the Array types.
 
1730
         */
 
1731
        public int getSQLType(String pgTypeName)
 
1732
        {
 
1733
                int sqlType = Types.OTHER; // default value
 
1734
                for (int i = 0;i < jdbc1Types.length;i++)
 
1735
                {
 
1736
                        if (pgTypeName.equals(jdbc1Types[i]))
 
1737
                        {
 
1738
                                sqlType = jdbc1Typei[i];
 
1739
                                break;
 
1740
                        }
 
1741
                }
 
1742
                return sqlType;
 
1743
        }
 
1744
 
 
1745
        /*
 
1746
         * This table holds the org.postgresql names for the types supported.
 
1747
         * Any types that map to Types.OTHER (eg POINT) don't go into this table.
 
1748
         * They default automatically to Types.OTHER
 
1749
         *
 
1750
         * Note: This must be in the same order as below.
 
1751
         *
 
1752
         * Tip: keep these grouped together by the Types. value
 
1753
         */
 
1754
        private static final String jdbc1Types[] = {
 
1755
                                "int2",
 
1756
                                "int4", "oid",
 
1757
                                "int8",
 
1758
                                "cash", "money",
 
1759
                                "numeric",
 
1760
                                "float4",
 
1761
                                "float8",
 
1762
                                "bpchar", "char", "char2", "char4", "char8", "char16",
 
1763
                                "varchar", "text", "name", "filename",
 
1764
                                "bytea",
 
1765
                                "bool",
 
1766
                                "bit",
 
1767
                                "date",
 
1768
                                "time",
 
1769
                                "abstime", "timestamp", "timestamptz"
 
1770
                        };
 
1771
 
 
1772
        /*
 
1773
         * This table holds the JDBC type for each entry above.
 
1774
         *
 
1775
         * Note: This must be in the same order as above
 
1776
         *
 
1777
         * Tip: keep these grouped together by the Types. value
 
1778
         */
 
1779
        private static final int jdbc1Typei[] = {
 
1780
                Types.SMALLINT,
 
1781
                Types.INTEGER, Types.INTEGER,
 
1782
                Types.BIGINT,
 
1783
                Types.DOUBLE, Types.DOUBLE,
 
1784
                Types.NUMERIC,
 
1785
                Types.REAL,
 
1786
                Types.DOUBLE,
 
1787
                Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR,
 
1788
                Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
 
1789
                Types.BINARY,
 
1790
                Types.BIT,
 
1791
                Types.BIT,
 
1792
                Types.DATE,
 
1793
                Types.TIME,
 
1794
                Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP
 
1795
        };
 
1796
 
 
1797
        public void cancelQuery() throws SQLException
 
1798
        {
 
1799
                org.postgresql.core.PGStream cancelStream = null;
 
1800
                try
 
1801
                {
 
1802
                        cancelStream = new org.postgresql.core.PGStream(PG_HOST, PG_PORT);
 
1803
                }
 
1804
                catch (ConnectException cex)
 
1805
                {
 
1806
                        // Added by Peter Mount <peter@retep.org.uk>
 
1807
                        // ConnectException is thrown when the connection cannot be made.
 
1808
                        // we trap this an return a more meaningful message for the end user
 
1809
                        throw new PSQLException ("postgresql.con.refused", PSQLState.CONNECTION_REJECTED);
 
1810
                }
 
1811
                catch (IOException e)
 
1812
                {
 
1813
                        throw new PSQLException ("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e);
 
1814
                }
 
1815
 
 
1816
                // Now we need to construct and send a cancel packet
 
1817
                try
 
1818
                {
 
1819
                        cancelStream.SendInteger(16, 4);
 
1820
                        cancelStream.SendInteger(80877102, 4);
 
1821
                        cancelStream.SendInteger(pid, 4);
 
1822
                        cancelStream.SendInteger(ckey, 4);
 
1823
                        cancelStream.flush();
 
1824
                }
 
1825
                catch (IOException e)
 
1826
                {
 
1827
                        throw new PSQLException("postgresql.con.failed", PSQLState.CONNECTION_UNABLE_TO_CONNECT, e);
 
1828
                }
 
1829
                finally
 
1830
                {
 
1831
                        try
 
1832
                        {
 
1833
                                if (cancelStream != null)
 
1834
                                        cancelStream.close();
 
1835
                        }
 
1836
                        catch (IOException e)
 
1837
                        {} // Ignore
 
1838
                }
 
1839
        }
 
1840
 
 
1841
 
 
1842
        //Methods to support postgres notifications
 
1843
        public void addNotification(org.postgresql.PGNotification p_notification)
 
1844
        {
 
1845
                if (m_notifications == null)
 
1846
                        m_notifications = new Vector();
 
1847
                m_notifications.addElement(p_notification);
 
1848
        }
 
1849
 
 
1850
        public PGNotification[] getNotifications()
 
1851
        {
 
1852
                PGNotification[] l_return = null;
 
1853
                if (m_notifications != null)
 
1854
                {
 
1855
                        l_return = new PGNotification[m_notifications.size()];
 
1856
                        m_notifications.copyInto(l_return);
 
1857
                }
 
1858
                m_notifications = null;
 
1859
                return l_return;
 
1860
        }
 
1861
}
 
1862
 
 
1863