13
13
import org .postgresql .largeobject .*;
14
14
import org .postgresql .util .*;
15
15
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 $
17
17
* This class defines methods of the jdbc1 specification. This class is
18
18
* extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2
19
19
* methods. The real Statement class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Statement
20
20
*/
21
21
public abstract class AbstractJdbc1Statement implements BaseStatement
22
22
{
23
-
24
- // The connection who created us
23
+ // The connection who created us
25
24
protected BaseConnection connection ;
26
25
27
26
/** The warnings chain. */
@@ -58,6 +57,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
58
57
59
58
protected String []m_bindTypes =new String [0 ];
60
59
protected String m_statementName =null ;
60
+ protected boolean m_statementIsCursor =false ;
61
61
62
62
private boolean m_useServerPrepare =false ;
63
63
private static int m_preparedCount =1 ;
@@ -159,14 +159,16 @@ public java.sql.ResultSet executeQuery(String p_sql) throws SQLException
159
159
{
160
160
try
161
161
{
162
- connection .execSQL ("DEALLOCATE " +m_statementName );
162
+ if (!m_statementIsCursor )
163
+ connection .execSQL ("DEALLOCATE " +m_statementName );
163
164
}
164
165
catch (Exception e )
165
166
{
166
167
}
167
168
finally
168
169
{
169
170
m_statementName =null ;
171
+ m_statementIsCursor =false ;
170
172
m_origSqlFragments =null ;
171
173
m_executeSqlFragments =null ;
172
174
}
@@ -183,11 +185,8 @@ public java.sql.ResultSet executeQuery(String p_sql) throws SQLException
183
185
*/
184
186
public java .sql .ResultSet executeQuery ()throws SQLException
185
187
{
186
- if (fetchSize >0 )
187
- this .executeWithCursor ();
188
- else
189
- this .execute ();
190
-
188
+ this .execute ();
189
+
191
190
while (result !=null && !result .reallyResultSet ())
192
191
result = (BaseResultSet )result .getNext ();
193
192
if (result ==null )
@@ -268,9 +267,13 @@ public boolean execute(String p_sql) throws SQLException
268
267
* Some prepared statements return multiple results; the execute method
269
268
* handles these complex statements as well as the simpler form of
270
269
* 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.
271
274
*
272
275
* @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
274
277
* @exception SQLException if a database access error occurs
275
278
*/
276
279
public boolean execute ()throws SQLException
@@ -353,10 +356,75 @@ public boolean execute() throws SQLException
353
356
}
354
357
}
355
358
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
357
425
result =QueryExecutor .execute (m_sqlFragments ,
358
- m_binds ,
359
- this );
426
+ m_binds ,
427
+ this );
360
428
361
429
//If we are executing a callable statement function set the return data
362
430
if (isFunction )
@@ -379,102 +447,6 @@ public boolean execute() throws SQLException
379
447
return (result !=null &&result .reallyResultSet ());
380
448
}
381
449
}
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
-
478
450
479
451
/*
480
452
* setCursorName defines the SQL cursor name that will be used by