diff -X /tmp/exclude -Nacr jdbc/org/postgresql/PGConnection.java jdbc.preparethreshold/org/postgresql/PGConnection.java
*** jdbc/org/postgresql/PGConnection.java	Tue Dec  2 15:13:55 2003
--- jdbc.preparethreshold/org/postgresql/PGConnection.java	Tue Dec  2 21:03:47 2003
***************
*** 63,69 ****
           * @see org.postgresql.util.PGobject
           */
          public void addDataType(String type, String name);
! 
  
  	/** @deprecated */
  	public Encoding getEncoding() throws SQLException;
--- 63,85 ----
           * @see org.postgresql.util.PGobject
           */
          public void addDataType(String type, String name);
! 	
! 	/**
! 	 * Set the default statement reuse threshold before enabling server-side
! 	 * prepare. See {@link org.postgresql.PGStatement#setPrepareThreshold(int)} for 
! 	 * details.
! 	 *
! 	 * @param threshold the new threshold
! 	 */
! 	public void setPrepareThreshold(int threshold);
! 	
! 	/**
! 	 * Get the default server-side prepare reuse threstold for statements created
! 	 * from this connection.
! 	 *
! 	 * @return the current threshold
! 	 */
! 	public int getPrepareThreshold();
  
  	/** @deprecated */
  	public Encoding getEncoding() throws SQLException;
diff -X /tmp/exclude -Nacr jdbc/org/postgresql/PGStatement.java jdbc.preparethreshold/org/postgresql/PGStatement.java
*** jdbc/org/postgresql/PGStatement.java	Tue Dec  2 15:13:55 2003
--- jdbc.preparethreshold/org/postgresql/PGStatement.java	Tue Dec  2 21:03:47 2003
***************
*** 30,43 ****
  	/**
  	 * Turn on the use of prepared statements in the server (server side
  	 * prepared statements are unrelated to jdbc PreparedStatements)
     	 * @since 7.3
  	 */
  	public void setUseServerPrepare(boolean flag) throws SQLException;
  
  	/**
! 	 * Is this statement using server side prepared statements
!    	 * @since 7.3
  	 */
  	public boolean isUseServerPrepare();
  
  }
--- 30,75 ----
  	/**
  	 * Turn on the use of prepared statements in the server (server side
  	 * prepared statements are unrelated to jdbc PreparedStatements)
+ 	 * As of 7.5, this method is equivalent to setPrepareThreshold(1).
+ 	 *
+ 	 * @deprecated As of 7.5, replaced by {@link #setPrepareThreshold(int)}
     	 * @since 7.3
  	 */
  	public void setUseServerPrepare(boolean flag) throws SQLException;
  
  	/**
! 	 * Checks if this statement will be executed as a server-prepared statement.
! 	 * A return value of true indicates that the next execution of the
! 	 * statement, with an unchanged query, via a PreparedStatement variant of
! 	 * execute(), will be done as a server-prepared statement.
! 	 *
! 	 * @return true if the next reuse of this statement will use a server-prepared statement
  	 */
  	public boolean isUseServerPrepare();
  
+ 	/**
+ 	 * Sets the reuse threshold for using server-prepared statements.
+ 	 *
+ 	 * If threshold is a non-zero value N, the Nth and subsequent
+ 	 * uses of a statement for the same query will use server-side
+ 	 * prepare. A query is currently only considered the "same" if the statement
+ 	 * is a PreparedStatement, and none of the base Statement query execution methods
+ 	 * that take an explicit query string have been called.
+ 	 *
+ 	 * If threshold is zero, server-side prepare will not be used.
+ 	 *
+ 	 * @param threshold the new threshold for this statement
+ 	 * @throws SQLException if an exception occurs while changing the threshold
+ 	 * @since 7.5
+ 	 */
+ 	public void setPrepareThreshold(int threshold) throws SQLException;
+ 	
+ 	/**
+ 	 * Gets the server-side prepare reuse threshold in use for this statement.
+ 	 *
+ 	 * @return the current threshold
+ 	 * @see #setPrepareThreshold(int)
+ 	 * @since 7.5
+ 	 */
+ 	public int getPrepareThreshold();
  }
diff -X /tmp/exclude -Nacr jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java jdbc.preparethreshold/org/postgresql/jdbc1/AbstractJdbc1Connection.java
*** jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java	Tue Dec  2 15:13:55 2003
--- jdbc.preparethreshold/org/postgresql/jdbc1/AbstractJdbc1Connection.java	Tue Dec  2 21:03:47 2003
***************
*** 53,58 ****
--- 53,59 ----
  	protected boolean PG_STATUS;
  	protected String compatible;
  	protected boolean useSSL;
+ 	protected int prepareThreshold;
  
  	// The PID an cancellation key we get from the backend process
  	protected int pid;
***************
*** 65,71 ****
  	 */
  	private Encoding encoding = Encoding.defaultEncoding();
  
! 	private String dbVersionNumber;
  
  	public boolean CONNECTION_OK = true;
  	public boolean CONNECTION_BAD = false;
--- 66,72 ----
  	 */
  	private Encoding encoding = Encoding.defaultEncoding();
  
! 	private String dbVersionNumber = "0.0"; // Dummy version until we really know.
  
  	public boolean CONNECTION_OK = true;
  	public boolean CONNECTION_BAD = false;
***************
*** 182,187 ****
--- 183,196 ----
  			enableDriverManagerLogging();
  		}
  
+ 		prepareThreshold = 0;
+ 		try {
+ 			prepareThreshold = Integer.parseInt(info.getProperty("prepareThreshold", "0"));
+ 		} catch (Exception e) {}
+ 		
+ 		if (prepareThreshold < 0)
+ 			prepareThreshold = 0;
+ 		
  		//Print out the driver version number
  		if (Driver.logInfo)
  			Driver.info(Driver.getVersion());
***************
*** 189,194 ****
--- 198,204 ----
  			Driver.debug("    ssl = " + useSSL);
  			Driver.debug("    compatible = " + compatible);
  			Driver.debug("    loglevel = " + l_logLevel);
+ 			Driver.debug("    prepare threshold = " + prepareThreshold);
  		}
  
  		// Now make the initial connection
***************
*** 1838,1843 ****
--- 1848,1861 ----
  		m_notifications = null;
  		return l_return;
  	}
+ 
+ 	public int getPrepareThreshold() {
+ 		return prepareThreshold;
+ 	}
+ 	
+ 	public void setPrepareThreshold(int newThreshold) {
+ 		this.prepareThreshold = (newThreshold <= 0 ? 0 : newThreshold);
+ 	}	
  }
  
  
diff -X /tmp/exclude -Nacr jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java jdbc.preparethreshold/org/postgresql/jdbc1/AbstractJdbc1Statement.java
*** jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java	Tue Dec  2 15:13:55 2003
--- jdbc.preparethreshold/org/postgresql/jdbc1/AbstractJdbc1Statement.java	Tue Dec  2 21:11:42 2003
***************
*** 82,88 ****
  	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;
  
      // m_preparedCount is used for naming of auto-cursors and must
      // be synchronized so that multiple threads using the same
--- 82,89 ----
  	private short m_isSingleSelect = UNKNOWN;      // Is the query a single SELECT?
  	private short m_isSingleStatement = UNKNOWN;   // Is the query a single statement?
  
! 	private int m_prepareThreshold;                // Reuse threshold to enable use of PREPARE
! 	private int m_useCount = 1;                    // Number of times this statement has been reused (plus 1)
  
      // m_preparedCount is used for naming of auto-cursors and must
      // be synchronized so that multiple threads using the same
***************
*** 110,123 ****
  
  	public abstract BaseResultSet createResultSet(Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException;
  
! 	public AbstractJdbc1Statement (BaseConnection connection)
  	{
  		this.connection = connection;
  	}
  
  	public AbstractJdbc1Statement (BaseConnection connection, String p_sql) throws SQLException
  	{
! 		this.connection = connection;
  		parseSqlStmt(p_sql);  // this allows Callable stmt to override
  	}
  
--- 111,125 ----
  
  	public abstract BaseResultSet createResultSet(Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException;
  
! 	public AbstractJdbc1Statement (BaseConnection connection) throws SQLException
  	{
  		this.connection = connection;
+ 		setPrepareThreshold(connection.getPrepareThreshold());
  	}
  
  	public AbstractJdbc1Statement (BaseConnection connection, String p_sql) throws SQLException
  	{
! 		this(connection);
  		parseSqlStmt(p_sql);  // this allows Callable stmt to override
  	}
  
***************
*** 199,204 ****
--- 201,207 ----
  		m_cursorName = null; // automatically closed at end of txn anyway
  		m_executeSqlFragments = null;
  		m_isSingleStatement = m_isSingleSelect = m_isSingleDML = UNKNOWN;
+ 		m_useCount = 1;
  	}
    
  	/*
***************
*** 510,515 ****
--- 513,519 ----
  		// Get the actual query fragments to run (might be a transformed version of
  		// the original fragments)
  		String[] fragments = getQueryFragments();
+ 		++m_useCount; // We used this statement once more.
  
  		// New in 7.1, pass Statement so that ExecSQL can customise to it                
  		result = QueryExecutor.execute(fragments,
***************
*** 2154,2178 ****
  			throw new PSQLException("postgresql.call.noinout", PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL);
  	}
  
  
! 
!     public void setUseServerPrepare(boolean flag) throws SQLException {
!         //Server side prepared statements were introduced in 7.3
!         if (connection.haveMinimumServerVersion("7.3")) {
! 			if (m_useServerPrepare != flag)
! 				deallocateQuery();
! 			m_useServerPrepare = flag;
! 		} else {
! 			//This is a pre 7.3 server so no op this method
! 			//which means we will never turn on the flag to use server
! 			//prepared statements and thus regular processing will continue
! 		}
  	}
! 
! 	public boolean isUseServerPrepare()
! 	{
! 		return m_useServerPrepare;
  	}
  
  	private java.sql.Date dateFromString (String s) throws SQLException
  	{
--- 2158,2186 ----
  			throw new PSQLException("postgresql.call.noinout", PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL);
  	}
  
+ 	public void setPrepareThreshold(int newThreshold) throws SQLException {
+ 		if (newThreshold < 0)
+ 			newThreshold = 0;
+ 		
+ 		if (!connection.haveMinimumServerVersion("7.3"))
+ 			newThreshold = 0; // PREPARE was only introduced in 7.3.
  
! 		this.m_prepareThreshold = newThreshold;
! 		if (m_statementName != null && !isUseServerPrepare()) // Turning off a previously prepared statement.
! 			deallocateQuery();
  	}
! 		
! 	public int getPrepareThreshold() {
! 		return m_prepareThreshold;
  	}
+ 	
+ 	public void setUseServerPrepare(boolean flag) throws SQLException {
+ 		setPrepareThreshold(flag ? 1 : 0);
+ 	}
+ 	
+ 	public boolean isUseServerPrepare() {
+ 		return m_prepareThreshold > 0 && m_useCount >= m_prepareThreshold;
+ 	}	
  
  	private java.sql.Date dateFromString (String s) throws SQLException
  	{
diff -X /tmp/exclude -Nacr jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java jdbc.preparethreshold/org/postgresql/jdbc2/AbstractJdbc2Statement.java
*** jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java	Tue Dec  2 15:13:55 2003
--- jdbc.preparethreshold/org/postgresql/jdbc2/AbstractJdbc2Statement.java	Tue Dec  2 21:03:47 2003
***************
*** 22,28 ****
  	protected int resultsettype;		 // the resultset type to return
  	protected int concurrency;		 // is it updateable or not?
  
! 	public AbstractJdbc2Statement (AbstractJdbc2Connection c)
  	{
  		super(c);
  		resultsettype = ResultSet.TYPE_SCROLL_INSENSITIVE;
--- 22,28 ----
  	protected int resultsettype;		 // the resultset type to return
  	protected int concurrency;		 // is it updateable or not?
  
! 	public AbstractJdbc2Statement (AbstractJdbc2Connection c) throws SQLException
  	{
  		super(c);
  		resultsettype = ResultSet.TYPE_SCROLL_INSENSITIVE;
diff -X /tmp/exclude -Nacr jdbc/org/postgresql/jdbc2/optional/BaseDataSource.java jdbc.preparethreshold/org/postgresql/jdbc2/optional/BaseDataSource.java
*** jdbc/org/postgresql/jdbc2/optional/BaseDataSource.java	Wed Sep 25 19:01:30 2002
--- jdbc.preparethreshold/org/postgresql/jdbc2/optional/BaseDataSource.java	Tue Dec  2 21:03:47 2003
***************
*** 36,41 ****
--- 36,42 ----
  	private String user;
  	private String password;
  	private int portNumber;
+ 	private int prepareThreshold;
  
  	/**
  	 * Gets a connection to the PostgreSQL database.  The database is identified by the
***************
*** 226,236 ****
  	}
  
  	/**
  	 * Generates a DriverManager URL from the other properties supplied.
  	 */
  	private String getUrl()
  	{
! 		return "jdbc:postgresql://" + serverName + (portNumber == 0 ? "" : ":" + portNumber) + "/" + databaseName;
  	}
  
      /**
--- 227,260 ----
  	}
  
  	/**
+ 	 * Sets the default threshold for enabling server-side prepare.
+ 	 * See {@link org.postgresql.PGConnection#setPrepareThreshold(int)} for details.
+ 	 *
+ 	 * @param count the number of times a statement object must be reused before server-side
+ 	 *   prepare is enabled.
+ 	 */
+ 	public void setPrepareThreshold(int count)
+ 	{
+ 		this.prepareThreshold = count;
+ 	}
+ 	
+ 	/**
+ 	 * Gets the default threshold for enabling server-side prepare.
+ 	 *
+ 	 * @see #setServerPrepareThreshold(int)
+ 	 */
+ 	public int getPrepareThreshold()
+ 	{
+ 		return prepareThreshold;
+ 	}
+ 	
+ 	/**
  	 * Generates a DriverManager URL from the other properties supplied.
  	 */
  	private String getUrl()
  	{
! 		return "jdbc:postgresql://" + serverName + (portNumber == 0 ? "" : ":" + portNumber) + "/" + databaseName + 
! 			(prepareThreshold == 0 ? "" : "?prepareThreshold=" + prepareThreshold);
  	}
  
      /**
***************
*** 259,264 ****
--- 283,290 ----
  		{
  			ref.add(new StringRefAddr("password", password));
  		}
+ 		if (prepareThreshold != 0)
+ 			ref.add(new StringRefAddr("prepareThreshold", Integer.toString(prepareThreshold)));
  		return ref;
  	}
  
diff -X /tmp/exclude -Nacr jdbc/org/postgresql/jdbc2/optional/PGObjectFactory.java jdbc.preparethreshold/org/postgresql/jdbc2/optional/PGObjectFactory.java
*** jdbc/org/postgresql/jdbc2/optional/PGObjectFactory.java	Wed Sep 25 19:01:30 2002
--- jdbc.preparethreshold/org/postgresql/jdbc2/optional/PGObjectFactory.java	Tue Dec  2 21:03:47 2003
***************
*** 92,97 ****
--- 92,102 ----
  		}
  		ds.setServerName(getProperty(ref, "serverName"));
  		ds.setUser(getProperty(ref, "user"));
+ 
+ 		String prepareThreshold = getProperty(ref, "prepareThreshold");
+ 		if (prepareThreshold != null)
+ 			ds.setPrepareThreshold(Integer.parseInt(prepareThreshold));
+ 
  		return ds;
  	}
  
diff -X /tmp/exclude -Nacr jdbc/org/postgresql/jdbc3/AbstractJdbc3Statement.java jdbc.preparethreshold/org/postgresql/jdbc3/AbstractJdbc3Statement.java
*** jdbc/org/postgresql/jdbc3/AbstractJdbc3Statement.java	Tue Dec  2 15:13:57 2003
--- jdbc.preparethreshold/org/postgresql/jdbc3/AbstractJdbc3Statement.java	Tue Dec  2 21:03:47 2003
***************
*** 13,19 ****
  public abstract class AbstractJdbc3Statement extends org.postgresql.jdbc2.AbstractJdbc2Statement
  {
  
! 	public AbstractJdbc3Statement (AbstractJdbc3Connection c)
  	{
  		super(c);
  	}
--- 13,19 ----
  public abstract class AbstractJdbc3Statement extends org.postgresql.jdbc2.AbstractJdbc2Statement
  {
  
! 	public AbstractJdbc3Statement (AbstractJdbc3Connection c) throws SQLException
  	{
  		super(c);
  	}
diff -X /tmp/exclude -Nacr jdbc/org/postgresql/jdbc3/Jdbc3Statement.java jdbc.preparethreshold/org/postgresql/jdbc3/Jdbc3Statement.java
*** jdbc/org/postgresql/jdbc3/Jdbc3Statement.java	Tue Dec  2 15:13:57 2003
--- jdbc.preparethreshold/org/postgresql/jdbc3/Jdbc3Statement.java	Tue Dec  2 21:03:47 2003
***************
*** 15,21 ****
  public class Jdbc3Statement extends org.postgresql.jdbc3.AbstractJdbc3Statement implements java.sql.Statement
  {
  
! 	public Jdbc3Statement (Jdbc3Connection c)
  	{
  		super(c);
  	}
--- 15,21 ----
  public class Jdbc3Statement extends org.postgresql.jdbc3.AbstractJdbc3Statement implements java.sql.Statement
  {
  
! 	public Jdbc3Statement (Jdbc3Connection c) throws SQLException
  	{
  		super(c);
  	}