Commit 51acf972 authored by Barry Lind's avatar Barry Lind

Applied patch submitted by Nic Ferrier with some cleanups of his previous

patch to add cursor based queries.

 Modified Files:
 	jdbc/org/postgresql/core/BaseConnection.java
 	jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java
parent 3fd5faed
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Copyright (c) 2003, PostgreSQL Global Development Group * Copyright (c) 2003, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/BaseConnection.java,v 1.1 2003/03/07 18:39:41 barry Exp $ * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/BaseConnection.java,v 1.2 2003/04/13 04:10:07 barry Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -27,6 +27,7 @@ public interface BaseConnection extends PGConnection ...@@ -27,6 +27,7 @@ public interface BaseConnection extends PGConnection
public void cancelQuery() throws SQLException; public void cancelQuery() throws SQLException;
public Statement createStatement() throws SQLException; public Statement createStatement() throws SQLException;
public BaseResultSet execSQL(String s) throws SQLException; public BaseResultSet execSQL(String s) throws SQLException;
public boolean getAutoCommit() throws SQLException;
public String getCursorName() throws SQLException; public String getCursorName() throws SQLException;
public Encoding getEncoding() throws SQLException; public Encoding getEncoding() throws SQLException;
public DatabaseMetaData getMetaData() throws SQLException; public DatabaseMetaData getMetaData() throws SQLException;
...@@ -38,6 +39,7 @@ public interface BaseConnection extends PGConnection ...@@ -38,6 +39,7 @@ public interface BaseConnection extends PGConnection
public int getSQLType(String pgTypeName) throws SQLException; public int getSQLType(String pgTypeName) throws SQLException;
public boolean haveMinimumCompatibleVersion(String ver) throws SQLException; public boolean haveMinimumCompatibleVersion(String ver) throws SQLException;
public boolean haveMinimumServerVersion(String ver) throws SQLException; public boolean haveMinimumServerVersion(String ver) throws SQLException;
public void setAutoCommit(boolean autoCommit) throws SQLException;
public void setCursorName(String cursor) throws SQLException; public void setCursorName(String cursor) throws SQLException;
} }
......
...@@ -13,14 +13,13 @@ import org.postgresql.core.QueryExecutor; ...@@ -13,14 +13,13 @@ import org.postgresql.core.QueryExecutor;
import org.postgresql.largeobject.*; import org.postgresql.largeobject.*;
import org.postgresql.util.*; import org.postgresql.util.*;
/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.18 2003/03/07 18:39:44 barry Exp $ /* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.19 2003/04/13 04:10:07 barry Exp $
* This class defines methods of the jdbc1 specification. This class is * This class defines methods of the jdbc1 specification. This class is
* extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2 * extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2
* methods. The real Statement class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Statement * methods. The real Statement class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Statement
*/ */
public abstract class AbstractJdbc1Statement implements BaseStatement public abstract class AbstractJdbc1Statement implements BaseStatement
{ {
// The connection who created us // The connection who created us
protected BaseConnection connection; protected BaseConnection connection;
...@@ -58,6 +57,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement ...@@ -58,6 +57,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
protected String[] m_bindTypes = new String[0]; protected String[] m_bindTypes = new String[0];
protected String m_statementName = null; protected String m_statementName = null;
protected boolean m_statementIsCursor = false;
private boolean m_useServerPrepare = false; private boolean m_useServerPrepare = false;
private static int m_preparedCount = 1; private static int m_preparedCount = 1;
...@@ -159,6 +159,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement ...@@ -159,6 +159,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
{ {
try try
{ {
if (!m_statementIsCursor)
connection.execSQL("DEALLOCATE " + m_statementName); connection.execSQL("DEALLOCATE " + m_statementName);
} }
catch (Exception e) catch (Exception e)
...@@ -167,6 +168,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement ...@@ -167,6 +168,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
finally finally
{ {
m_statementName = null; m_statementName = null;
m_statementIsCursor = false;
m_origSqlFragments = null; m_origSqlFragments = null;
m_executeSqlFragments = null; m_executeSqlFragments = null;
} }
...@@ -183,9 +185,6 @@ public abstract class AbstractJdbc1Statement implements BaseStatement ...@@ -183,9 +185,6 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
*/ */
public java.sql.ResultSet executeQuery() throws SQLException public java.sql.ResultSet executeQuery() throws SQLException
{ {
if (fetchSize > 0)
this.executeWithCursor();
else
this.execute(); this.execute();
while (result != null && !result.reallyResultSet()) while (result != null && !result.reallyResultSet())
...@@ -269,8 +268,12 @@ public abstract class AbstractJdbc1Statement implements BaseStatement ...@@ -269,8 +268,12 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
* handles these complex statements as well as the simpler form of * handles these complex statements as well as the simpler form of
* statements handled by executeQuery and executeUpdate * 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 * @return true if the next result is a ResultSet; false if it is an
* * update count or there are no more results * update count or there are no more results
* @exception SQLException if a database access error occurs * @exception SQLException if a database access error occurs
*/ */
public boolean execute() throws SQLException public boolean execute() throws SQLException
...@@ -353,98 +356,72 @@ public abstract class AbstractJdbc1Statement implements BaseStatement ...@@ -353,98 +356,72 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
} }
} }
// New in 7.1, pass Statement so that ExecSQL can customise to it // Use a cursor if directed and in a transaction.
result = QueryExecutor.execute(m_sqlFragments, else if (fetchSize > 0 && !connection.getAutoCommit())
m_binds,
this);
//If we are executing a callable statement function set the return data
if (isFunction)
{ {
if (!result.reallyResultSet()) // The first thing to do is transform the statement text into the cursor form.
throw new PSQLException("postgresql.call.noreturnval"); String[] cursorBasedSql = new String[m_sqlFragments.length];
if (!result.next ()) // Pinch the prepared count for our own nefarious purposes.
throw new PSQLException ("postgresql.call.noreturnval"); String statementName = "JDBC_CURS_" + m_preparedCount++;
callResult = result.getObject(1); // Setup the cursor decleration.
int columnType = result.getMetaData().getColumnType(1); // Note that we don't need a BEGIN because we've already
if (columnType != functionReturnType) // made sure we're executing inside a transaction.
throw new PSQLException ("postgresql.call.wrongrtntype", String cursDecl = "DECLARE " + statementName + " CURSOR FOR ";
new Object[]{ String endCurs = " FETCH FORWARD " + fetchSize + " FROM " + statementName + ";";
"java.sql.Types=" + columnType, "java.sql.Types=" + functionReturnType });
result.close (); // Copy the real query to the curs decleration.
return true; try
}
else
{ {
return (result != null && result.reallyResultSet()); // 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;
} }
/** version of execute which converts the query to a cursor. if (cursorBasedSql[i] != null)
*/ cursorBasedSql[i] += m_sqlFragments[i];
public boolean executeWithCursor() throws SQLException else
cursorBasedSql[i] = m_sqlFragments[i];
if (i == cursorBasedSql.length - 1)
{ {
if (isFunction && !returnTypeSet) // We have to be smart about adding the delimitting ";"
throw new PSQLException("postgresql.call.noreturntype"); if (m_sqlFragments[i].endsWith(";"))
if (isFunction) cursorBasedSql[i] += endCurs;
{ // set entry 1 to dummy entry.. else
m_binds[0] = ""; // dummy entry which ensured that no one overrode cursorBasedSql[i] += (";" + endCurs);
m_bindTypes[0] = PG_TEXT;
// and calls to setXXX (2,..) really went to first arg in a function call..
} }
else if (m_sqlFragments[i].indexOf(";") > -1)
// 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(); throw new IllegalStateException("multiple statements not "
if (rs != null) + "allowed with cursor based querys.");
rs.close(); }
} }
// I've pretty much ignored server prepared statements... can declare and prepare be // Make the cursor based query the one that will be used.
// used together? if (org.postgresql.Driver.logDebug)
// It's trivial to change this: you just have to resolve this issue org.postgresql.Driver.debug("using cursor based sql with cursor name " + statementName);
// 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) // Do all of this after exceptions have been thrown.
{ m_statementName = statementName;
if (m_sqlFragments[0].endsWith(";")) m_statementIsCursor = true;
m_sqlFragments[0] = cursDecl + m_sqlFragments[0] + endCurs; m_sqlFragments = cursorBasedSql;
else
m_sqlFragments[0] = cursDecl + m_sqlFragments[0] + ";" + endCurs;
} }
else catch (IllegalStateException e)
{ {
m_sqlFragments[0] = cursDecl + m_sqlFragments[0]; // Something went wrong generating the cursor based statement.
if (m_sqlFragments[m_sqlFragments.length - 1].endsWith(";")) if (org.postgresql.Driver.logDebug)
m_sqlFragments[m_sqlFragments.length - 1] += endCurs; org.postgresql.Driver.debug(e.getMessage());
else }
m_sqlFragments[m_sqlFragments.length - 1] += (";" + endCurs);
} }
// New in 7.1, pass Statement so that ExecSQL can customise to it
result = QueryExecutor.execute(m_sqlFragments, result = QueryExecutor.execute(m_sqlFragments,
m_binds, m_binds,
this); this);
...@@ -459,13 +436,9 @@ public abstract class AbstractJdbc1Statement implements BaseStatement ...@@ -459,13 +436,9 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
callResult = result.getObject(1); callResult = result.getObject(1);
int columnType = result.getMetaData().getColumnType(1); int columnType = result.getMetaData().getColumnType(1);
if (columnType != functionReturnType) if (columnType != functionReturnType)
{ throw new PSQLException ("postgresql.call.wrongrtntype",
Object[] arr = new Object[]{
{ "java.sql.Types=" + columnType, "java.sql.Types=" + columnType, "java.sql.Types=" + functionReturnType });
"java.sql.Types=" + functionReturnType
};
throw new PSQLException ("postgresql.call.wrongrtntype",arr);
}
result.close (); result.close ();
return true; return true;
} }
...@@ -475,7 +448,6 @@ public abstract class AbstractJdbc1Statement implements BaseStatement ...@@ -475,7 +448,6 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
} }
} }
/* /*
* setCursorName defines the SQL cursor name that will be used by * setCursorName defines the SQL cursor name that will be used by
* subsequent execute methods. This name can then be used in SQL * subsequent execute methods. This name can then be used in SQL
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment