1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-15 19:21:59 +03:00

Patch from Nic Ferrier to add support for result sets being cursor based

so that rows can be fetched incrementally.  This is enabled by using
setFetchSize()
This commit is contained in:
Barry Lind
2003-02-04 09:20:12 +00:00
parent 2d1f940542
commit 16a30346c8
26 changed files with 430 additions and 233 deletions

View File

@ -8,7 +8,7 @@ import java.util.Vector;
import org.postgresql.largeobject.*;
import org.postgresql.util.*;
/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.14 2002/11/20 07:34:32 barry Exp $
/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.15 2003/02/04 09:20:08 barry 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
@ -19,12 +19,19 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
// The connection who created us
protected AbstractJdbc1Connection connection;
public org.postgresql.PGConnection getPGConnection() {
return connection;
}
/** The warnings chain. */
protected SQLWarning warnings = null;
/** Maximum number of rows to return, 0 = unlimited */
protected int maxrows = 0;
/** Number of rows to get in a batch. */
protected int fetchSize = 0;
/** Timeout (in seconds) for a query (not used) */
protected int timeout = 0;
@ -47,8 +54,10 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
private String[] m_origSqlFragments;
private String[] m_executeSqlFragments;
protected Object[] m_binds = new Object[0];
protected String[] m_bindTypes = new String[0];
private String m_statementName = null;
protected String m_statementName = null;
private boolean m_useServerPrepare = false;
private static int m_preparedCount = 1;
@ -67,6 +76,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
protected Object callResult;
public abstract java.sql.ResultSet createResultSet(org.postgresql.Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException;
public AbstractJdbc1Statement (AbstractJdbc1Connection connection)
{
@ -117,7 +127,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
}
/*
* Execute a SQL statement that retruns a single ResultSet
*
@ -132,11 +142,21 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
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) {
((AbstractJdbc1Connection)connection).ExecSQL("DEALLOCATE " + m_statementName);
m_statementName = null;
m_origSqlFragments = null;
m_executeSqlFragments = null;
if (m_statementName != null)
{
try
{
((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName);
}
catch (Exception e)
{
}
finally
{
m_statementName = null;
m_origSqlFragments = null;
m_executeSqlFragments = null;
}
}
return executeQuery();
}
@ -150,7 +170,11 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
*/
public java.sql.ResultSet executeQuery() throws SQLException
{
this.execute();
if (fetchSize > 0)
this.executeWithCursor();
else
this.execute();
while (result != null && !((AbstractJdbc1ResultSet)result).reallyResultSet())
result = ((AbstractJdbc1ResultSet)result).getNext();
if (result == null)
@ -175,7 +199,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
//If we have already created a server prepared statement, we need
//to deallocate the existing one
if (m_statementName != null) {
((AbstractJdbc1Connection)connection).ExecSQL("DEALLOCATE " + m_statementName);
((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName);
m_statementName = null;
m_origSqlFragments = null;
m_executeSqlFragments = null;
@ -219,7 +243,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
//If we have already created a server prepared statement, we need
//to deallocate the existing one
if (m_statementName != null) {
((AbstractJdbc1Connection)connection).ExecSQL("DEALLOCATE " + m_statementName);
((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName);
m_statementName = null;
m_origSqlFragments = null;
m_executeSqlFragments = null;
@ -317,7 +341,9 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
}
// New in 7.1, pass Statement so that ExecSQL can customise to it
result = ((AbstractJdbc1Connection)connection).ExecSQL(m_sqlFragments, m_binds, (java.sql.Statement)this);
result = org.postgresql.core.QueryExecutor.execute(m_sqlFragments,
m_binds,
(java.sql.Statement)this);
//If we are executing a callable statement function set the return data
if (isFunction)
@ -341,6 +367,102 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
}
}
/** version of execute which converts the query to a cursor.
*/
public boolean executeWithCursor() throws SQLException
{
if (isFunction && !returnTypeSet)
throw new PSQLException("postgresql.call.noreturntype");
if (isFunction)
{ // set entry 1 to dummy entry..
m_binds[0] = ""; // dummy entry which ensured that no one overrode
m_bindTypes[0] = PG_TEXT;
// and calls to setXXX (2,..) really went to first arg in a function call..
}
// New in 7.1, if we have a previous resultset then force it to close
// This brings us nearer to compliance, and helps memory management.
// Internal stuff will call ExecSQL directly, bypassing this.
if (result != null)
{
java.sql.ResultSet rs = getResultSet();
if (rs != null)
rs.close();
}
// I've pretty much ignored server prepared statements... can declare and prepare be
// used together?
// It's trivial to change this: you just have to resolve this issue
// of how to work out whether there's a function call. If there isn't then the first
// element of the array must be the bit that you extend to become the cursor
// decleration.
// The last thing that can go wrong is when the user supplies a cursor statement
// directly: the translation takes no account of that. I think we should just look
// for declare and stop the translation if we find it.
// The first thing to do is transform the statement text into the cursor form.
String[] origSqlFragments = m_sqlFragments;
m_sqlFragments = new String[origSqlFragments.length];
System.arraycopy(origSqlFragments, 0, m_sqlFragments, 0, origSqlFragments.length);
// Pinch the prepared count for our own nefarious purposes.
m_statementName = "JDBC_CURS_" + m_preparedCount++;
// The static bit to prepend to all querys.
String cursDecl = "BEGIN; DECLARE " + m_statementName + " CURSOR FOR ";
String endCurs = " FETCH FORWARD " + fetchSize + " FROM " + m_statementName + ";";
// Add the real query to the curs decleration.
// This is the bit that really makes the presumption about
// m_sqlFragments not being a function call.
if (m_sqlFragments.length < 1)
m_sqlFragments[0] = cursDecl + "SELECT NULL;";
else if (m_sqlFragments.length < 2)
{
if (m_sqlFragments[0].endsWith(";"))
m_sqlFragments[0] = cursDecl + m_sqlFragments[0] + endCurs;
else
m_sqlFragments[0] = cursDecl + m_sqlFragments[0] + ";" + endCurs;
}
else
{
m_sqlFragments[0] = cursDecl + m_sqlFragments[0];
if (m_sqlFragments[m_sqlFragments.length - 1].endsWith(";"))
m_sqlFragments[m_sqlFragments.length - 1] += endCurs;
else
m_sqlFragments[m_sqlFragments.length - 1] += (";" + endCurs);
}
result = org.postgresql.core.QueryExecutor.execute(m_sqlFragments,
m_binds,
(java.sql.Statement)this);
//If we are executing a callable statement function set the return data
if (isFunction)
{
if (!((AbstractJdbc1ResultSet)result).reallyResultSet())
throw new PSQLException("postgresql.call.noreturnval");
if (!result.next ())
throw new PSQLException ("postgresql.call.noreturnval");
callResult = result.getObject(1);
int columnType = result.getMetaData().getColumnType(1);
if (columnType != functionReturnType)
{
Object[] arr =
{ "java.sql.Types=" + columnType,
"java.sql.Types=" + functionReturnType
};
throw new PSQLException ("postgresql.call.wrongrtntype",arr);
}
result.close ();
return true;
}
else
{
return (result != null && ((AbstractJdbc1ResultSet)result).reallyResultSet());
}
}
/*
* setCursorName defines the SQL cursor name that will be used by
* subsequent execute methods. This name can then be used in SQL
@ -593,7 +715,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
// If using server prepared statements deallocate them
if (m_useServerPrepare && m_statementName != null) {
((AbstractJdbc1Connection)connection).ExecSQL("DEALLOCATE " + m_statementName);
((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName);
}
// Disasociate it from us (For Garbage Collection)
@ -1690,7 +1812,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
*/
public byte[] getBytes(int parameterIndex) throws SQLException
{
checkIndex (parameterIndex, Types.VARBINARY, "Bytes");
checkIndex (parameterIndex, Types.VARBINARY, Types.BINARY, "Bytes");
return ((byte [])callResult);
}
@ -1847,7 +1969,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
String l_sql = p_sql;
int index = l_sql.indexOf ("="); // is implied func or proc?
boolean isValid = true;
if (index != -1)
if (index > -1)
{
isFunction = true;
isValid = l_sql.indexOf ("?") < index; // ? before =
@ -1875,11 +1997,24 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
// sure that the parameter numbers are the same as in the original
// sql we add a dummy parameter in this case
l_sql = (isFunction ? "?" : "") + l_sql.substring (index + 4);
l_sql = "select " + l_sql + " as " + RESULT_COLUMN + ";";
return l_sql;
}
/** helperfunction for the getXXX calls to check isFunction and index == 1
* Compare BOTH type fields against the return type.
*/
protected void checkIndex (int parameterIndex, int type1, int type2, String getName)
throws SQLException
{
checkIndex (parameterIndex);
if (type1 != this.testReturn && type2 != this.testReturn)
throw new PSQLException("postgresql.call.wrongget",
new Object[]{"java.sql.Types=" + testReturn,
getName,
"java.sql.Types=" + type1});
}
/** helperfunction for the getXXX calls to check isFunction and index == 1
*/
protected void checkIndex (int parameterIndex, int type, String getName)
@ -1888,10 +2023,11 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
checkIndex (parameterIndex);
if (type != this.testReturn)
throw new PSQLException("postgresql.call.wrongget",
new Object[]{"java.sql.Types=" + testReturn,
getName,
"java.sql.Types=" + type});
new Object[]{"java.sql.Types=" + testReturn,
getName,
"java.sql.Types=" + type});
}
/** helperfunction for the getXXX calls to check isFunction and index == 1
* @param parameterIndex index of getXXX (index)
* check to make sure is a function and index == 1
@ -1912,7 +2048,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
//If turning server prepared statements off deallocate statement
//and reset statement name
if (m_useServerPrepare != flag && !flag)
((AbstractJdbc1Connection)connection).ExecSQL("DEALLOCATE " + m_statementName);
((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName);
m_statementName = null;
m_useServerPrepare = flag;
} else {