Patches from Oliver Jowett to fix CursorFetchTest, 7.4 now does not automatically delete cursors
This commit is contained in:
parent
15c6764bda
commit
7ecb6ede28
|
@ -6,7 +6,7 @@
|
|||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/BaseConnection.java,v 1.3 2003/05/29 03:21:32 barry Exp $
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/BaseConnection.java,v 1.4 2003/10/29 02:39:09 davec Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -26,7 +26,7 @@ public interface BaseConnection extends PGConnection
|
|||
public void cancelQuery() throws SQLException;
|
||||
public Statement createStatement() throws SQLException;
|
||||
public BaseResultSet execSQL(String s) throws SQLException;
|
||||
public boolean getAutoCommit() throws SQLException;
|
||||
public boolean getAutoCommit();
|
||||
public String getCursorName() throws SQLException;
|
||||
public Encoding getEncoding() throws SQLException;
|
||||
public DatabaseMetaData getMetaData() throws SQLException;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/BaseStatement.java,v 1.5 2003/08/24 22:10:09 barry Exp $
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/BaseStatement.java,v 1.6 2003/10/29 02:39:09 davec Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -30,11 +30,11 @@ public interface BaseStatement extends org.postgresql.PGStatement
|
|||
*/
|
||||
public void addWarning(String p_warning) throws SQLException;
|
||||
public void close() throws SQLException;
|
||||
public int getFetchSize() throws SQLException;
|
||||
public int getFetchSize();
|
||||
public int getMaxFieldSize() throws SQLException;
|
||||
public int getMaxRows() throws SQLException;
|
||||
public int getResultSetConcurrency() throws SQLException;
|
||||
public String getStatementName();
|
||||
public String getFetchingCursorName();
|
||||
public SQLWarning getWarnings() throws SQLException;
|
||||
public void setMaxFieldSize(int max) throws SQLException;
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Connection.java,v 1.26 2003/09/13 04:02:15 barry Exp $
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Connection.java,v 1.27 2003/10/29 02:39:09 davec Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -1270,10 +1270,9 @@ public abstract class AbstractJdbc1Connection implements BaseConnection
|
|||
* gets the current auto-commit state
|
||||
*
|
||||
* @return Current state of the auto-commit mode
|
||||
* @exception SQLException (why?)
|
||||
* @see setAutoCommit
|
||||
*/
|
||||
public boolean getAutoCommit() throws SQLException
|
||||
public boolean getAutoCommit()
|
||||
{
|
||||
return this.autoCommit;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1ResultSet.java,v 1.21 2003/09/22 04:54:59 barry Exp $
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1ResultSet.java,v 1.22 2003/10/29 02:39:09 davec Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -61,6 +61,9 @@ public abstract class AbstractJdbc1ResultSet implements BaseResultSet
|
|||
private SimpleDateFormat m_tstzFormat = null;
|
||||
private SimpleDateFormat m_dateFormat = null;
|
||||
|
||||
private int fetchSize; // Fetch size for next read (might be 0).
|
||||
private int lastFetchSize; // Fetch size of last read (might be 0).
|
||||
|
||||
public abstract ResultSetMetaData getMetaData() throws SQLException;
|
||||
|
||||
public AbstractJdbc1ResultSet(BaseStatement statement,
|
||||
|
@ -82,6 +85,8 @@ public abstract class AbstractJdbc1ResultSet implements BaseResultSet
|
|||
this.this_row = null;
|
||||
this.current_row = -1;
|
||||
this.binaryCursor = binaryCursor;
|
||||
|
||||
this.lastFetchSize = this.fetchSize = (statement == null ? 0 : statement.getFetchSize());
|
||||
}
|
||||
|
||||
public BaseStatement getPGStatement() {
|
||||
|
@ -112,6 +117,20 @@ public abstract class AbstractJdbc1ResultSet implements BaseResultSet
|
|||
this.binaryCursor = binaryCursor;
|
||||
}
|
||||
|
||||
//
|
||||
// Part of the JDBC2 support, but convenient to implement here.
|
||||
//
|
||||
|
||||
public void setFetchSize(int rows) throws SQLException
|
||||
{
|
||||
fetchSize = rows;
|
||||
}
|
||||
|
||||
|
||||
public int getFetchSize() throws SQLException
|
||||
{
|
||||
return fetchSize;
|
||||
}
|
||||
|
||||
public boolean next() throws SQLException
|
||||
{
|
||||
|
@ -120,30 +139,32 @@ public abstract class AbstractJdbc1ResultSet implements BaseResultSet
|
|||
|
||||
if (++current_row >= rows.size())
|
||||
{
|
||||
int fetchSize = statement.getFetchSize();
|
||||
// Must be false if we weren't batching.
|
||||
if (fetchSize == 0)
|
||||
return false;
|
||||
// Use the ref to the statement to get
|
||||
// the details we need to do another cursor
|
||||
// query - it will use reinit() to repopulate this
|
||||
// with the right data.
|
||||
String[] sql = new String[1];
|
||||
String[] binds = new String[0];
|
||||
// Is this the correct query???
|
||||
String cursorName = statement.getStatementName();
|
||||
//if cursorName is null, we are not batching (likely because the
|
||||
//query itself can't be batched)
|
||||
if (cursorName == null)
|
||||
return false;
|
||||
sql[0] = "FETCH FORWARD " + fetchSize + " FROM " + cursorName;
|
||||
QueryExecutor.execute(sql,
|
||||
binds,
|
||||
this);
|
||||
String cursorName = statement.getFetchingCursorName();
|
||||
if (cursorName == null || lastFetchSize == 0 || rows.size() < lastFetchSize)
|
||||
return false; // Not doing a cursor-based fetch or the last fetch was the end of the query
|
||||
|
||||
// Use the ref to the statement to get
|
||||
// the details we need to do another cursor
|
||||
// query - it will use reinit() to repopulate this
|
||||
// with the right data.
|
||||
|
||||
// NB: We can reach this point with fetchSize == 0
|
||||
// if the fetch size is changed halfway through reading results.
|
||||
// Use "FETCH FORWARD ALL" in that case to complete the query.
|
||||
String[] sql = new String[] {
|
||||
fetchSize == 0 ? ("FETCH FORWARD ALL FROM " + cursorName) :
|
||||
("FETCH FORWARD " + fetchSize + " FROM " + cursorName)
|
||||
};
|
||||
|
||||
QueryExecutor.execute(sql,
|
||||
new String[0],
|
||||
this);
|
||||
|
||||
// Test the new rows array.
|
||||
lastFetchSize = fetchSize;
|
||||
if (rows.size() == 0)
|
||||
return false;
|
||||
|
||||
// Test the new rows array.
|
||||
if (rows.size() == 0)
|
||||
return false;
|
||||
// Otherwise reset the counter and let it go on...
|
||||
current_row = 0;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import java.sql.Timestamp;
|
|||
import java.sql.Types;
|
||||
import java.util.Vector;
|
||||
|
||||
/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.40 2003/10/09 01:17:07 wieck Exp $
|
||||
/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.41 2003/10/29 02:39:09 davec Exp $
|
||||
* This class defines methods of the jdbc1 specification. This class is
|
||||
* extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2
|
||||
* methods. The real Statement class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Statement
|
||||
|
@ -62,15 +62,25 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
|
|||
// Some performance caches
|
||||
private StringBuffer sbuf = new StringBuffer(32);
|
||||
|
||||
//Used by the preparedstatement style methods
|
||||
protected String[] m_sqlFragments;
|
||||
private String[] m_origSqlFragments;
|
||||
private String[] m_executeSqlFragments;
|
||||
protected Object[] m_binds = new Object[0];
|
||||
protected String[] m_sqlFragments; // Query fragments.
|
||||
private String[] m_executeSqlFragments; // EXECUTE(...) if useServerPrepare
|
||||
protected Object[] m_binds = new Object[0]; // Parameter values
|
||||
|
||||
protected String[] m_bindTypes = new String[0];
|
||||
protected String m_statementName = null;
|
||||
protected boolean m_statementIsCursor = false;
|
||||
protected String[] m_bindTypes = new String[0]; // Parameter types, for PREPARE(...)
|
||||
protected String m_statementName = null; // Allocated PREPARE statement name for server-prepared statements
|
||||
protected String m_cursorName = null; // Allocated DECLARE cursor name for cursor-based fetch
|
||||
|
||||
// Constants for allowXXX and m_isSingleStatement vars, below.
|
||||
// The idea is to defer the cost of examining the query until we really need to know,
|
||||
// but don't reexamine it every time thereafter.
|
||||
|
||||
private static final short UNKNOWN = 0; // Don't know yet, examine the query.
|
||||
private static final short NO = 1; // Don't use feature
|
||||
private static final short YES = 2; // Do use feature
|
||||
|
||||
private short m_isSingleDML = UNKNOWN; // Is the query a single SELECT/UPDATE/INSERT/DELETE?
|
||||
private short m_isSingleSelect = UNKNOWN; // Is the query a single SELECT?
|
||||
private short m_isSingleStatement = UNKNOWN; // Is the query a single statement?
|
||||
|
||||
private boolean m_useServerPrepare = false;
|
||||
|
||||
|
@ -115,11 +125,11 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
|
|||
return connection;
|
||||
}
|
||||
|
||||
public String getStatementName() {
|
||||
return m_statementName;
|
||||
public String getFetchingCursorName() {
|
||||
return m_cursorName;
|
||||
}
|
||||
|
||||
public int getFetchSize() throws SQLException {
|
||||
public int getFetchSize() {
|
||||
return fetchSize;
|
||||
}
|
||||
|
||||
|
@ -138,6 +148,9 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
|
|||
boolean inQuotes = false;
|
||||
int lastParmEnd = 0, i;
|
||||
|
||||
m_isSingleSelect = m_isSingleDML = UNKNOWN;
|
||||
m_isSingleStatement = YES;
|
||||
|
||||
for (i = 0; i < l_sql.length(); ++i)
|
||||
{
|
||||
int c = l_sql.charAt(i);
|
||||
|
@ -149,6 +162,8 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
|
|||
v.addElement(l_sql.substring (lastParmEnd, i));
|
||||
lastParmEnd = i + 1;
|
||||
}
|
||||
if (c == ';' && !inQuotes)
|
||||
m_isSingleStatement = m_isSingleSelect = m_isSingleDML = NO;
|
||||
}
|
||||
v.addElement(l_sql.substring (lastParmEnd, l_sql.length()));
|
||||
|
||||
|
@ -161,6 +176,30 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
|
|||
|
||||
}
|
||||
|
||||
/*
|
||||
* Deallocate resources allocated for the current query
|
||||
* in preparation for replacing it with a new query.
|
||||
*/
|
||||
private void deallocateQuery()
|
||||
{
|
||||
//If we have already created a server prepared statement, we need
|
||||
//to deallocate the existing one
|
||||
if (m_statementName != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
connection.execSQL("DEALLOCATE " + m_statementName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
m_statementName = null;
|
||||
m_cursorName = null; // automatically closed at end of txn anyway
|
||||
m_executeSqlFragments = null;
|
||||
m_isSingleStatement = m_isSingleSelect = m_isSingleDML = UNKNOWN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute a SQL statement that retruns a single ResultSet
|
||||
|
@ -171,29 +210,12 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
|
|||
*/
|
||||
public java.sql.ResultSet executeQuery(String p_sql) throws SQLException
|
||||
{
|
||||
deallocateQuery();
|
||||
|
||||
String l_sql = replaceProcessing(p_sql);
|
||||
m_sqlFragments = new String[] {l_sql};
|
||||
m_binds = new Object[0];
|
||||
//If we have already created a server prepared statement, we need
|
||||
//to deallocate the existing one
|
||||
if (m_statementName != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!m_statementIsCursor)
|
||||
connection.execSQL("DEALLOCATE " + m_statementName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_statementName = null;
|
||||
m_statementIsCursor = false;
|
||||
m_origSqlFragments = null;
|
||||
m_executeSqlFragments = null;
|
||||
}
|
||||
}
|
||||
|
||||
return executeQuery();
|
||||
}
|
||||
|
||||
|
@ -226,17 +248,12 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
|
|||
*/
|
||||
public int executeUpdate(String p_sql) throws SQLException
|
||||
{
|
||||
deallocateQuery();
|
||||
|
||||
String l_sql = replaceProcessing(p_sql);
|
||||
m_sqlFragments = new String[] {l_sql};
|
||||
m_binds = new Object[0];
|
||||
//If we have already created a server prepared statement, we need
|
||||
//to deallocate the existing one
|
||||
if (m_statementName != null) {
|
||||
connection.execSQL("DEALLOCATE " + m_statementName);
|
||||
m_statementName = null;
|
||||
m_origSqlFragments = null;
|
||||
m_executeSqlFragments = null;
|
||||
}
|
||||
|
||||
return executeUpdate();
|
||||
}
|
||||
|
||||
|
@ -270,28 +287,199 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
|
|||
*/
|
||||
public boolean execute(String p_sql) throws SQLException
|
||||
{
|
||||
deallocateQuery();
|
||||
|
||||
String l_sql = replaceProcessing(p_sql);
|
||||
m_sqlFragments = new String[] {l_sql};
|
||||
m_binds = new Object[0];
|
||||
//If we have already created a server prepared statement, we need
|
||||
//to deallocate the existing one
|
||||
if (m_statementName != null) {
|
||||
connection.execSQL("DEALLOCATE " + m_statementName);
|
||||
m_statementName = null;
|
||||
m_origSqlFragments = null;
|
||||
m_executeSqlFragments = null;
|
||||
}
|
||||
|
||||
return execute();
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the current query is a single statement.
|
||||
*/
|
||||
private boolean isSingleStatement()
|
||||
{
|
||||
if (m_isSingleStatement != UNKNOWN)
|
||||
return m_isSingleStatement == YES;
|
||||
|
||||
// Crude detection of multiple statements. This could be
|
||||
// improved by parsing the whole query for quotes, but is
|
||||
// it worth it given that the only queries that get here are
|
||||
// unparameterized queries?
|
||||
|
||||
for (int i = 0; i < m_sqlFragments.length; ++i) { // a bit redundant, but ..
|
||||
if (m_sqlFragments[i].indexOf(';') != -1) {
|
||||
m_isSingleStatement = NO;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_isSingleStatement = YES;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper for isSingleSelect() and isSingleDML(): computes values
|
||||
* of m_isSingleDML and m_isSingleSelect.
|
||||
*/
|
||||
private void analyzeStatementType()
|
||||
{
|
||||
if (!isSingleStatement()) {
|
||||
m_isSingleSelect = m_isSingleDML = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
String compare = m_sqlFragments[0].trim().toLowerCase();
|
||||
if (compare.startsWith("select")) {
|
||||
m_isSingleSelect = m_isSingleDML = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
m_isSingleSelect = NO;
|
||||
|
||||
if (!compare.startsWith("update") &&
|
||||
!compare.startsWith("delete") &&
|
||||
!compare.startsWith("insert")) {
|
||||
m_isSingleDML = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
m_isSingleDML = YES;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the current query is a single SELECT.
|
||||
*/
|
||||
private boolean isSingleSelect()
|
||||
{
|
||||
if (m_isSingleSelect == UNKNOWN)
|
||||
analyzeStatementType();
|
||||
|
||||
return m_isSingleSelect == YES;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the current query is a single SELECT/UPDATE/INSERT/DELETE.
|
||||
*/
|
||||
private boolean isSingleDML()
|
||||
{
|
||||
if (m_isSingleDML == UNKNOWN)
|
||||
analyzeStatementType();
|
||||
|
||||
return m_isSingleDML == YES;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the query fragments to use for a server-prepared statement.
|
||||
* The first query executed will include a PREPARE and EXECUTE;
|
||||
* subsequent queries will just be an EXECUTE.
|
||||
*/
|
||||
private String[] transformToServerPrepare() {
|
||||
if (m_statementName != null)
|
||||
return m_executeSqlFragments;
|
||||
|
||||
// First time through.
|
||||
m_statementName = "JDBC_STATEMENT_" + m_preparedCount++;
|
||||
|
||||
// Set up m_executeSqlFragments
|
||||
m_executeSqlFragments = new String[m_sqlFragments.length];
|
||||
m_executeSqlFragments[0] = "EXECUTE " + m_statementName;
|
||||
if (m_sqlFragments.length > 1) {
|
||||
m_executeSqlFragments[0] += "(";
|
||||
for (int i = 1; i < m_bindTypes.length; i++)
|
||||
m_executeSqlFragments[i] = ", ";
|
||||
m_executeSqlFragments[m_bindTypes.length] = ")";
|
||||
}
|
||||
|
||||
// Set up the PREPARE.
|
||||
String[] prepareSqlFragments = new String[m_sqlFragments.length];
|
||||
System.arraycopy(m_sqlFragments, 0, prepareSqlFragments, 0, m_sqlFragments.length);
|
||||
|
||||
synchronized (sbuf) {
|
||||
sbuf.setLength(0);
|
||||
sbuf.append("PREPARE ");
|
||||
sbuf.append(m_statementName);
|
||||
if (m_sqlFragments.length > 1) {
|
||||
sbuf.append("(");
|
||||
for (int i = 0; i < m_bindTypes.length; i++) {
|
||||
if (i != 0) sbuf.append(", ");
|
||||
sbuf.append(m_bindTypes[i]);
|
||||
}
|
||||
sbuf.append(")");
|
||||
}
|
||||
sbuf.append(" AS ");
|
||||
sbuf.append(m_sqlFragments[0]);
|
||||
for (int i = 1; i < m_sqlFragments.length; i++) {
|
||||
sbuf.append(" $");
|
||||
sbuf.append(i);
|
||||
sbuf.append(" ");
|
||||
sbuf.append(m_sqlFragments[i]);
|
||||
}
|
||||
sbuf.append("; ");
|
||||
sbuf.append(m_executeSqlFragments[0]);
|
||||
|
||||
prepareSqlFragments[0] = sbuf.toString();
|
||||
}
|
||||
|
||||
System.arraycopy(m_executeSqlFragments, 1, prepareSqlFragments, 1, prepareSqlFragments.length - 1);
|
||||
return prepareSqlFragments;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the current query transformed into a cursor-based statement.
|
||||
* This uses a new cursor on each query.
|
||||
*/
|
||||
private String[] transformToCursorFetch()
|
||||
{
|
||||
|
||||
// Pinch the prepared count for our own nefarious purposes.
|
||||
m_cursorName = "JDBC_CURS_" + m_preparedCount++;
|
||||
|
||||
// Create a cursor declaration and initial fetch statement from the original query.
|
||||
int len = m_sqlFragments.length;
|
||||
String[] cursorBasedSql = new String[len];
|
||||
System.arraycopy(m_sqlFragments, 0, cursorBasedSql, 0, len);
|
||||
cursorBasedSql[0] = "DECLARE " + m_cursorName + " CURSOR FOR " + cursorBasedSql[0];
|
||||
cursorBasedSql[len-1] += "; FETCH FORWARD " + fetchSize + " FROM " + m_cursorName;
|
||||
|
||||
// Make the cursor based query the one that will be used.
|
||||
if (org.postgresql.Driver.logDebug)
|
||||
org.postgresql.Driver.debug("using cursor based sql with cursor name " + m_cursorName);
|
||||
|
||||
return cursorBasedSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do transformations to a query for server-side prepare or setFetchSize() cursor
|
||||
* work.
|
||||
* @return the query fragments to execute
|
||||
*/
|
||||
private String[] getQueryFragments()
|
||||
{
|
||||
// nb: isSingleXXX() are relatively expensive, avoid calling them unless we must.
|
||||
|
||||
// We check the "mutable" bits of these conditions (which may change without
|
||||
// a new query being created) here; isSingleXXX() only concern themselves with
|
||||
// the query structure itself.
|
||||
|
||||
// We prefer cursor-based-fetch over server-side-prepare here.
|
||||
// Eventually a v3 implementation should let us do both at once.
|
||||
if (fetchSize > 0 && !connection.getAutoCommit() && isSingleSelect())
|
||||
return transformToCursorFetch();
|
||||
|
||||
if (isUseServerPrepare() && isSingleDML())
|
||||
return transformToServerPrepare();
|
||||
|
||||
// Not server-prepare or cursor-fetch, just return a plain query.
|
||||
return m_sqlFragments;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some prepared statements return multiple results; the execute method
|
||||
* handles these complex statements as well as the simpler form of
|
||||
* statements handled by executeQuery and executeUpdate
|
||||
*
|
||||
* This method also handles the translation of the query into a cursor based
|
||||
* query if the user has specified a fetch size and set the connection
|
||||
* into a non-auto commit state.
|
||||
*
|
||||
* @return true if the next result is a ResultSet; false if it is an
|
||||
* update count or there are no more results
|
||||
|
@ -319,133 +507,14 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
|
|||
rs.close();
|
||||
}
|
||||
|
||||
//Use server prepared statements if directed
|
||||
if (m_useServerPrepare)
|
||||
{
|
||||
if (m_statementName == null)
|
||||
{
|
||||
m_statementName = "JDBC_STATEMENT_" + next_preparedCount();
|
||||
m_origSqlFragments = new String[m_sqlFragments.length];
|
||||
m_executeSqlFragments = new String[m_sqlFragments.length];
|
||||
System.arraycopy(m_sqlFragments, 0, m_origSqlFragments, 0, m_sqlFragments.length);
|
||||
m_executeSqlFragments[0] = "EXECUTE " + m_statementName;
|
||||
if (m_sqlFragments.length > 1)
|
||||
{
|
||||
m_executeSqlFragments[0] = m_executeSqlFragments[0] + "(";
|
||||
for (int i = 1; i < m_bindTypes.length; i++)
|
||||
{
|
||||
m_executeSqlFragments[i] = ", ";
|
||||
}
|
||||
m_executeSqlFragments[m_bindTypes.length] = ")";
|
||||
}
|
||||
synchronized (sbuf)
|
||||
{
|
||||
sbuf.setLength(0);
|
||||
sbuf.append("PREPARE ");
|
||||
sbuf.append(m_statementName);
|
||||
if (m_origSqlFragments.length > 1)
|
||||
{
|
||||
sbuf.append("(");
|
||||
for (int i = 0; i < m_bindTypes.length - 1; i++)
|
||||
{
|
||||
sbuf.append(m_bindTypes[i]);
|
||||
sbuf.append(", ");
|
||||
}
|
||||
sbuf.append(m_bindTypes[m_bindTypes.length - 1]);
|
||||
sbuf.append(")");
|
||||
}
|
||||
sbuf.append(" AS ");
|
||||
sbuf.append(m_origSqlFragments[0]);
|
||||
for (int i = 1; i < m_origSqlFragments.length; i++)
|
||||
{
|
||||
sbuf.append(" $");
|
||||
sbuf.append(i);
|
||||
sbuf.append(" ");
|
||||
sbuf.append(m_origSqlFragments[i]);
|
||||
}
|
||||
sbuf.append("; ");
|
||||
|
||||
sbuf.append(m_executeSqlFragments[0]);
|
||||
m_sqlFragments[0] = sbuf.toString();
|
||||
System.arraycopy(m_executeSqlFragments, 1, m_sqlFragments, 1, m_sqlFragments.length - 1);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sqlFragments = m_executeSqlFragments;
|
||||
}
|
||||
}
|
||||
|
||||
// Use a cursor if directed and in a transaction.
|
||||
else if (fetchSize > 0 && !connection.getAutoCommit())
|
||||
{
|
||||
// The first thing to do is transform the statement text into the cursor form.
|
||||
String[] cursorBasedSql = new String[m_sqlFragments.length];
|
||||
// Pinch the prepared count for our own nefarious purposes.
|
||||
String statementName = "JDBC_CURS_" + next_preparedCount();
|
||||
// Setup the cursor decleration.
|
||||
// Note that we don't need a BEGIN because we've already
|
||||
// made sure we're executing inside a transaction.
|
||||
String cursDecl = "DECLARE " + statementName + " CURSOR FOR ";
|
||||
String endCurs = " FETCH FORWARD " + fetchSize + " FROM " + statementName + ";";
|
||||
|
||||
// Copy the real query to the curs decleration.
|
||||
try
|
||||
{
|
||||
// Need to confirm this with Barry Lind.
|
||||
if (cursorBasedSql.length > 1)
|
||||
throw new IllegalStateException("cursor fetches not supported with prepared statements.");
|
||||
for (int i = 0; i < cursorBasedSql.length; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
if (m_sqlFragments[i].trim().toUpperCase().startsWith("DECLARE "))
|
||||
throw new IllegalStateException("statement is already cursor based.");
|
||||
cursorBasedSql[i] = cursDecl;
|
||||
}
|
||||
|
||||
if (cursorBasedSql[i] != null)
|
||||
cursorBasedSql[i] += m_sqlFragments[i];
|
||||
else
|
||||
cursorBasedSql[i] = m_sqlFragments[i];
|
||||
|
||||
if (i == cursorBasedSql.length - 1)
|
||||
{
|
||||
// We have to be smart about adding the delimitting ";"
|
||||
if (m_sqlFragments[i].endsWith(";"))
|
||||
cursorBasedSql[i] += endCurs;
|
||||
else
|
||||
cursorBasedSql[i] += (";" + endCurs);
|
||||
}
|
||||
else if (m_sqlFragments[i].indexOf(";") > -1)
|
||||
{
|
||||
throw new IllegalStateException("multiple statements not "
|
||||
+ "allowed with cursor based querys.");
|
||||
}
|
||||
}
|
||||
|
||||
// Make the cursor based query the one that will be used.
|
||||
if (org.postgresql.Driver.logDebug)
|
||||
org.postgresql.Driver.debug("using cursor based sql with cursor name " + statementName);
|
||||
|
||||
// Do all of this after exceptions have been thrown.
|
||||
m_statementName = statementName;
|
||||
m_statementIsCursor = true;
|
||||
m_sqlFragments = cursorBasedSql;
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
// Something went wrong generating the cursor based statement.
|
||||
if (org.postgresql.Driver.logDebug)
|
||||
org.postgresql.Driver.debug(e.getMessage());
|
||||
}
|
||||
}
|
||||
// Get the actual query fragments to run (might be a transformed version of
|
||||
// the original fragments)
|
||||
String[] fragments = getQueryFragments();
|
||||
|
||||
// New in 7.1, pass Statement so that ExecSQL can customise to it
|
||||
result = QueryExecutor.execute(m_sqlFragments,
|
||||
m_binds,
|
||||
this);
|
||||
result = QueryExecutor.execute(fragments,
|
||||
m_binds,
|
||||
this);
|
||||
|
||||
//If we are executing a callable statement function set the return data
|
||||
if (isFunction)
|
||||
|
@ -721,10 +790,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
|
|||
if (rs != null)
|
||||
rs.close();
|
||||
|
||||
// If using server prepared statements deallocate them
|
||||
if (m_useServerPrepare && m_statementName != null) {
|
||||
connection.execSQL("DEALLOCATE " + m_statementName);
|
||||
}
|
||||
deallocateQuery();
|
||||
|
||||
// Disasociate it from us (For Garbage Collection)
|
||||
result = null;
|
||||
|
@ -2093,11 +2159,8 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
|
|||
public void setUseServerPrepare(boolean flag) throws SQLException {
|
||||
//Server side prepared statements were introduced in 7.3
|
||||
if (connection.haveMinimumServerVersion("7.3")) {
|
||||
//If turning server prepared statements off deallocate statement
|
||||
//and reset statement name
|
||||
if (m_useServerPrepare != flag && !flag && m_statementName != null)
|
||||
connection.execSQL("DEALLOCATE " + m_statementName);
|
||||
m_statementName = null;
|
||||
if (m_useServerPrepare != flag)
|
||||
deallocateQuery();
|
||||
m_useServerPrepare = flag;
|
||||
} else {
|
||||
//This is a pre 7.3 server so no op this method
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2ResultSet.java,v 1.24 2003/09/17 05:14:52 barry Exp $
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2ResultSet.java,v 1.25 2003/10/29 02:39:09 davec Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -389,13 +389,6 @@ public abstract class AbstractJdbc2ResultSet extends org.postgresql.jdbc1.Abstra
|
|||
}
|
||||
|
||||
|
||||
public int getFetchSize() throws SQLException
|
||||
{
|
||||
// Returning the current batch size seems the right thing to do.
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
|
||||
public Object getObject(String columnName, java.util.Map map) throws SQLException
|
||||
{
|
||||
return getObject(findColumn(columnName), map);
|
||||
|
@ -518,13 +511,6 @@ public abstract class AbstractJdbc2ResultSet extends org.postgresql.jdbc1.Abstra
|
|||
}
|
||||
|
||||
|
||||
public void setFetchSize(int rows) throws SQLException
|
||||
{
|
||||
// Sub-classes should implement this as part of their cursor support
|
||||
throw org.postgresql.Driver.notImplemented();
|
||||
}
|
||||
|
||||
|
||||
public synchronized void cancelRowUpdates()
|
||||
throws SQLException
|
||||
{
|
||||
|
|
|
@ -51,7 +51,10 @@ public class CursorFetchTest extends TestCase
|
|||
int[] testSizes = { 0, 1, 49, 50, 51, 99, 100, 101 };
|
||||
for (int i = 0; i < testSizes.length; ++i) {
|
||||
stmt.setFetchSize(testSizes[i]);
|
||||
assertEquals(testSizes[i], stmt.getFetchSize());
|
||||
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
assertEquals(testSizes[i], rs.getFetchSize());
|
||||
|
||||
int count = 0;
|
||||
while (rs.next()) {
|
||||
|
@ -63,6 +66,115 @@ public class CursorFetchTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Tests for ResultSet.setFetchSize().
|
||||
//
|
||||
|
||||
// test one:
|
||||
// set fetchsize = 0
|
||||
// run query (all rows should be fetched)
|
||||
// set fetchsize = 50 (should have no effect)
|
||||
// process results
|
||||
public void testResultSetFetchSizeOne() throws Exception
|
||||
{
|
||||
createRows(100);
|
||||
|
||||
PreparedStatement stmt = con.prepareStatement("select * from test_fetch order by value");
|
||||
stmt.setFetchSize(0);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
stmt.setFetchSize(50); // Should have no effect.
|
||||
|
||||
int count = 0;
|
||||
while (rs.next()) {
|
||||
assertEquals(count, rs.getInt(1));
|
||||
++count;
|
||||
}
|
||||
|
||||
assertEquals(100, count);
|
||||
}
|
||||
|
||||
// test two:
|
||||
// set fetchsize = 25
|
||||
// run query (25 rows fetched)
|
||||
// set fetchsize = 0
|
||||
// process results:
|
||||
// process 25 rows
|
||||
// should do a FETCH ALL to get more data
|
||||
// process 75 rows
|
||||
public void testResultSetFetchSizeTwo() throws Exception
|
||||
{
|
||||
createRows(100);
|
||||
|
||||
PreparedStatement stmt = con.prepareStatement("select * from test_fetch order by value");
|
||||
stmt.setFetchSize(25);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
stmt.setFetchSize(0);
|
||||
|
||||
int count = 0;
|
||||
while (rs.next()) {
|
||||
assertEquals(count, rs.getInt(1));
|
||||
++count;
|
||||
}
|
||||
|
||||
assertEquals(100, count);
|
||||
}
|
||||
|
||||
// test three:
|
||||
// set fetchsize = 25
|
||||
// run query (25 rows fetched)
|
||||
// set fetchsize = 50
|
||||
// process results:
|
||||
// process 25 rows. should NOT hit end-of-results here.
|
||||
// do a FETCH FORWARD 50
|
||||
// process 50 rows
|
||||
// do a FETCH FORWARD 50
|
||||
// process 25 rows. end of results.
|
||||
public void testResultSetFetchSizeThree() throws Exception
|
||||
{
|
||||
createRows(100);
|
||||
|
||||
PreparedStatement stmt = con.prepareStatement("select * from test_fetch order by value");
|
||||
stmt.setFetchSize(25);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
stmt.setFetchSize(50);
|
||||
|
||||
int count = 0;
|
||||
while (rs.next()) {
|
||||
assertEquals(count, rs.getInt(1));
|
||||
++count;
|
||||
}
|
||||
|
||||
assertEquals(100, count);
|
||||
}
|
||||
|
||||
// test four:
|
||||
// set fetchsize = 50
|
||||
// run query (50 rows fetched)
|
||||
// set fetchsize = 25
|
||||
// process results:
|
||||
// process 50 rows.
|
||||
// do a FETCH FORWARD 25
|
||||
// process 25 rows
|
||||
// do a FETCH FORWARD 25
|
||||
// process 25 rows. end of results.
|
||||
public void testResultSetFetchSizeFour() throws Exception
|
||||
{
|
||||
createRows(100);
|
||||
|
||||
PreparedStatement stmt = con.prepareStatement("select * from test_fetch order by value");
|
||||
stmt.setFetchSize(50);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
stmt.setFetchSize(25);
|
||||
|
||||
int count = 0;
|
||||
while (rs.next()) {
|
||||
assertEquals(count, rs.getInt(1));
|
||||
++count;
|
||||
}
|
||||
|
||||
assertEquals(100, count);
|
||||
}
|
||||
|
||||
// Test odd queries that should not be transformed into cursor-based fetches.
|
||||
public void TODO_FAILS_testInsert() throws Exception
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue