1313import org .postgresql .largeobject .*;
1414import org .postgresql .util .*;
1515
16- /* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.18 2003/03/07 18:39:44 barry Exp $
16+ /* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.19 2003/04/13 04:10:07 barry Exp $
1717 * This class defines methods of the jdbc1 specification. This class is
1818 * extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2
1919 * methods. The real Statement class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Statement
2020 */
2121public abstract class AbstractJdbc1Statement implements BaseStatement
2222{
23-
24- // The connection who created us
23+ // The connection who created us
2524protected BaseConnection connection ;
2625
2726/** The warnings chain. */
@@ -58,6 +57,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
5857
5958protected String []m_bindTypes =new String [0 ];
6059protected String m_statementName =null ;
60+ protected boolean m_statementIsCursor =false ;
6161
6262private boolean m_useServerPrepare =false ;
6363private static int m_preparedCount =1 ;
@@ -159,14 +159,16 @@ public java.sql.ResultSet executeQuery(String p_sql) throws SQLException
159159{
160160try
161161{
162- connection .execSQL ("DEALLOCATE " +m_statementName );
162+ if (!m_statementIsCursor )
163+ connection .execSQL ("DEALLOCATE " +m_statementName );
163164}
164165catch (Exception e )
165166{
166167}
167168finally
168169{
169170m_statementName =null ;
171+ m_statementIsCursor =false ;
170172m_origSqlFragments =null ;
171173m_executeSqlFragments =null ;
172174}
@@ -183,11 +185,8 @@ public java.sql.ResultSet executeQuery(String p_sql) throws SQLException
183185 */
184186public java .sql .ResultSet executeQuery ()throws SQLException
185187{
186- if (fetchSize >0 )
187- this .executeWithCursor ();
188- else
189- this .execute ();
190-
188+ this .execute ();
189+
191190while (result !=null && !result .reallyResultSet ())
192191result = (BaseResultSet )result .getNext ();
193192if (result ==null )
@@ -268,9 +267,13 @@ public boolean execute(String p_sql) throws SQLException
268267 * Some prepared statements return multiple results; the execute method
269268 * handles these complex statements as well as the simpler form of
270269 * statements handled by executeQuery and executeUpdate
270+ *
271+ * This method also handles the translation of the query into a cursor based
272+ * query if the user has specified a fetch size and set the connection
273+ * into a non-auto commit state.
271274 *
272275 * @return true if the next result is a ResultSet; false if it is an
273- ** update count or there are no more results
276+ * update count or there are no more results
274277 * @exception SQLException if a database access error occurs
275278 */
276279public boolean execute ()throws SQLException
@@ -353,10 +356,75 @@ public boolean execute() throws SQLException
353356}
354357}
355358
356- // New in 7.1, pass Statement so that ExecSQL can customise to it
359+ // Use a cursor if directed and in a transaction.
360+ else if (fetchSize >0 && !connection .getAutoCommit ())
361+ {
362+ // The first thing to do is transform the statement text into the cursor form.
363+ String []cursorBasedSql =new String [m_sqlFragments .length ];
364+ // Pinch the prepared count for our own nefarious purposes.
365+ String statementName ="JDBC_CURS_" +m_preparedCount ++;
366+ // Setup the cursor decleration.
367+ // Note that we don't need a BEGIN because we've already
368+ // made sure we're executing inside a transaction.
369+ String cursDecl ="DECLARE " +statementName +" CURSOR FOR " ;
370+ String endCurs =" FETCH FORWARD " +fetchSize +" FROM " +statementName +";" ;
371+
372+ // Copy the real query to the curs decleration.
373+ try
374+ {
375+ // Need to confirm this with Barry Lind.
376+ if (cursorBasedSql .length >1 )
377+ throw new IllegalStateException ("cursor fetches not supported with prepared statements." );
378+ for (int i =0 ;i <cursorBasedSql .length ;i ++)
379+ {
380+ if (i ==0 )
381+ {
382+ if (m_sqlFragments [i ].trim ().toUpperCase ().startsWith ("DECLARE " ))
383+ throw new IllegalStateException ("statement is already cursor based." );
384+ cursorBasedSql [i ] =cursDecl ;
385+ }
386+
387+ if (cursorBasedSql [i ] !=null )
388+ cursorBasedSql [i ] +=m_sqlFragments [i ];
389+ else
390+ cursorBasedSql [i ] =m_sqlFragments [i ];
391+
392+ if (i ==cursorBasedSql .length -1 )
393+ {
394+ // We have to be smart about adding the delimitting ";"
395+ if (m_sqlFragments [i ].endsWith (";" ))
396+ cursorBasedSql [i ] +=endCurs ;
397+ else
398+ cursorBasedSql [i ] += (";" +endCurs );
399+ }
400+ else if (m_sqlFragments [i ].indexOf (";" ) > -1 )
401+ {
402+ throw new IllegalStateException ("multiple statements not "
403+ +"allowed with cursor based querys." );
404+ }
405+ }
406+
407+ // Make the cursor based query the one that will be used.
408+ if (org .postgresql .Driver .logDebug )
409+ org .postgresql .Driver .debug ("using cursor based sql with cursor name " +statementName );
410+
411+ // Do all of this after exceptions have been thrown.
412+ m_statementName =statementName ;
413+ m_statementIsCursor =true ;
414+ m_sqlFragments =cursorBasedSql ;
415+ }
416+ catch (IllegalStateException e )
417+ {
418+ // Something went wrong generating the cursor based statement.
419+ if (org .postgresql .Driver .logDebug )
420+ org .postgresql .Driver .debug (e .getMessage ());
421+ }
422+ }
423+
424+ // New in 7.1, pass Statement so that ExecSQL can customise to it
357425result =QueryExecutor .execute (m_sqlFragments ,
358- m_binds ,
359- this );
426+ m_binds ,
427+ this );
360428
361429//If we are executing a callable statement function set the return data
362430if (isFunction )
@@ -379,102 +447,6 @@ public boolean execute() throws SQLException
379447return (result !=null &&result .reallyResultSet ());
380448}
381449}
382-
383- /** version of execute which converts the query to a cursor.
384- */
385- public boolean executeWithCursor ()throws SQLException
386- {
387- if (isFunction && !returnTypeSet )
388- throw new PSQLException ("postgresql.call.noreturntype" );
389- if (isFunction )
390- {// set entry 1 to dummy entry..
391- m_binds [0 ] ="" ;// dummy entry which ensured that no one overrode
392- m_bindTypes [0 ] =PG_TEXT ;
393- // and calls to setXXX (2,..) really went to first arg in a function call..
394- }
395-
396- // New in 7.1, if we have a previous resultset then force it to close
397- // This brings us nearer to compliance, and helps memory management.
398- // Internal stuff will call ExecSQL directly, bypassing this.
399- if (result !=null )
400- {
401- java .sql .ResultSet rs =getResultSet ();
402- if (rs !=null )
403- rs .close ();
404- }
405-
406- // I've pretty much ignored server prepared statements... can declare and prepare be
407- // used together?
408- // It's trivial to change this: you just have to resolve this issue
409- // of how to work out whether there's a function call. If there isn't then the first
410- // element of the array must be the bit that you extend to become the cursor
411- // decleration.
412- // The last thing that can go wrong is when the user supplies a cursor statement
413- // directly: the translation takes no account of that. I think we should just look
414- // for declare and stop the translation if we find it.
415-
416- // The first thing to do is transform the statement text into the cursor form.
417- String []origSqlFragments =m_sqlFragments ;
418- m_sqlFragments =new String [origSqlFragments .length ];
419- System .arraycopy (origSqlFragments ,0 ,m_sqlFragments ,0 ,origSqlFragments .length );
420- // Pinch the prepared count for our own nefarious purposes.
421- m_statementName ="JDBC_CURS_" +m_preparedCount ++;
422- // The static bit to prepend to all querys.
423- String cursDecl ="BEGIN; DECLARE " +m_statementName +" CURSOR FOR " ;
424- String endCurs =" FETCH FORWARD " +fetchSize +" FROM " +m_statementName +";" ;
425-
426- // Add the real query to the curs decleration.
427- // This is the bit that really makes the presumption about
428- // m_sqlFragments not being a function call.
429- if (m_sqlFragments .length <1 )
430- m_sqlFragments [0 ] =cursDecl +"SELECT NULL;" ;
431-
432- else if (m_sqlFragments .length <2 )
433- {
434- if (m_sqlFragments [0 ].endsWith (";" ))
435- m_sqlFragments [0 ] =cursDecl +m_sqlFragments [0 ] +endCurs ;
436- else
437- m_sqlFragments [0 ] =cursDecl +m_sqlFragments [0 ] +";" +endCurs ;
438- }
439- else
440- {
441- m_sqlFragments [0 ] =cursDecl +m_sqlFragments [0 ];
442- if (m_sqlFragments [m_sqlFragments .length -1 ].endsWith (";" ))
443- m_sqlFragments [m_sqlFragments .length -1 ] +=endCurs ;
444- else
445- m_sqlFragments [m_sqlFragments .length -1 ] += (";" +endCurs );
446- }
447-
448- result =QueryExecutor .execute (m_sqlFragments ,
449- m_binds ,
450- this );
451-
452- //If we are executing a callable statement function set the return data
453- if (isFunction )
454- {
455- if (!result .reallyResultSet ())
456- throw new PSQLException ("postgresql.call.noreturnval" );
457- if (!result .next ())
458- throw new PSQLException ("postgresql.call.noreturnval" );
459- callResult =result .getObject (1 );
460- int columnType =result .getMetaData ().getColumnType (1 );
461- if (columnType !=functionReturnType )
462- {
463- Object []arr =
464- {"java.sql.Types=" +columnType ,
465- "java.sql.Types=" +functionReturnType
466- };
467- throw new PSQLException ("postgresql.call.wrongrtntype" ,arr );
468- }
469- result .close ();
470- return true ;
471- }
472- else
473- {
474- return (result !=null &&result .reallyResultSet ());
475- }
476- }
477-
478450
479451/*
480452 * setCursorName defines the SQL cursor name that will be used by