]> granicus.if.org Git - postgresql/blob - src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java
cda5ecb59c93229a59305df29e15d22ef74db1ac
[postgresql] / src / interfaces / jdbc / org / postgresql / jdbc1 / AbstractJdbc1Statement.java
1 package org.postgresql.jdbc1;
2
3 import java.io.*;
4
5 import java.math.BigDecimal;
6 import java.sql.*;
7 import java.util.Vector;
8 import org.postgresql.largeobject.*;
9 import org.postgresql.util.*;
10
11 /* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.17 2003/02/09 23:14:55 barry Exp $
12  * This class defines methods of the jdbc1 specification.  This class is
13  * extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2
14  * methods.  The real Statement class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Statement
15  */
16 public abstract class AbstractJdbc1Statement implements org.postgresql.PGStatement
17 {
18
19         // The connection who created us
20         protected AbstractJdbc1Connection connection;
21
22         public org.postgresql.PGConnection getPGConnection() {
23                 return connection;
24         }
25
26         /** The warnings chain. */
27         protected SQLWarning warnings = null;
28
29         /** Maximum number of rows to return, 0 = unlimited */
30         protected int maxrows = 0;
31
32         /** Number of rows to get in a batch. */
33         protected int fetchSize = 0;
34
35         /** Timeout (in seconds) for a query (not used) */
36         protected int timeout = 0;
37
38         protected boolean replaceProcessingEnabled = true;
39
40         /** The current results */
41         protected java.sql.ResultSet result = null;
42
43         // Static variables for parsing SQL when replaceProcessing is true.
44         private static final short IN_SQLCODE = 0;
45         private static final short IN_STRING = 1;
46         private static final short BACKSLASH = 2;
47         private static final short ESC_TIMEDATE = 3;
48
49         // Some performance caches
50         private StringBuffer sbuf = new StringBuffer(32);
51
52         //Used by the preparedstatement style methods
53         protected String[] m_sqlFragments;
54         private String[] m_origSqlFragments;
55         private String[] m_executeSqlFragments;
56         protected Object[] m_binds = new Object[0];
57
58         protected String[] m_bindTypes = new String[0];
59         protected String m_statementName = null;
60
61         private boolean m_useServerPrepare = false;
62         private static int m_preparedCount = 1;
63
64         //Used by the callablestatement style methods
65         private static final String JDBC_SYNTAX = "{[? =] call <some_function> ([? [,?]*]) }";
66         private static final String RESULT_ALIAS = "result";
67         private String originalSql = "";
68         private boolean isFunction;
69         // functionReturnType contains the user supplied value to check
70         // testReturn contains a modified version to make it easier to
71         // check the getXXX methods..
72         private int functionReturnType;
73         private int testReturn;
74         // returnTypeSet is true when a proper call to registerOutParameter has been made
75         private boolean returnTypeSet;
76         protected Object callResult;
77
78
79         public abstract java.sql.ResultSet createResultSet(org.postgresql.Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException;
80
81         public AbstractJdbc1Statement (AbstractJdbc1Connection connection)
82         {
83                 this.connection = connection;
84         }
85
86         public AbstractJdbc1Statement (AbstractJdbc1Connection connection, String p_sql) throws SQLException
87         {
88                 this.connection = connection;
89                 parseSqlStmt(p_sql);  // this allows Callable stmt to override
90         }
91
92         protected void parseSqlStmt (String p_sql) throws SQLException
93         {
94                 String l_sql = p_sql;
95
96                 l_sql = replaceProcessing(l_sql);
97
98                 if (this instanceof CallableStatement)
99                 {
100                         l_sql = modifyJdbcCall(l_sql);
101                 }
102
103                 Vector v = new Vector();
104                 boolean inQuotes = false;
105                 int lastParmEnd = 0, i;
106
107                 for (i = 0; i < l_sql.length(); ++i)
108                 {
109                         int c = l_sql.charAt(i);
110
111                         if (c == '\'')
112                                 inQuotes = !inQuotes;
113                         if (c == '?' && !inQuotes)
114                         {
115                                 v.addElement(l_sql.substring (lastParmEnd, i));
116                                 lastParmEnd = i + 1;
117                         }
118                 }
119                 v.addElement(l_sql.substring (lastParmEnd, l_sql.length()));
120
121                 m_sqlFragments = new String[v.size()];
122                 m_binds = new String[v.size() - 1];
123                 m_bindTypes = new String[v.size() - 1];
124
125                 for (i = 0 ; i < m_sqlFragments.length; ++i)
126                         m_sqlFragments[i] = (String)v.elementAt(i);
127
128         }
129
130   
131         /*
132          * Execute a SQL statement that retruns a single ResultSet
133          *
134          * @param sql typically a static SQL SELECT statement
135          * @return a ResulSet that contains the data produced by the query
136          * @exception SQLException if a database access error occurs
137          */
138         public java.sql.ResultSet executeQuery(String p_sql) throws SQLException
139         {
140                 String l_sql = replaceProcessing(p_sql);
141                 m_sqlFragments = new String[] {l_sql};
142                 m_binds = new Object[0];
143                 //If we have already created a server prepared statement, we need
144                 //to deallocate the existing one
145                 if (m_statementName != null)
146                 {
147                         try
148                         {
149                                 ((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName);
150                         }
151                         catch (Exception e)
152                         {
153                         }
154                         finally
155                         {
156                                 m_statementName = null;
157                                 m_origSqlFragments = null;
158                                 m_executeSqlFragments = null;
159                         }
160                 }
161                 return executeQuery();
162         }
163
164         /*
165          * A Prepared SQL query is executed and its ResultSet is returned
166          *
167          * @return a ResultSet that contains the data produced by the
168          *               *      query - never null
169          * @exception SQLException if a database access error occurs
170          */
171         public java.sql.ResultSet executeQuery() throws SQLException
172         {
173                 if (fetchSize > 0)
174                         this.executeWithCursor();
175                 else
176                         this.execute();
177         
178                 while (result != null && !((AbstractJdbc1ResultSet)result).reallyResultSet())
179                         result = ((AbstractJdbc1ResultSet)result).getNext();
180                 if (result == null)
181                         throw new PSQLException("postgresql.stat.noresult");
182                 return result;
183         }
184
185         /*
186          * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition
187          * SQL statements that return nothing such as SQL DDL statements
188          * can be executed
189          *
190          * @param sql a SQL statement
191          * @return either a row count, or 0 for SQL commands
192          * @exception SQLException if a database access error occurs
193          */
194         public int executeUpdate(String p_sql) throws SQLException
195         {
196                 String l_sql = replaceProcessing(p_sql);
197                 m_sqlFragments = new String[] {l_sql};
198                 m_binds = new Object[0];
199                 //If we have already created a server prepared statement, we need
200                 //to deallocate the existing one
201                 if (m_statementName != null) {
202                         ((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName);
203                         m_statementName = null;
204                         m_origSqlFragments = null;
205                         m_executeSqlFragments = null;
206                 }
207                 return executeUpdate();
208         }
209
210         /*
211          * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition,
212          * SQL statements that return nothing such as SQL DDL statements can
213          * be executed.
214          *
215          * @return either the row count for INSERT, UPDATE or DELETE; or
216          *               *      0 for SQL statements that return nothing.
217          * @exception SQLException if a database access error occurs
218          */
219         public int executeUpdate() throws SQLException
220         {
221                 this.execute();
222                 if (((AbstractJdbc1ResultSet)result).reallyResultSet())
223                         throw new PSQLException("postgresql.stat.result");
224                 return this.getUpdateCount();
225         }
226
227         /*
228          * Execute a SQL statement that may return multiple results. We
229          * don't have to worry about this since we do not support multiple
230          * ResultSets.   You can use getResultSet or getUpdateCount to
231          * retrieve the result.
232          *
233          * @param sql any SQL statement
234          * @return true if the next result is a ResulSet, false if it is
235          *      an update count or there are no more results
236          * @exception SQLException if a database access error occurs
237          */
238         public boolean execute(String p_sql) throws SQLException
239         {
240                 String l_sql = replaceProcessing(p_sql);
241                 m_sqlFragments = new String[] {l_sql};
242                 m_binds = new Object[0];
243                 //If we have already created a server prepared statement, we need
244                 //to deallocate the existing one
245                 if (m_statementName != null) {
246                         ((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName);
247                         m_statementName = null;
248                         m_origSqlFragments = null;
249                         m_executeSqlFragments = null;
250                 }
251                 return execute();
252         }
253
254         /*
255          * Some prepared statements return multiple results; the execute method
256          * handles these complex statements as well as the simpler form of
257          * statements handled by executeQuery and executeUpdate
258          *
259          * @return true if the next result is a ResultSet; false if it is an
260          *               *      update count or there are no more results
261          * @exception SQLException if a database access error occurs
262          */
263         public boolean execute() throws SQLException
264         {
265                 if (isFunction && !returnTypeSet)
266                         throw new PSQLException("postgresql.call.noreturntype");
267                 if (isFunction)
268                 { // set entry 1 to dummy entry..
269                         m_binds[0] = ""; // dummy entry which ensured that no one overrode
270                         m_bindTypes[0] = PG_TEXT;
271                         // and calls to setXXX (2,..) really went to first arg in a function call..
272                 }
273
274                 // New in 7.1, if we have a previous resultset then force it to close
275                 // This brings us nearer to compliance, and helps memory management.
276                 // Internal stuff will call ExecSQL directly, bypassing this.
277
278                 if (result != null)
279                 {
280                         java.sql.ResultSet rs = getResultSet();
281                         if (rs != null)
282                                 rs.close();
283                 }
284
285                 //Use server prepared statements if directed
286                 if (m_useServerPrepare)
287                 {
288                         if (m_statementName == null)
289                         {
290                                 m_statementName = "JDBC_STATEMENT_" + m_preparedCount++;
291                                 m_origSqlFragments = new String[m_sqlFragments.length];
292                                 m_executeSqlFragments = new String[m_sqlFragments.length];
293                                 System.arraycopy(m_sqlFragments, 0, m_origSqlFragments, 0, m_sqlFragments.length);
294                                 m_executeSqlFragments[0] = "EXECUTE " + m_statementName;
295                                 if (m_sqlFragments.length > 1)
296                                 {
297                                         m_executeSqlFragments[0] = m_executeSqlFragments[0] + "(";
298                                         for (int i = 1; i < m_bindTypes.length; i++)
299                                         {
300                                                 m_executeSqlFragments[i] = ", ";
301                                         }
302                                         m_executeSqlFragments[m_bindTypes.length] = ")";
303                                 }
304                                 synchronized (sbuf)
305                                 {
306                                         sbuf.setLength(0);
307                                         sbuf.append("PREPARE ");
308                                         sbuf.append(m_statementName);
309                                         if (m_origSqlFragments.length > 1)
310                                         {
311                                                 sbuf.append("(");
312                                                 for (int i = 0; i < m_bindTypes.length - 1; i++)
313                                                 {
314                                                         sbuf.append(m_bindTypes[i]);
315                                                         sbuf.append(", ");
316                                                 }
317                                                 sbuf.append(m_bindTypes[m_bindTypes.length - 1]);
318                                                 sbuf.append(")");
319                                         }
320                                         sbuf.append(" AS ");
321                                         sbuf.append(m_origSqlFragments[0]);
322                                         for (int i = 1; i < m_origSqlFragments.length; i++)
323                                         {
324                                                 sbuf.append(" $");
325                                                 sbuf.append(i);
326                                                 sbuf.append(" ");
327                                                 sbuf.append(m_origSqlFragments[i]);
328                                         }
329                                         sbuf.append("; ");
330
331                                         sbuf.append(m_executeSqlFragments[0]);
332                                         m_sqlFragments[0] = sbuf.toString();
333                                         System.arraycopy(m_executeSqlFragments, 1, m_sqlFragments, 1, m_sqlFragments.length - 1);
334                                 }
335
336                         }
337                         else
338                         {
339                                 m_sqlFragments = m_executeSqlFragments;
340                         }
341                 }
342
343                 // New in 7.1, pass Statement so that ExecSQL can customise to it
344                 result = org.postgresql.core.QueryExecutor.execute(m_sqlFragments,
345                                                                            m_binds,
346                                                                            (java.sql.Statement)this);
347
348                 //If we are executing a callable statement function set the return data
349                 if (isFunction)
350                 {
351                         if (!((AbstractJdbc1ResultSet)result).reallyResultSet())
352                                 throw new PSQLException("postgresql.call.noreturnval");
353                         if (!result.next ())
354                                 throw new PSQLException ("postgresql.call.noreturnval");
355                         callResult = result.getObject(1);
356                         int columnType = result.getMetaData().getColumnType(1);
357                         if (columnType != functionReturnType)
358                                 throw new PSQLException ("postgresql.call.wrongrtntype",
359                                                                                  new Object[]{
360                                                                                          "java.sql.Types=" + columnType, "java.sql.Types=" + functionReturnType });
361                         result.close ();
362                         return true;
363                 }
364                 else
365                 {
366                         return (result != null && ((AbstractJdbc1ResultSet)result).reallyResultSet());
367                 }
368         }
369
370         /** version of execute which converts the query to a cursor.
371          */
372         public boolean executeWithCursor() throws SQLException
373         {
374                 if (isFunction && !returnTypeSet)
375                         throw new PSQLException("postgresql.call.noreturntype");
376                 if (isFunction)
377                 { // set entry 1 to dummy entry..
378                         m_binds[0] = ""; // dummy entry which ensured that no one overrode
379                         m_bindTypes[0] = PG_TEXT;
380                         // and calls to setXXX (2,..) really went to first arg in a function call..
381                 }
382
383                 // New in 7.1, if we have a previous resultset then force it to close
384                 // This brings us nearer to compliance, and helps memory management.
385                 // Internal stuff will call ExecSQL directly, bypassing this.
386                 if (result != null)
387                 {
388                         java.sql.ResultSet rs = getResultSet();
389                         if (rs != null)
390                                 rs.close();
391                 }
392
393                 // I've pretty much ignored server prepared statements... can declare and prepare be
394                 // used together?
395                 // It's trivial to change this: you just have to resolve this issue
396                 // of how to work out whether there's a function call. If there isn't then the first
397                 // element of the array must be the bit that you extend to become the cursor
398                 // decleration.
399                 // The last thing that can go wrong is when the user supplies a cursor statement
400                 // directly: the translation takes no account of that. I think we should just look
401                 // for declare and stop the translation if we find it.
402
403                 // The first thing to do is transform the statement text into the cursor form.
404                 String[] origSqlFragments = m_sqlFragments;
405                 m_sqlFragments = new String[origSqlFragments.length];
406                 System.arraycopy(origSqlFragments, 0, m_sqlFragments, 0, origSqlFragments.length);
407                 // Pinch the prepared count for our own nefarious purposes.
408                 m_statementName = "JDBC_CURS_" + m_preparedCount++;
409                 // The static bit to prepend to all querys.
410                 String cursDecl = "BEGIN; DECLARE " + m_statementName + " CURSOR FOR ";
411                 String endCurs = " FETCH FORWARD " + fetchSize + " FROM " + m_statementName + ";";
412
413                 // Add the real query to the curs decleration.
414                 // This is the bit that really makes the presumption about
415                 // m_sqlFragments not being a function call.
416                 if (m_sqlFragments.length < 1)
417                         m_sqlFragments[0] = cursDecl + "SELECT NULL;";
418                 
419                 else if (m_sqlFragments.length < 2)
420                 {
421                         if (m_sqlFragments[0].endsWith(";"))
422                                 m_sqlFragments[0] = cursDecl + m_sqlFragments[0] + endCurs;
423                         else
424                                 m_sqlFragments[0] = cursDecl + m_sqlFragments[0] + ";" + endCurs;
425                 }
426                 else
427                 {
428                         m_sqlFragments[0] = cursDecl + m_sqlFragments[0];
429                         if (m_sqlFragments[m_sqlFragments.length - 1].endsWith(";"))
430                                 m_sqlFragments[m_sqlFragments.length - 1] += endCurs;
431                         else
432                                 m_sqlFragments[m_sqlFragments.length - 1] += (";" + endCurs);
433                 }
434
435                 result = org.postgresql.core.QueryExecutor.execute(m_sqlFragments,
436                                                                            m_binds,
437                                                                            (java.sql.Statement)this);
438
439                 //If we are executing a callable statement function set the return data
440                 if (isFunction)
441                 {
442                         if (!((AbstractJdbc1ResultSet)result).reallyResultSet())
443                                 throw new PSQLException("postgresql.call.noreturnval");
444                         if (!result.next ())
445                                 throw new PSQLException ("postgresql.call.noreturnval");
446                         callResult = result.getObject(1);
447                         int columnType = result.getMetaData().getColumnType(1);
448                         if (columnType != functionReturnType)
449                         {
450                                 Object[] arr =
451                                         { "java.sql.Types=" + columnType,
452                                           "java.sql.Types=" + functionReturnType
453                                         };
454                                 throw new PSQLException ("postgresql.call.wrongrtntype",arr);
455                         }
456                         result.close ();
457                         return true;
458                 }
459                 else
460                 {
461                         return (result != null && ((AbstractJdbc1ResultSet)result).reallyResultSet());
462                 }
463         }
464
465         
466         /*
467          * setCursorName defines the SQL cursor name that will be used by
468          * subsequent execute methods.  This name can then be used in SQL
469          * positioned update/delete statements to identify the current row
470          * in the ResultSet generated by this statement.  If a database
471          * doesn't support positioned update/delete, this method is a
472          * no-op.
473          *
474          * <p><B>Note:</B> By definition, positioned update/delete execution
475          * must be done by a different Statement than the one which
476          * generated the ResultSet being used for positioning.  Also, cursor
477          * names must be unique within a Connection.
478          *
479          * <p>We throw an additional constriction.      There can only be one
480          * cursor active at any one time.
481          *
482          * @param name the new cursor name
483          * @exception SQLException if a database access error occurs
484          */
485         public void setCursorName(String name) throws SQLException
486         {
487                 ((AbstractJdbc1Connection)connection).setCursorName(name);
488         }
489
490
491         /*
492          * getUpdateCount returns the current result as an update count,
493          * if the result is a ResultSet or there are no more results, -1
494          * is returned.  It should only be called once per result.
495          *
496          * @return the current result as an update count.
497          * @exception SQLException if a database access error occurs
498          */
499         public int getUpdateCount() throws SQLException
500         {
501                 if (result == null)
502                         return -1;
503                 if (isFunction)
504                         return 1;
505                 if (((AbstractJdbc1ResultSet)result).reallyResultSet())
506                         return -1;
507                 return ((AbstractJdbc1ResultSet)result).getResultCount();
508         }
509
510         /*
511          * getMoreResults moves to a Statement's next result.  If it returns
512          * true, this result is a ResulSet.
513          *
514          * @return true if the next ResultSet is valid
515          * @exception SQLException if a database access error occurs
516          */
517         public boolean getMoreResults() throws SQLException
518         {
519                 result = ((AbstractJdbc1ResultSet)result).getNext();
520                 return (result != null && ((AbstractJdbc1ResultSet)result).reallyResultSet());
521         }
522
523
524
525         /*
526          * Returns the status message from the current Result.<p>
527          * This is used internally by the driver.
528          *
529          * @return status message from backend
530          */
531         public String getResultStatusString()
532         {
533                 if (result == null)
534                         return null;
535                 return ((AbstractJdbc1ResultSet)result).getStatusString();
536         }
537
538         /*
539          * The maxRows limit is set to limit the number of rows that
540          * any ResultSet can contain.  If the limit is exceeded, the
541          * excess rows are silently dropped.
542          *
543          * @return the current maximum row limit; zero means unlimited
544          * @exception SQLException if a database access error occurs
545          */
546         public int getMaxRows() throws SQLException
547         {
548                 return maxrows;
549         }
550
551         /*
552          * Set the maximum number of rows
553          *
554          * @param max the new max rows limit; zero means unlimited
555          * @exception SQLException if a database access error occurs
556          * @see getMaxRows
557          */
558         public void setMaxRows(int max) throws SQLException
559         {
560                 maxrows = max;
561         }
562
563         /*
564          * If escape scanning is on (the default), the driver will do escape
565          * substitution before sending the SQL to the database.
566          *
567          * @param enable true to enable; false to disable
568          * @exception SQLException if a database access error occurs
569          */
570         public void setEscapeProcessing(boolean enable) throws SQLException
571         {
572                 replaceProcessingEnabled = enable;
573         }
574
575         /*
576          * The queryTimeout limit is the number of seconds the driver
577          * will wait for a Statement to execute.  If the limit is
578          * exceeded, a SQLException is thrown.
579          *
580          * @return the current query timeout limit in seconds; 0 = unlimited
581          * @exception SQLException if a database access error occurs
582          */
583         public int getQueryTimeout() throws SQLException
584         {
585                 return timeout;
586         }
587
588         /*
589          * Sets the queryTimeout limit
590          *
591          * @param seconds - the new query timeout limit in seconds
592          * @exception SQLException if a database access error occurs
593          */
594         public void setQueryTimeout(int seconds) throws SQLException
595         {
596                 timeout = seconds;
597         }
598
599         /**
600          * This adds a warning to the warning chain.
601          * @param msg message to add
602          */
603         public void addWarning(String msg)
604         {
605                 if (warnings != null)
606                         warnings.setNextWarning(new SQLWarning(msg));
607                 else
608                         warnings = new SQLWarning(msg);
609         }
610
611         /*
612          * The first warning reported by calls on this Statement is
613          * returned.  A Statement's execute methods clear its SQLWarning
614          * chain.  Subsequent Statement warnings will be chained to this
615          * SQLWarning.
616          *
617          * <p>The Warning chain is automatically cleared each time a statement
618          * is (re)executed.
619          *
620          * <p><B>Note:</B>      If you are processing a ResultSet then any warnings
621          * associated with ResultSet reads will be chained on the ResultSet
622          * object.
623          *
624          * @return the first SQLWarning on null
625          * @exception SQLException if a database access error occurs
626          */
627         public SQLWarning getWarnings() throws SQLException
628         {
629                 return warnings;
630         }
631
632         /*
633          * The maxFieldSize limit (in bytes) is the maximum amount of
634          * data returned for any column value; it only applies to
635          * BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR
636          * columns.  If the limit is exceeded, the excess data is silently
637          * discarded.
638          *
639          * @return the current max column size limit; zero means unlimited
640          * @exception SQLException if a database access error occurs
641          */
642         public int getMaxFieldSize() throws SQLException
643         {
644                 return 8192;            // We cannot change this
645         }
646
647         /*
648          * Sets the maxFieldSize - NOT! - We throw an SQLException just
649          * to inform them to stop doing this.
650          *
651          * @param max the new max column size limit; zero means unlimited
652          * @exception SQLException if a database access error occurs
653          */
654         public void setMaxFieldSize(int max) throws SQLException
655         {
656                 throw new PSQLException("postgresql.stat.maxfieldsize");
657         }
658
659         /*
660          * After this call, getWarnings returns null until a new warning
661          * is reported for this Statement.
662          *
663          * @exception SQLException if a database access error occurs
664          */
665         public void clearWarnings() throws SQLException
666         {
667                 warnings = null;
668         }
669
670         /*
671          * Cancel can be used by one thread to cancel a statement that
672          * is being executed by another thread.
673          * <p>
674          * Not implemented, this method is a no-op.
675          *
676          * @exception SQLException only because thats the spec.
677          */
678         public void cancel() throws SQLException
679         {
680                 throw new PSQLException("postgresql.unimplemented");
681         }
682
683         /*
684          * getResultSet returns the current result as a ResultSet.      It
685          * should only be called once per result.
686          *
687          * @return the current result set; null if there are no more
688          * @exception SQLException if a database access error occurs (why?)
689          */
690         public java.sql.ResultSet getResultSet() throws SQLException
691         {
692                 if (result != null && ((AbstractJdbc1ResultSet) result).reallyResultSet())
693                         return result;
694                 return null;
695         }
696
697         /*
698          * In many cases, it is desirable to immediately release a
699          * Statement's database and JDBC resources instead of waiting
700          * for this to happen when it is automatically closed.  The
701          * close method provides this immediate release.
702          *
703          * <p><B>Note:</B> A Statement is automatically closed when it is
704          * garbage collected.  When a Statement is closed, its current
705          * ResultSet, if one exists, is also closed.
706          *
707          * @exception SQLException if a database access error occurs (why?)
708          */
709         public void close() throws SQLException
710         {
711                 // Force the ResultSet to close
712                 java.sql.ResultSet rs = getResultSet();
713                 if (rs != null)
714                         rs.close();
715
716                 // If using server prepared statements deallocate them
717                 if (m_useServerPrepare && m_statementName != null) {
718                         ((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName);
719                 }
720
721                 // Disasociate it from us (For Garbage Collection)
722                 result = null;
723         }
724
725         /*
726          * Filter the SQL string of Java SQL Escape clauses.
727          *
728          * Currently implemented Escape clauses are those mentioned in 11.3
729          * in the specification. Basically we look through the sql string for
730          * {d xxx}, {t xxx} or {ts xxx} in non-string sql code. When we find
731          * them, we just strip the escape part leaving only the xxx part.
732          * So, something like "select * from x where d={d '2001-10-09'}" would
733          * return "select * from x where d= '2001-10-09'".
734          */
735         protected String replaceProcessing(String p_sql)
736         {
737                 if (replaceProcessingEnabled)
738                 {
739                         // Since escape codes can only appear in SQL CODE, we keep track
740                         // of if we enter a string or not.
741                         StringBuffer newsql = new StringBuffer(p_sql.length());
742                         short state = IN_SQLCODE;
743
744                         int i = -1;
745                         int len = p_sql.length();
746                         while (++i < len)
747                         {
748                                 char c = p_sql.charAt(i);
749                                 switch (state)
750                                 {
751                                         case IN_SQLCODE:
752                                                 if (c == '\'')                            // start of a string?
753                                                         state = IN_STRING;
754                                                 else if (c == '{')                        // start of an escape code?
755                                                         if (i + 1 < len)
756                                                         {
757                                                                 char next = p_sql.charAt(i + 1);
758                                                                 if (next == 'd')
759                                                                 {
760                                                                         state = ESC_TIMEDATE;
761                                                                         i++;
762                                                                         break;
763                                                                 }
764                                                                 else if (next == 't')
765                                                                 {
766                                                                         state = ESC_TIMEDATE;
767                                                                         i += (i + 2 < len && p_sql.charAt(i + 2) == 's') ? 2 : 1;
768                                                                         break;
769                                                                 }
770                                                         }
771                                                 newsql.append(c);
772                                                 break;
773
774                                         case IN_STRING:
775                                                 if (c == '\'')                             // end of string?
776                                                         state = IN_SQLCODE;
777                                                 else if (c == '\\')                        // a backslash?
778                                                         state = BACKSLASH;
779
780                                                 newsql.append(c);
781                                                 break;
782
783                                         case BACKSLASH:
784                                                 state = IN_STRING;
785
786                                                 newsql.append(c);
787                                                 break;
788
789                                         case ESC_TIMEDATE:
790                                                 if (c == '}')
791                                                         state = IN_SQLCODE;               // end of escape code.
792                                                 else
793                                                         newsql.append(c);
794                                                 break;
795                                 } // end switch
796                         }
797
798                         return newsql.toString();
799                 }
800                 else
801                 {
802                         return p_sql;
803                 }
804         }
805
806         /*
807          *
808          * The following methods are postgres extensions and are defined
809          * in the interface org.postgresql.Statement
810          *
811          */
812
813         /*
814          * Returns the Last inserted/updated oid.  Deprecated in 7.2 because
815                         * range of OID values is greater than a java signed int.
816          * @deprecated Replaced by getLastOID in 7.2
817          */
818         public int getInsertedOID() throws SQLException
819         {
820                 if (result == null)
821                         return 0;
822                 return (int)((AbstractJdbc1ResultSet)result).getLastOID();
823         }
824
825         /*
826          * Returns the Last inserted/updated oid.
827          * @return OID of last insert
828                         * @since 7.2
829          */
830         public long getLastOID() throws SQLException
831         {
832                 if (result == null)
833                         return 0;
834                 return ((AbstractJdbc1ResultSet)result).getLastOID();
835         }
836
837         /*
838          * Set a parameter to SQL NULL
839          *
840          * <p><B>Note:</B> You must specify the parameters SQL type (although
841          * PostgreSQL ignores it)
842          *
843          * @param parameterIndex the first parameter is 1, etc...
844          * @param sqlType the SQL type code defined in java.sql.Types
845          * @exception SQLException if a database access error occurs
846          */
847         public void setNull(int parameterIndex, int sqlType) throws SQLException
848         {
849         String l_pgType;
850                 switch (sqlType)
851                 {
852                         case Types.INTEGER:
853                                 l_pgType = PG_INTEGER;
854                                 break;
855                         case Types.TINYINT:
856                         case Types.SMALLINT:
857                                 l_pgType = PG_INT2;
858                                 break;
859                         case Types.BIGINT:
860                                 l_pgType = PG_INT8;
861                                 break;
862                         case Types.REAL:
863                         case Types.FLOAT:
864                                 l_pgType = PG_FLOAT;
865                                 break;
866                         case Types.DOUBLE:
867                                 l_pgType = PG_DOUBLE;
868                                 break;
869                         case Types.DECIMAL:
870                         case Types.NUMERIC:
871                                 l_pgType = PG_NUMERIC;
872                                 break;
873                         case Types.CHAR:
874                         case Types.VARCHAR:
875                         case Types.LONGVARCHAR:
876                                 l_pgType = PG_TEXT;
877                                 break;
878                         case Types.DATE:
879                                 l_pgType = PG_DATE;
880                                 break;
881                         case Types.TIME:
882                                 l_pgType = PG_TIME;
883                                 break;
884                         case Types.TIMESTAMP:
885                                 l_pgType = PG_TIMESTAMPTZ;
886                                 break;
887                         case Types.BINARY:
888                         case Types.VARBINARY:
889                                 l_pgType = PG_BYTEA;
890                                 break;
891                         case Types.OTHER:
892                                 l_pgType = PG_TEXT;
893                                 break;
894                         default:
895                                 l_pgType = PG_TEXT;
896                 }
897                 bind(parameterIndex, "null", l_pgType);
898         }
899
900         /*
901          * Set a parameter to a Java boolean value.  The driver converts this
902          * to a SQL BIT value when it sends it to the database.
903          *
904          * @param parameterIndex the first parameter is 1...
905          * @param x the parameter value
906          * @exception SQLException if a database access error occurs
907          */
908         public void setBoolean(int parameterIndex, boolean x) throws SQLException
909         {
910                 bind(parameterIndex, x ? "'t'" : "'f'", PG_BOOLEAN);
911         }
912
913         /*
914          * Set a parameter to a Java byte value.  The driver converts this to
915          * a SQL TINYINT value when it sends it to the database.
916          *
917          * @param parameterIndex the first parameter is 1...
918          * @param x the parameter value
919          * @exception SQLException if a database access error occurs
920          */
921         public void setByte(int parameterIndex, byte x) throws SQLException
922         {
923                 bind(parameterIndex, Integer.toString(x), PG_TEXT);
924         }
925
926         /*
927          * Set a parameter to a Java short value.  The driver converts this
928          * to a SQL SMALLINT value when it sends it to the database.
929          *
930          * @param parameterIndex the first parameter is 1...
931          * @param x the parameter value
932          * @exception SQLException if a database access error occurs
933          */
934         public void setShort(int parameterIndex, short x) throws SQLException
935         {
936                 bind(parameterIndex, Integer.toString(x), PG_INT2);
937         }
938
939         /*
940          * Set a parameter to a Java int value.  The driver converts this to
941          * a SQL INTEGER value when it sends it to the database.
942          *
943          * @param parameterIndex the first parameter is 1...
944          * @param x the parameter value
945          * @exception SQLException if a database access error occurs
946          */
947         public void setInt(int parameterIndex, int x) throws SQLException
948         {
949                 bind(parameterIndex, Integer.toString(x), PG_INTEGER);
950         }
951
952         /*
953          * Set a parameter to a Java long value.  The driver converts this to
954          * a SQL BIGINT value when it sends it to the database.
955          *
956          * @param parameterIndex the first parameter is 1...
957          * @param x the parameter value
958          * @exception SQLException if a database access error occurs
959          */
960         public void setLong(int parameterIndex, long x) throws SQLException
961         {
962                 bind(parameterIndex, Long.toString(x), PG_INT8);
963         }
964
965         /*
966          * Set a parameter to a Java float value.  The driver converts this
967          * to a SQL FLOAT value when it sends it to the database.
968          *
969          * @param parameterIndex the first parameter is 1...
970          * @param x the parameter value
971          * @exception SQLException if a database access error occurs
972          */
973         public void setFloat(int parameterIndex, float x) throws SQLException
974         {
975                 bind(parameterIndex, Float.toString(x), PG_FLOAT);
976         }
977
978         /*
979          * Set a parameter to a Java double value.      The driver converts this
980          * to a SQL DOUBLE value when it sends it to the database
981          *
982          * @param parameterIndex the first parameter is 1...
983          * @param x the parameter value
984          * @exception SQLException if a database access error occurs
985          */
986         public void setDouble(int parameterIndex, double x) throws SQLException
987         {
988                 bind(parameterIndex, Double.toString(x), PG_DOUBLE);
989         }
990
991         /*
992          * Set a parameter to a java.lang.BigDecimal value.  The driver
993          * converts this to a SQL NUMERIC value when it sends it to the
994          * database.
995          *
996          * @param parameterIndex the first parameter is 1...
997          * @param x the parameter value
998          * @exception SQLException if a database access error occurs
999          */
1000         public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException
1001         {
1002                 if (x == null)
1003                         setNull(parameterIndex, Types.DECIMAL);
1004                 else
1005                 {
1006                         bind(parameterIndex, x.toString(), PG_NUMERIC);
1007                 }
1008         }
1009
1010         /*
1011          * Set a parameter to a Java String value.      The driver converts this
1012          * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments
1013          * size relative to the driver's limits on VARCHARs) when it sends it
1014          * to the database.
1015          *
1016          * @param parameterIndex the first parameter is 1...
1017          * @param x the parameter value
1018          * @exception SQLException if a database access error occurs
1019          */
1020         public void setString(int parameterIndex, String x) throws SQLException
1021         {
1022                 setString(parameterIndex, x, PG_TEXT);
1023         }
1024
1025         public void setString(int parameterIndex, String x, String type) throws SQLException
1026         {
1027                 // if the passed string is null, then set this column to null
1028                 if (x == null)
1029                         setNull(parameterIndex, Types.VARCHAR);
1030                 else
1031                 {
1032                         // use the shared buffer object. Should never clash but this makes
1033                         // us thread safe!
1034                         synchronized (sbuf)
1035                         {
1036                                 sbuf.setLength(0);
1037                                 sbuf.ensureCapacity(x.length());
1038                                 int i;
1039
1040                                 sbuf.append('\'');
1041                                 for (i = 0 ; i < x.length() ; ++i)
1042                                 {
1043                                         char c = x.charAt(i);
1044                                         if (c == '\\' || c == '\'')
1045                                                 sbuf.append((char)'\\');
1046                                         sbuf.append(c);
1047                                 }
1048                                 sbuf.append('\'');
1049                                 bind(parameterIndex, sbuf.toString(), type);
1050                         }
1051                 }
1052         }
1053
1054         /*
1055          * Set a parameter to a Java array of bytes.  The driver converts this
1056          * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's
1057          * size relative to the driver's limits on VARBINARYs) when it sends
1058          * it to the database.
1059          *
1060          * <p>Implementation note:
1061          * <br>With org.postgresql, this creates a large object, and stores the
1062          * objects oid in this column.
1063          *
1064          * @param parameterIndex the first parameter is 1...
1065          * @param x the parameter value
1066          * @exception SQLException if a database access error occurs
1067          */
1068         public void setBytes(int parameterIndex, byte x[]) throws SQLException
1069         {
1070                 if (connection.haveMinimumCompatibleVersion("7.2"))
1071                 {
1072                         //Version 7.2 supports the bytea datatype for byte arrays
1073                         if (null == x)
1074                         {
1075                                 setNull(parameterIndex, Types.VARBINARY);
1076                         }
1077                         else
1078                         {
1079                                 setString(parameterIndex, PGbytea.toPGString(x), PG_TEXT);
1080                         }
1081                 }
1082                 else
1083                 {
1084                         //Version 7.1 and earlier support done as LargeObjects
1085                         LargeObjectManager lom = connection.getLargeObjectAPI();
1086                         int oid = lom.create();
1087                         LargeObject lob = lom.open(oid);
1088                         lob.write(x);
1089                         lob.close();
1090                         setInt(parameterIndex, oid);
1091                 }
1092         }
1093
1094         /*
1095          * Set a parameter to a java.sql.Date value.  The driver converts this
1096          * to a SQL DATE value when it sends it to the database.
1097          *
1098          * @param parameterIndex the first parameter is 1...
1099          * @param x the parameter value
1100          * @exception SQLException if a database access error occurs
1101          */
1102         public void setDate(int parameterIndex, java.sql.Date x) throws SQLException
1103         {
1104                 if (null == x)
1105                 {
1106                         setNull(parameterIndex, Types.DATE);
1107                 }
1108                 else
1109                 {
1110                         bind(parameterIndex, "'" + x.toString() + "'", PG_DATE);
1111                 }
1112         }
1113
1114         /*
1115          * Set a parameter to a java.sql.Time value.  The driver converts
1116          * this to a SQL TIME value when it sends it to the database.
1117          *
1118          * @param parameterIndex the first parameter is 1...));
1119          * @param x the parameter value
1120          * @exception SQLException if a database access error occurs
1121          */
1122         public void setTime(int parameterIndex, Time x) throws SQLException
1123         {
1124                 if (null == x)
1125                 {
1126                         setNull(parameterIndex, Types.TIME);
1127                 }
1128                 else
1129                 {
1130                         bind(parameterIndex, "'" + x.toString() + "'", PG_TIME);
1131                 }
1132         }
1133
1134         /*
1135          * Set a parameter to a java.sql.Timestamp value.  The driver converts
1136          * this to a SQL TIMESTAMP value when it sends it to the database.
1137          *
1138          * @param parameterIndex the first parameter is 1...
1139          * @param x the parameter value
1140          * @exception SQLException if a database access error occurs
1141          */
1142         public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException
1143         {
1144                 if (null == x)
1145                 {
1146                         setNull(parameterIndex, Types.TIMESTAMP);
1147                 }
1148                 else
1149                 {
1150                         // Use the shared StringBuffer
1151                         synchronized (sbuf)
1152                         {
1153                                 sbuf.setLength(0);
1154                                 sbuf.ensureCapacity(32);
1155                                 sbuf.append("'");
1156                                 //format the timestamp
1157                                 //we do our own formating so that we can get a format
1158                                 //that works with both timestamp with time zone and
1159                                 //timestamp without time zone datatypes.
1160                                 //The format is '2002-01-01 23:59:59.123456-0130'
1161                                 //we need to include the local time and timezone offset
1162                                 //so that timestamp without time zone works correctly
1163                                 int l_year = x.getYear() + 1900;
1164                                 sbuf.append(l_year);
1165                                 sbuf.append('-');
1166                                 int l_month = x.getMonth() + 1;
1167                                 if (l_month < 10)
1168                                         sbuf.append('0');
1169                                 sbuf.append(l_month);
1170                                 sbuf.append('-');
1171                                 int l_day = x.getDate();
1172                                 if (l_day < 10)
1173                                         sbuf.append('0');
1174                                 sbuf.append(l_day);
1175                                 sbuf.append(' ');
1176                                 int l_hours = x.getHours();
1177                                 if (l_hours < 10)
1178                                         sbuf.append('0');
1179                                 sbuf.append(l_hours);
1180                                 sbuf.append(':');
1181                                 int l_minutes = x.getMinutes();
1182                                 if (l_minutes < 10)
1183                                         sbuf.append('0');
1184                                 sbuf.append(l_minutes);
1185                                 sbuf.append(':');
1186                                 int l_seconds = x.getSeconds();
1187                                 if (l_seconds < 10)
1188                                         sbuf.append('0');
1189                                 sbuf.append(l_seconds);
1190                                 // Make decimal from nanos.
1191                                 char[] l_decimal = {'0', '0', '0', '0', '0', '0', '0', '0', '0'};
1192                                 char[] l_nanos = Integer.toString(x.getNanos()).toCharArray();
1193                                 System.arraycopy(l_nanos, 0, l_decimal, l_decimal.length - l_nanos.length, l_nanos.length);
1194                                 sbuf.append('.');
1195                                 if (connection.haveMinimumServerVersion("7.2"))
1196                                 {
1197                                         sbuf.append(l_decimal, 0, 6);
1198                                 }
1199                                 else
1200                                 {
1201                                         // Because 7.1 include bug that "hh:mm:59.999" becomes "hh:mm:60.00".
1202                                         sbuf.append(l_decimal, 0, 2);
1203                                 }
1204                                 //add timezone offset
1205                                 int l_offset = -(x.getTimezoneOffset());
1206                                 int l_houros = l_offset / 60;
1207                                 if (l_houros >= 0)
1208                                 {
1209                                         sbuf.append('+');
1210                                 }
1211                                 else
1212                                 {
1213                                         sbuf.append('-');
1214                                 }
1215                                 if (l_houros > -10 && l_houros < 10)
1216                                         sbuf.append('0');
1217                                 if (l_houros >= 0)
1218                                 {
1219                                         sbuf.append(l_houros);
1220                                 }
1221                                 else
1222                                 {
1223                                         sbuf.append( -l_houros);
1224                                 }
1225                                 int l_minos = l_offset - (l_houros * 60);
1226                                 if (l_minos != 0)
1227                                 {
1228                                         if (l_minos < 10)
1229                                                 sbuf.append('0');
1230                                         sbuf.append(l_minos);
1231                                 }
1232                                 sbuf.append("'");
1233                                 bind(parameterIndex, sbuf.toString(), PG_TIMESTAMPTZ);
1234                         }
1235
1236                 }
1237         }
1238
1239         /*
1240          * When a very large ASCII value is input to a LONGVARCHAR parameter,
1241          * it may be more practical to send it via a java.io.InputStream.
1242          * JDBC will read the data from the stream as needed, until it reaches
1243          * end-of-file.  The JDBC driver will do any necessary conversion from
1244          * ASCII to the database char format.
1245          *
1246          * <P><B>Note:</B> This stream object can either be a standard Java
1247          * stream object or your own subclass that implements the standard
1248          * interface.
1249          *
1250          * @param parameterIndex the first parameter is 1...
1251          * @param x the parameter value
1252          * @param length the number of bytes in the stream
1253          * @exception SQLException if a database access error occurs
1254          */
1255         public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException
1256         {
1257                 if (connection.haveMinimumCompatibleVersion("7.2"))
1258                 {
1259                         //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text)
1260                         //As the spec/javadoc for this method indicate this is to be used for
1261                         //large String values (i.e. LONGVARCHAR)  PG doesn't have a separate
1262                         //long varchar datatype, but with toast all text datatypes are capable of
1263                         //handling very large values.  Thus the implementation ends up calling
1264                         //setString() since there is no current way to stream the value to the server
1265                         try
1266                         {
1267                                 InputStreamReader l_inStream = new InputStreamReader(x, "ASCII");
1268                                 char[] l_chars = new char[length];
1269                                 int l_charsRead = l_inStream.read(l_chars, 0, length);
1270                                 setString(parameterIndex, new String(l_chars, 0, l_charsRead), PG_TEXT);
1271                         }
1272                         catch (UnsupportedEncodingException l_uee)
1273                         {
1274                                 throw new PSQLException("postgresql.unusual", l_uee);
1275                         }
1276                         catch (IOException l_ioe)
1277                         {
1278                                 throw new PSQLException("postgresql.unusual", l_ioe);
1279                         }
1280                 }
1281                 else
1282                 {
1283                         //Version 7.1 supported only LargeObjects by treating everything
1284                         //as binary data
1285                         setBinaryStream(parameterIndex, x, length);
1286                 }
1287         }
1288
1289         /*
1290          * When a very large Unicode value is input to a LONGVARCHAR parameter,
1291          * it may be more practical to send it via a java.io.InputStream.
1292          * JDBC will read the data from the stream as needed, until it reaches
1293          * end-of-file.  The JDBC driver will do any necessary conversion from
1294          * UNICODE to the database char format.
1295          *
1296          * <P><B>Note:</B> This stream object can either be a standard Java
1297          * stream object or your own subclass that implements the standard
1298          * interface.
1299          *
1300          * @param parameterIndex the first parameter is 1...
1301          * @param x the parameter value
1302          * @exception SQLException if a database access error occurs
1303          */
1304         public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException
1305         {
1306                 if (connection.haveMinimumCompatibleVersion("7.2"))
1307                 {
1308                         //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text)
1309                         //As the spec/javadoc for this method indicate this is to be used for
1310                         //large String values (i.e. LONGVARCHAR)  PG doesn't have a separate
1311                         //long varchar datatype, but with toast all text datatypes are capable of
1312                         //handling very large values.  Thus the implementation ends up calling
1313                         //setString() since there is no current way to stream the value to the server
1314                         try
1315                         {
1316                                 InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8");
1317                                 char[] l_chars = new char[length];
1318                                 int l_charsRead = l_inStream.read(l_chars, 0, length);
1319                                 setString(parameterIndex, new String(l_chars, 0, l_charsRead), PG_TEXT);
1320                         }
1321                         catch (UnsupportedEncodingException l_uee)
1322                         {
1323                                 throw new PSQLException("postgresql.unusual", l_uee);
1324                         }
1325                         catch (IOException l_ioe)
1326                         {
1327                                 throw new PSQLException("postgresql.unusual", l_ioe);
1328                         }
1329                 }
1330                 else
1331                 {
1332                         //Version 7.1 supported only LargeObjects by treating everything
1333                         //as binary data
1334                         setBinaryStream(parameterIndex, x, length);
1335                 }
1336         }
1337
1338         /*
1339          * When a very large binary value is input to a LONGVARBINARY parameter,
1340          * it may be more practical to send it via a java.io.InputStream.
1341          * JDBC will read the data from the stream as needed, until it reaches
1342          * end-of-file.
1343          *
1344          * <P><B>Note:</B> This stream object can either be a standard Java
1345          * stream object or your own subclass that implements the standard
1346          * interface.
1347          *
1348          * @param parameterIndex the first parameter is 1...
1349          * @param x the parameter value
1350          * @exception SQLException if a database access error occurs
1351          */
1352         public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
1353         {
1354                 if (connection.haveMinimumCompatibleVersion("7.2"))
1355                 {
1356                         //Version 7.2 supports BinaryStream for for the PG bytea type
1357                         //As the spec/javadoc for this method indicate this is to be used for
1358                         //large binary values (i.e. LONGVARBINARY)      PG doesn't have a separate
1359                         //long binary datatype, but with toast the bytea datatype is capable of
1360                         //handling very large values.  Thus the implementation ends up calling
1361                         //setBytes() since there is no current way to stream the value to the server
1362                         byte[] l_bytes = new byte[length];
1363                         int l_bytesRead;
1364                         try
1365                         {
1366                                 l_bytesRead = x.read(l_bytes, 0, length);
1367                         }
1368                         catch (IOException l_ioe)
1369                         {
1370                                 throw new PSQLException("postgresql.unusual", l_ioe);
1371                         }
1372                         if (l_bytesRead == length)
1373                         {
1374                                 setBytes(parameterIndex, l_bytes);
1375                         }
1376                         else
1377                         {
1378                                 //the stream contained less data than they said
1379                                 byte[] l_bytes2 = new byte[l_bytesRead];
1380                                 System.arraycopy(l_bytes, 0, l_bytes2, 0, l_bytesRead);
1381                                 setBytes(parameterIndex, l_bytes2);
1382                         }
1383                 }
1384                 else
1385                 {
1386                         //Version 7.1 only supported streams for LargeObjects
1387                         //but the jdbc spec indicates that streams should be
1388                         //available for LONGVARBINARY instead
1389                         LargeObjectManager lom = connection.getLargeObjectAPI();
1390                         int oid = lom.create();
1391                         LargeObject lob = lom.open(oid);
1392                         OutputStream los = lob.getOutputStream();
1393                         try
1394                         {
1395                                 // could be buffered, but then the OutputStream returned by LargeObject
1396                                 // is buffered internally anyhow, so there would be no performance
1397                                 // boost gained, if anything it would be worse!
1398                                 int c = x.read();
1399                                 int p = 0;
1400                                 while (c > -1 && p < length)
1401                                 {
1402                                         los.write(c);
1403                                         c = x.read();
1404                                         p++;
1405                                 }
1406                                 los.close();
1407                         }
1408                         catch (IOException se)
1409                         {
1410                                 throw new PSQLException("postgresql.unusual", se);
1411                         }
1412                         // lob is closed by the stream so don't call lob.close()
1413                         setInt(parameterIndex, oid);
1414                 }
1415         }
1416
1417
1418         /*
1419          * In general, parameter values remain in force for repeated used of a
1420          * Statement.  Setting a parameter value automatically clears its
1421          * previous value.      However, in coms cases, it is useful to immediately
1422          * release the resources used by the current parameter values; this
1423          * can be done by calling clearParameters
1424          *
1425          * @exception SQLException if a database access error occurs
1426          */
1427         public void clearParameters() throws SQLException
1428         {
1429                 int i;
1430
1431                 for (i = 0 ; i < m_binds.length ; i++)
1432                 {
1433                         m_binds[i] = null;
1434                         m_bindTypes[i] = null;
1435                 }
1436         }
1437
1438         /*
1439          * Set the value of a parameter using an object; use the java.lang
1440          * equivalent objects for integral values.
1441          *
1442          * <P>The given Java object will be converted to the targetSqlType before
1443          * being sent to the database.
1444          *
1445          * <P>note that this method may be used to pass database-specific
1446          * abstract data types.  This is done by using a Driver-specific
1447          * Java type and using a targetSqlType of java.sql.Types.OTHER
1448          *
1449          * @param parameterIndex the first parameter is 1...
1450          * @param x the object containing the input parameter value
1451          * @param targetSqlType The SQL type to be send to the database
1452          * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC
1453          *               *      types this is the number of digits after the decimal.  For
1454          *               *      all other types this value will be ignored.
1455          * @exception SQLException if a database access error occurs
1456          */
1457         public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException
1458         {
1459                 if (x == null)
1460                 {
1461                         setNull(parameterIndex, targetSqlType);
1462                         return ;
1463                 }
1464                 switch (targetSqlType)
1465                 {
1466                         case Types.INTEGER:
1467                                 bind(parameterIndex, x.toString(), PG_INTEGER);
1468                                 break;
1469                         case Types.TINYINT:
1470                         case Types.SMALLINT:
1471                         case Types.BIGINT:
1472                         case Types.REAL:
1473                         case Types.FLOAT:
1474                         case Types.DOUBLE:
1475                         case Types.DECIMAL:
1476                         case Types.NUMERIC:
1477                                 if (x instanceof Boolean)
1478                                         bind(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0", PG_BOOLEAN);
1479                                 else
1480                                         bind(parameterIndex, x.toString(), PG_NUMERIC);
1481                                 break;
1482                         case Types.CHAR:
1483                         case Types.VARCHAR:
1484                         case Types.LONGVARCHAR:
1485                                 setString(parameterIndex, x.toString());
1486                                 break;
1487                         case Types.DATE:
1488                                 setDate(parameterIndex, (java.sql.Date)x);
1489                                 break;
1490                         case Types.TIME:
1491                                 setTime(parameterIndex, (Time)x);
1492                                 break;
1493                         case Types.TIMESTAMP:
1494                                 setTimestamp(parameterIndex, (Timestamp)x);
1495                                 break;
1496                         case Types.BIT:
1497                                 if (x instanceof Boolean)
1498                                 {
1499                                         bind(parameterIndex, ((Boolean)x).booleanValue() ? "TRUE" : "FALSE", PG_TEXT);
1500                                 }
1501                                 else
1502                                 {
1503                                         throw new PSQLException("postgresql.prep.type");
1504                                 }
1505                                 break;
1506                         case Types.BINARY:
1507                         case Types.VARBINARY:
1508                                 setObject(parameterIndex, x);
1509                                 break;
1510                         case Types.OTHER:
1511                                 setString(parameterIndex, ((PGobject)x).getValue(), PG_TEXT);
1512                                 break;
1513                         default:
1514                                 throw new PSQLException("postgresql.prep.type");
1515                 }
1516         }
1517
1518         public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException
1519         {
1520                 setObject(parameterIndex, x, targetSqlType, 0);
1521         }
1522
1523         /*
1524          * This stores an Object into a parameter.
1525          * <p>New for 6.4, if the object is not recognised, but it is
1526          * Serializable, then the object is serialised using the
1527          * org.postgresql.util.Serialize class.
1528          */
1529         public void setObject(int parameterIndex, Object x) throws SQLException
1530         {
1531                 if (x == null)
1532                 {
1533                         int l_sqlType;
1534                         if (x instanceof String)
1535                                 l_sqlType = Types.VARCHAR;
1536                         else if (x instanceof BigDecimal)
1537                                 l_sqlType = Types.DECIMAL;
1538                         else if (x instanceof Short)
1539                                 l_sqlType = Types.SMALLINT;
1540                         else if (x instanceof Integer)
1541                                 l_sqlType = Types.INTEGER;
1542                         else if (x instanceof Long)
1543                                 l_sqlType = Types.BIGINT;
1544                         else if (x instanceof Float)
1545                                 l_sqlType = Types.FLOAT;
1546                         else if (x instanceof Double)
1547                                 l_sqlType = Types.DOUBLE;
1548                         else if (x instanceof byte[])
1549                                 l_sqlType = Types.BINARY;
1550                         else if (x instanceof java.sql.Date)
1551                                 l_sqlType = Types.DATE;
1552                         else if (x instanceof Time)
1553                                 l_sqlType = Types.TIME;
1554                         else if (x instanceof Timestamp)
1555                                 l_sqlType = Types.TIMESTAMP;
1556                         else if (x instanceof Boolean)
1557                                 l_sqlType = Types.OTHER;
1558                         else 
1559                                 l_sqlType = Types.OTHER;
1560
1561                         setNull(parameterIndex, l_sqlType);
1562                         return ;
1563                 }
1564                 if (x instanceof String)
1565                         setString(parameterIndex, (String)x);
1566                 else if (x instanceof BigDecimal)
1567                         setBigDecimal(parameterIndex, (BigDecimal)x);
1568                 else if (x instanceof Short)
1569                         setShort(parameterIndex, ((Short)x).shortValue());
1570                 else if (x instanceof Integer)
1571                         setInt(parameterIndex, ((Integer)x).intValue());
1572                 else if (x instanceof Long)
1573                         setLong(parameterIndex, ((Long)x).longValue());
1574                 else if (x instanceof Float)
1575                         setFloat(parameterIndex, ((Float)x).floatValue());
1576                 else if (x instanceof Double)
1577                         setDouble(parameterIndex, ((Double)x).doubleValue());
1578                 else if (x instanceof byte[])
1579                         setBytes(parameterIndex, (byte[])x);
1580                 else if (x instanceof java.sql.Date)
1581                         setDate(parameterIndex, (java.sql.Date)x);
1582                 else if (x instanceof Time)
1583                         setTime(parameterIndex, (Time)x);
1584                 else if (x instanceof Timestamp)
1585                         setTimestamp(parameterIndex, (Timestamp)x);
1586                 else if (x instanceof Boolean)
1587                         setBoolean(parameterIndex, ((Boolean)x).booleanValue());
1588                 else if (x instanceof PGobject)
1589                         setString(parameterIndex, ((PGobject)x).getValue(), PG_TEXT);
1590                 else
1591                         // Try to store java object in database
1592                         setSerialize(parameterIndex, connection.storeObject(x), x.getClass().getName() );
1593         }
1594
1595         /*
1596          * Before executing a stored procedure call you must explicitly
1597          * call registerOutParameter to register the java.sql.Type of each
1598          * out parameter.
1599          *
1600          * <p>Note: When reading the value of an out parameter, you must use
1601          * the getXXX method whose Java type XXX corresponds to the
1602          * parameter's registered SQL type.
1603          *
1604          * ONLY 1 RETURN PARAMETER if {?= call ..} syntax is used
1605          *
1606          * @param parameterIndex the first parameter is 1, the second is 2,...
1607          * @param sqlType SQL type code defined by java.sql.Types; for
1608          * parameters of type Numeric or Decimal use the version of
1609          * registerOutParameter that accepts a scale value
1610          * @exception SQLException if a database-access error occurs.
1611          */
1612         public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException
1613         {
1614                 if (parameterIndex != 1)
1615                         throw new PSQLException ("postgresql.call.noinout");
1616                 if (!isFunction)
1617                         throw new PSQLException ("postgresql.call.procasfunc", originalSql);
1618
1619                 // functionReturnType contains the user supplied value to check
1620                 // testReturn contains a modified version to make it easier to
1621                 // check the getXXX methods..
1622                 functionReturnType = sqlType;
1623                 testReturn = sqlType;
1624                 if (functionReturnType == Types.CHAR ||
1625                                 functionReturnType == Types.LONGVARCHAR)
1626                         testReturn = Types.VARCHAR;
1627                 else if (functionReturnType == Types.FLOAT)
1628                         testReturn = Types.REAL; // changes to streamline later error checking
1629                 returnTypeSet = true;
1630         }
1631
1632         /*
1633          * You must also specify the scale for numeric/decimal types:
1634          *
1635          * <p>Note: When reading the value of an out parameter, you must use
1636          * the getXXX method whose Java type XXX corresponds to the
1637          * parameter's registered SQL type.
1638          *
1639          * @param parameterIndex the first parameter is 1, the second is 2,...
1640          * @param sqlType use either java.sql.Type.NUMERIC or java.sql.Type.DECIMAL
1641          * @param scale a value greater than or equal to zero representing the
1642          * desired number of digits to the right of the decimal point
1643          * @exception SQLException if a database-access error occurs.
1644          */
1645         public void registerOutParameter(int parameterIndex, int sqlType,
1646                                                                          int scale) throws SQLException
1647         {
1648                 registerOutParameter (parameterIndex, sqlType); // ignore for now..
1649         }
1650
1651         /*
1652          * An OUT parameter may have the value of SQL NULL; wasNull
1653          * reports whether the last value read has this special value.
1654          *
1655          * <p>Note: You must first call getXXX on a parameter to read its
1656          * value and then call wasNull() to see if the value was SQL NULL.
1657          * @return true if the last parameter read was SQL NULL
1658          * @exception SQLException if a database-access error occurs.
1659          */
1660         public boolean wasNull() throws SQLException
1661         {
1662                 // check to see if the last access threw an exception
1663                 return (callResult == null);
1664         }
1665
1666         /*
1667          * Get the value of a CHAR, VARCHAR, or LONGVARCHAR parameter as a
1668          * Java String.
1669          *
1670          * @param parameterIndex the first parameter is 1, the second is 2,...
1671          * @return the parameter value; if the value is SQL NULL, the result is null
1672          * @exception SQLException if a database-access error occurs.
1673          */
1674         public String getString(int parameterIndex) throws SQLException
1675         {
1676                 checkIndex (parameterIndex, Types.VARCHAR, "String");
1677                 return (String)callResult;
1678         }
1679
1680
1681         /*
1682          * Get the value of a BIT parameter as a Java boolean.
1683          *
1684          * @param parameterIndex the first parameter is 1, the second is 2,...
1685          * @return the parameter value; if the value is SQL NULL, the result is false
1686          * @exception SQLException if a database-access error occurs.
1687          */
1688         public boolean getBoolean(int parameterIndex) throws SQLException
1689         {
1690                 checkIndex (parameterIndex, Types.BIT, "Boolean");
1691                 if (callResult == null)
1692                         return false;
1693                 return ((Boolean)callResult).booleanValue ();
1694         }
1695
1696         /*
1697          * Get the value of a TINYINT parameter as a Java byte.
1698          *
1699          * @param parameterIndex the first parameter is 1, the second is 2,...
1700          * @return the parameter value; if the value is SQL NULL, the result is 0
1701          * @exception SQLException if a database-access error occurs.
1702          */
1703         public byte getByte(int parameterIndex) throws SQLException
1704         {
1705                 checkIndex (parameterIndex, Types.TINYINT, "Byte");
1706                 if (callResult == null)
1707                         return 0;
1708                 return (byte)((Integer)callResult).intValue ();
1709         }
1710
1711         /*
1712          * Get the value of a SMALLINT parameter as a Java short.
1713          *
1714          * @param parameterIndex the first parameter is 1, the second is 2,...
1715          * @return the parameter value; if the value is SQL NULL, the result is 0
1716          * @exception SQLException if a database-access error occurs.
1717          */
1718         public short getShort(int parameterIndex) throws SQLException
1719         {
1720                 checkIndex (parameterIndex, Types.SMALLINT, "Short");
1721                 if (callResult == null)
1722                         return 0;
1723                 return (short)((Integer)callResult).intValue ();
1724         }
1725
1726
1727         /*
1728          * Get the value of an INTEGER parameter as a Java int.
1729          *
1730          * @param parameterIndex the first parameter is 1, the second is 2,...
1731          * @return the parameter value; if the value is SQL NULL, the result is 0
1732          * @exception SQLException if a database-access error occurs.
1733          */
1734         public int getInt(int parameterIndex) throws SQLException
1735         {
1736                 checkIndex (parameterIndex, Types.INTEGER, "Int");
1737                 if (callResult == null)
1738                         return 0;
1739                 return ((Integer)callResult).intValue ();
1740         }
1741
1742         /*
1743          * Get the value of a BIGINT parameter as a Java long.
1744          *
1745          * @param parameterIndex the first parameter is 1, the second is 2,...
1746          * @return the parameter value; if the value is SQL NULL, the result is 0
1747          * @exception SQLException if a database-access error occurs.
1748          */
1749         public long getLong(int parameterIndex) throws SQLException
1750         {
1751                 checkIndex (parameterIndex, Types.BIGINT, "Long");
1752                 if (callResult == null)
1753                         return 0;
1754                 return ((Long)callResult).longValue ();
1755         }
1756
1757         /*
1758          * Get the value of a FLOAT parameter as a Java float.
1759          *
1760          * @param parameterIndex the first parameter is 1, the second is 2,...
1761          * @return the parameter value; if the value is SQL NULL, the result is 0
1762          * @exception SQLException if a database-access error occurs.
1763          */
1764         public float getFloat(int parameterIndex) throws SQLException
1765         {
1766                 checkIndex (parameterIndex, Types.REAL, "Float");
1767                 if (callResult == null)
1768                         return 0;
1769                 return ((Float)callResult).floatValue ();
1770         }
1771
1772         /*
1773          * Get the value of a DOUBLE parameter as a Java double.
1774          *
1775          * @param parameterIndex the first parameter is 1, the second is 2,...
1776          * @return the parameter value; if the value is SQL NULL, the result is 0
1777          * @exception SQLException if a database-access error occurs.
1778          */
1779         public double getDouble(int parameterIndex) throws SQLException
1780         {
1781                 checkIndex (parameterIndex, Types.DOUBLE, "Double");
1782                 if (callResult == null)
1783                         return 0;
1784                 return ((Double)callResult).doubleValue ();
1785         }
1786
1787         /*
1788          * Get the value of a NUMERIC parameter as a java.math.BigDecimal
1789          * object.
1790          *
1791          * @param parameterIndex the first parameter is 1, the second is 2,...
1792          * @param scale a value greater than or equal to zero representing the
1793          * desired number of digits to the right of the decimal point
1794          * @return the parameter value; if the value is SQL NULL, the result is null
1795          * @exception SQLException if a database-access error occurs.
1796          * @deprecated in Java2.0
1797          */
1798         public BigDecimal getBigDecimal(int parameterIndex, int scale)
1799         throws SQLException
1800         {
1801                 checkIndex (parameterIndex, Types.NUMERIC, "BigDecimal");
1802                 return ((BigDecimal)callResult);
1803         }
1804
1805         /*
1806          * Get the value of a SQL BINARY or VARBINARY parameter as a Java
1807          * byte[]
1808          *
1809          * @param parameterIndex the first parameter is 1, the second is 2,...
1810          * @return the parameter value; if the value is SQL NULL, the result is null
1811          * @exception SQLException if a database-access error occurs.
1812          */
1813         public byte[] getBytes(int parameterIndex) throws SQLException
1814         {
1815                 checkIndex (parameterIndex, Types.VARBINARY, Types.BINARY, "Bytes");
1816                 return ((byte [])callResult);
1817         }
1818
1819
1820         /*
1821          * Get the value of a SQL DATE parameter as a java.sql.Date object
1822          *
1823          * @param parameterIndex the first parameter is 1, the second is 2,...
1824          * @return the parameter value; if the value is SQL NULL, the result is null
1825          * @exception SQLException if a database-access error occurs.
1826          */
1827         public java.sql.Date getDate(int parameterIndex) throws SQLException
1828         {
1829                 checkIndex (parameterIndex, Types.DATE, "Date");
1830                 return (java.sql.Date)callResult;
1831         }
1832
1833         /*
1834          * Get the value of a SQL TIME parameter as a java.sql.Time object.
1835          *
1836          * @param parameterIndex the first parameter is 1, the second is 2,...
1837          * @return the parameter value; if the value is SQL NULL, the result is null
1838          * @exception SQLException if a database-access error occurs.
1839          */
1840         public java.sql.Time getTime(int parameterIndex) throws SQLException
1841         {
1842                 checkIndex (parameterIndex, Types.TIME, "Time");
1843                 return (java.sql.Time)callResult;
1844         }
1845
1846         /*
1847          * Get the value of a SQL TIMESTAMP parameter as a java.sql.Timestamp object.
1848          *
1849          * @param parameterIndex the first parameter is 1, the second is 2,...
1850          * @return the parameter value; if the value is SQL NULL, the result is null
1851          * @exception SQLException if a database-access error occurs.
1852          */
1853         public java.sql.Timestamp getTimestamp(int parameterIndex)
1854         throws SQLException
1855         {
1856                 checkIndex (parameterIndex, Types.TIMESTAMP, "Timestamp");
1857                 return (java.sql.Timestamp)callResult;
1858         }
1859
1860         // getObject returns a Java object for the parameter.
1861         // See the JDBC spec's "Dynamic Programming" chapter for details.
1862         /*
1863          * Get the value of a parameter as a Java object.
1864          *
1865          * <p>This method returns a Java object whose type coresponds to the
1866          * SQL type that was registered for this parameter using
1867          * registerOutParameter.
1868          *
1869          * <P>Note that this method may be used to read datatabase-specific,
1870          * abstract data types. This is done by specifying a targetSqlType
1871          * of java.sql.types.OTHER, which allows the driver to return a
1872          * database-specific Java type.
1873          *
1874          * <p>See the JDBC spec's "Dynamic Programming" chapter for details.
1875          *
1876          * @param parameterIndex the first parameter is 1, the second is 2,...
1877          * @return A java.lang.Object holding the OUT parameter value.
1878          * @exception SQLException if a database-access error occurs.
1879          */
1880         public Object getObject(int parameterIndex)
1881         throws SQLException
1882         {
1883                 checkIndex (parameterIndex);
1884                 return callResult;
1885         }
1886
1887         /*
1888          * Returns the SQL statement with the current template values
1889          * substituted.
1890          */
1891         public String toString()
1892         {
1893                 if (m_sqlFragments == null)
1894                         return "";
1895
1896                 synchronized (sbuf)
1897                 {
1898                         sbuf.setLength(0);
1899                         int i;
1900
1901                         for (i = 0 ; i < m_binds.length ; ++i)
1902                         {
1903                                 sbuf.append (m_sqlFragments[i]);
1904                                 if (m_binds[i] == null)
1905                                         sbuf.append( '?' );
1906                                 else
1907                                         sbuf.append (m_binds[i]);
1908                         }
1909                         sbuf.append(m_sqlFragments[m_binds.length]);
1910                         return sbuf.toString();
1911                 }
1912         }
1913
1914         /*
1915          * There are a lot of setXXX classes which all basically do
1916          * the same thing.      We need a method which actually does the
1917          * set for us.
1918          *
1919          * @param paramIndex the index into the inString
1920          * @param s a string to be stored
1921          * @exception SQLException if something goes wrong
1922          */
1923         protected void bind(int paramIndex, Object s, String type) throws SQLException
1924         {
1925                 if (paramIndex < 1 || paramIndex > m_binds.length)
1926                         throw new PSQLException("postgresql.prep.range");
1927                 if (paramIndex == 1 && isFunction) // need to registerOut instead
1928                         throw new PSQLException ("postgresql.call.funcover");
1929                 m_binds[paramIndex - 1] = s;
1930                 m_bindTypes[paramIndex - 1] = type;
1931         }
1932
1933         /*
1934          * Set a parameter to a tablerow-type oid reference.
1935          *
1936          * @param parameterIndex the first parameter is 1...
1937          * @param x the oid of the object from org.postgresql.util.Serialize.store
1938          * @param classname the classname of the java object x
1939          * @exception SQLException if a database access error occurs
1940          */
1941         private void setSerialize(int parameterIndex, long x, String classname) throws SQLException
1942         {
1943                 // converts . to _, toLowerCase, and ensures length < max name length
1944                 String tablename = Serialize.toPostgreSQL((java.sql.Connection)connection, classname );
1945                 DriverManager.println("setSerialize: setting " + x + "::" + tablename );
1946
1947                 // OID reference to tablerow-type must be cast like:  <oid>::<tablename>
1948                 // Note that postgres support for tablerow data types is incomplete/broken.
1949                 // This cannot be just a plain OID because then there would be ambiguity
1950                 // between when you want the oid itself and when you want the object
1951                 // an oid references.
1952                 bind(parameterIndex, Long.toString(x) + "::" + tablename, PG_TEXT );
1953         }
1954
1955         /**
1956          * this method will turn a string of the form
1957          * {? = call <some_function> (?, [?,..]) }
1958          * into the PostgreSQL format which is
1959          * select <some_function> (?, [?, ...]) as result
1960          * or select * from <some_function> (?, [?, ...]) as result (7.3)
1961          *
1962          */
1963         private String modifyJdbcCall(String p_sql) throws SQLException
1964         {
1965                 //Check that this is actually a call which should start with a {
1966         //if not do nothing and treat this as a standard prepared sql
1967                 if (!p_sql.trim().startsWith("{")) {
1968                         return p_sql;
1969                 }
1970
1971                 // syntax checking is not complete only a few basics :(
1972                 originalSql = p_sql; // save for error msgs..
1973                 String l_sql = p_sql;
1974                 int index = l_sql.indexOf ("="); // is implied func or proc?
1975                 boolean isValid = true;
1976                 if (index > -1)
1977                 {
1978                         isFunction = true;
1979                         isValid = l_sql.indexOf ("?") < index; // ? before =
1980                 }
1981                 l_sql = l_sql.trim ();
1982                 if (l_sql.startsWith ("{") && l_sql.endsWith ("}"))
1983                 {
1984                         l_sql = l_sql.substring (1, l_sql.length() - 1);
1985                 }
1986                 else
1987                         isValid = false;
1988                 index = l_sql.indexOf ("call");
1989                 if (index == -1 || !isValid)
1990                         throw new PSQLException ("postgresql.call.malformed",
1991                                                                          new Object[]{l_sql, JDBC_SYNTAX});
1992                 l_sql = l_sql.replace ('{', ' '); // replace these characters
1993                 l_sql = l_sql.replace ('}', ' ');
1994                 l_sql = l_sql.replace (';', ' ');
1995
1996                 // this removes the 'call' string and also puts a hidden '?'
1997                 // at the front of the line for functions, this will
1998                 // allow the registerOutParameter to work correctly
1999                 // because in the source sql there was one more ? for the return
2000                 // value that is not needed by the postgres syntax.  But to make
2001                 // sure that the parameter numbers are the same as in the original
2002                 // sql we add a dummy parameter in this case
2003                 l_sql = (isFunction ? "?" : "") + l_sql.substring (index + 4);
2004                 if (connection.haveMinimumServerVersion("7.3")) {
2005                         l_sql = "select * from " + l_sql + " as " + RESULT_ALIAS + ";";
2006                 } else {
2007                         l_sql = "select " + l_sql + " as " + RESULT_ALIAS + ";";
2008                 }
2009                 return l_sql;
2010         }
2011
2012         /** helperfunction for the getXXX calls to check isFunction and index == 1
2013          * Compare BOTH type fields against the return type.
2014          */
2015         protected void checkIndex (int parameterIndex, int type1, int type2, String getName)
2016         throws SQLException
2017         {
2018                 checkIndex (parameterIndex);            
2019                 if (type1 != this.testReturn && type2 != this.testReturn)
2020                         throw new PSQLException("postgresql.call.wrongget",
2021                                                 new Object[]{"java.sql.Types=" + testReturn,
2022                                                              getName,
2023                                                              "java.sql.Types=" + type1});
2024         }
2025
2026         /** helperfunction for the getXXX calls to check isFunction and index == 1
2027          */
2028         protected void checkIndex (int parameterIndex, int type, String getName)
2029         throws SQLException
2030         {
2031                 checkIndex (parameterIndex);
2032                 if (type != this.testReturn)
2033                         throw new PSQLException("postgresql.call.wrongget",
2034                                                 new Object[]{"java.sql.Types=" + testReturn,
2035                                                              getName,
2036                                                              "java.sql.Types=" + type});
2037         }
2038
2039         /** helperfunction for the getXXX calls to check isFunction and index == 1
2040          * @param parameterIndex index of getXXX (index)
2041          * check to make sure is a function and index == 1
2042          */
2043         private void checkIndex (int parameterIndex) throws SQLException
2044         {
2045                 if (!isFunction)
2046                         throw new PSQLException("postgresql.call.noreturntype");
2047                 if (parameterIndex != 1)
2048                         throw new PSQLException("postgresql.call.noinout");
2049         }
2050
2051
2052
2053     public void setUseServerPrepare(boolean flag) throws SQLException {
2054         //Server side prepared statements were introduced in 7.3
2055         if (connection.haveMinimumServerVersion("7.3")) {
2056                         //If turning server prepared statements off deallocate statement
2057                         //and reset statement name
2058                         if (m_useServerPrepare != flag && !flag)
2059                                 ((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName);
2060                         m_statementName = null;
2061                         m_useServerPrepare = flag;
2062                 } else {
2063                         //This is a pre 7.3 server so no op this method
2064                         //which means we will never turn on the flag to use server
2065                         //prepared statements and thus regular processing will continue
2066                 }
2067         }
2068
2069         public boolean isUseServerPrepare()
2070         {
2071                 return m_useServerPrepare;
2072         }
2073
2074
2075         private static final String PG_TEXT = "text";
2076         private static final String PG_INTEGER = "integer";
2077         private static final String PG_INT2 = "int2";
2078         private static final String PG_INT8 = "int8";
2079         private static final String PG_NUMERIC = "numeric";
2080         private static final String PG_FLOAT = "float";
2081         private static final String PG_DOUBLE = "double precision";
2082         private static final String PG_BOOLEAN = "boolean";
2083         private static final String PG_DATE = "date";
2084         private static final String PG_TIME = "time";
2085         private static final String PG_TIMESTAMPTZ = "timestamptz";
2086     private static final String PG_BYTEA = "bytea";
2087
2088
2089 }