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:
@ -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 {
|
||||
|
Reference in New Issue
Block a user