postgresql-jdbc.git / summary / log / commit / refs
commit 8bf62dffda73887f311f9f0e776a1ccb5f74459d
Author: Kris Jurka <books@ejurka.com>
Date: Tue Feb 01 07:27:55 2005 +0000
Implement the Describe Statement protocol message. Reallow untyped
nulls and get the backend to resolve the type for us (if necessary).
Also use this message and a new non-executing query execution to
implement PreparedStatement.getMetaData() for an unexecuted
statement and PreparedStatement.getParameterMetaData().
build.xml | 3 +-
org/postgresql/core/ParameterList.java | 9 +-
org/postgresql/core/QueryExecutor.java | 8 +-
org/postgresql/core/v2/FastpathParameterList.java | 6 +-
org/postgresql/core/v2/QueryExecutorImpl.java | 8 +-
org/postgresql/core/v2/SimpleParameterList.java | 6 +-
org/postgresql/core/v3/CompositeParameterList.java | 11 +-
org/postgresql/core/v3/QueryExecutorImpl.java | 127 ++++++++++++++++++---
org/postgresql/core/v3/SimpleParameterList.java | 30 ++++-
org/postgresql/core/v3/SimpleQuery.java | 7 +-
org/postgresql/jdbc2/AbstractJdbc2Statement.java | 30 +++--
org/postgresql/jdbc3/AbstractJdbc3Statement.java | 14 ++-
org/postgresql/jdbc3/PSQLParameterMetaData.java | 85 ++++++++++++++
.../test/jdbc2/PreparedStatementTest.java | 38 ++----
.../test/jdbc2/ResultSetMetaDataTest.java | 117 ++++++++++---------
org/postgresql/test/jdbc3/Jdbc3TestSuite.java | 5 +-
.../test/jdbc3/ParameterMetaDataTest.java | 85 ++++++++++++++
17 files changed, 464 insertions(+), 125 deletions(-)
diff --git a/build.xml b/build.xml
index 0a06818a..654d961d 100644
--- a/build.xml
+++ b/build.xml
@@ -8,7 +8,7 @@
This file now requires Ant 1.4.1. 2002-04-18
- $PostgreSQL: pgjdbc/build.xml,v 1.61 2005/01/17 09:52:13 jurka Exp $
+ $PostgreSQL: pgjdbc/build.xml,v 1.62 2005/01/21 11:53:26 jurka Exp $
-->
@@ -137,6 +137,7 @@
<!-- jdbc3 subpackage -->
<include name="${package}/jdbc3/Abstract*.java" if="jdbc3any" />
<include name="${package}/jdbc3/PSQLSavepoint.java" if="jdcb3any" />
+ <include name="${package}/jdbc3/PSQLParameterMetaData.java" if="jdbc3any" />
<include name="${package}/jdbc3/Jdbc3Array.java" if="jdbc3"/>
diff --git a/org/postgresql/core/ParameterList.java b/org/postgresql/core/ParameterList.java
index 9cfd2343..285b5972 100644
--- a/org/postgresql/core/ParameterList.java
+++ b/org/postgresql/core/ParameterList.java
@@ -4,7 +4,7 @@
* Copyright (c) 2004, Open Cloud Limited.
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/core/ParameterList.java,v 1.4 2004/11/09 08:44:40 jurka Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/core/ParameterList.java,v 1.5 2005/01/11 08:25:43 jurka Exp $
*
*-------------------------------------------------------------------------
*/
@@ -36,6 +36,13 @@ public interface ParameterList {
*/
int getParameterCount();
+
+ /**
+ * Return the oids of the parameters in this list. May be null for
+ * a ParameterList that does not support typing of parameters.
+ */
+ public int[] getTypeOIDs();
+
/**
* Binds an integer value to a parameter. The type of the parameter is
* implicitly 'int4'.
diff --git a/org/postgresql/core/QueryExecutor.java b/org/postgresql/core/QueryExecutor.java
index 6974cd84..b6fc3a2b 100644
--- a/org/postgresql/core/QueryExecutor.java
+++ b/org/postgresql/core/QueryExecutor.java
@@ -4,7 +4,7 @@
* Copyright (c) 2004, Open Cloud Limited.
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/core/QueryExecutor.java,v 1.38 2004/11/09 08:44:49 jurka Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/core/QueryExecutor.java,v 1.39 2005/01/11 08:25:43 jurka Exp $
*
*-------------------------------------------------------------------------
*/
@@ -77,6 +77,12 @@ public interface QueryExecutor {
*/
static int QUERY_SUPPRESS_BEGIN = 16;
+ /**
+ * Flag for query execution when we don't really want to execute, we just
+ * want to get the parameter metadata for the statement.
+ */
+ static int QUERY_DESCRIBE_ONLY = 32;
+
/**
* Execute a Query, passing results to a provided ResultHandler.
*
diff --git a/org/postgresql/core/v2/FastpathParameterList.java b/org/postgresql/core/v2/FastpathParameterList.java
index a7b2836e..fc63a7df 100644
--- a/org/postgresql/core/v2/FastpathParameterList.java
+++ b/org/postgresql/core/v2/FastpathParameterList.java
@@ -4,7 +4,7 @@
* Copyright (c) 2004, Open Cloud Limited.
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/core/v2/FastpathParameterList.java,v 1.5 2004/11/09 08:45:31 jurka Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/core/v2/FastpathParameterList.java,v 1.6 2005/01/11 08:25:43 jurka Exp $
*
*-------------------------------------------------------------------------
*/
@@ -38,6 +38,10 @@ class FastpathParameterList implements ParameterList {
return paramValues.length;
}
+ public int[] getTypeOIDs() {
+ return null;
+ }
+
public void setIntParameter(int index, int value) throws SQLException {
if (index < 1 || index > paramValues.length)
throw new PSQLException(GT.tr("The column index is out of range: {0}, number of columns: {1}.", new Object[]{new Integer(index), new Integer(paramValues.length)}), PSQLState.INVALID_PARAMETER_VALUE );
diff --git a/org/postgresql/core/v2/QueryExecutorImpl.java b/org/postgresql/core/v2/QueryExecutorImpl.java
index 23e798ad..61e79459 100644
--- a/org/postgresql/core/v2/QueryExecutorImpl.java
+++ b/org/postgresql/core/v2/QueryExecutorImpl.java
@@ -4,7 +4,7 @@
* Copyright (c) 2004, Open Cloud Limited.
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/core/v2/QueryExecutorImpl.java,v 1.8 2005/01/11 08:25:43 jurka Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/core/v2/QueryExecutorImpl.java,v 1.9 2005/01/14 01:20:15 oliver Exp $
*
*-------------------------------------------------------------------------
*/
@@ -275,6 +275,12 @@ public class QueryExecutorImpl implements QueryExecutor {
ResultHandler handler,
int maxRows, int flags) throws SQLException
{
+
+ // The V2 protocol has no support for retrieving metadata
+ // without executing the whole query.
+ if ((flags & QueryExecutor.QUERY_DESCRIBE_ONLY) != 0)
+ return;
+
if (parameters == null)
parameters = (SimpleParameterList)query.createParameterList();
diff --git a/org/postgresql/core/v2/SimpleParameterList.java b/org/postgresql/core/v2/SimpleParameterList.java
index 7f1076b4..5a0710fe 100644
--- a/org/postgresql/core/v2/SimpleParameterList.java
+++ b/org/postgresql/core/v2/SimpleParameterList.java
@@ -4,7 +4,7 @@
* Copyright (c) 2004, Open Cloud Limited.
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/core/v2/SimpleParameterList.java,v 1.4 2004/11/09 08:45:31 jurka Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/core/v2/SimpleParameterList.java,v 1.5 2005/01/11 08:25:43 jurka Exp $
*
*-------------------------------------------------------------------------
*/
@@ -36,6 +36,10 @@ class SimpleParameterList implements ParameterList {
return paramValues.length;
}
+ public int[] getTypeOIDs() {
+ return null;
+ }
+
public void setIntParameter(int index, int value) throws SQLException {
setLiteralParameter(index, "" + value, Oid.INT4);
}
diff --git a/org/postgresql/core/v3/CompositeParameterList.java b/org/postgresql/core/v3/CompositeParameterList.java
index b043aba0..0336b958 100644
--- a/org/postgresql/core/v3/CompositeParameterList.java
+++ b/org/postgresql/core/v3/CompositeParameterList.java
@@ -4,7 +4,7 @@
* Copyright (c) 2004, Open Cloud Limited.
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/core/v3/CompositeParameterList.java,v 1.5 2004/11/09 08:45:54 jurka Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/core/v3/CompositeParameterList.java,v 1.6 2005/01/11 08:25:44 jurka Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,6 +47,15 @@ class CompositeParameterList implements V3ParameterList {
return total;
}
+ public int[] getTypeOIDs() {
+ int oids[] = new int[total];
+ for (int i=0; i<offsets.length; i++) {
+ int subOids[] = subparams[i].getTypeOIDs();
+ System.arraycopy(subOids, 0, oids, offsets[i], subOids.length);
+ }
+ return oids;
+ }
+
public void setIntParameter(int index, int value) throws SQLException {
int sub = findSubParam(index);
subparams[sub].setIntParameter(index - offsets[sub], value);
diff --git a/org/postgresql/core/v3/QueryExecutorImpl.java b/org/postgresql/core/v3/QueryExecutorImpl.java
index 24455aec..945a9b80 100644
--- a/org/postgresql/core/v3/QueryExecutorImpl.java
+++ b/org/postgresql/core/v3/QueryExecutorImpl.java
@@ -4,7 +4,7 @@
* Copyright (c) 2004, Open Cloud Limited.
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/core/v3/QueryExecutorImpl.java,v 1.19 2005/01/14 01:20:16 oliver Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/core/v3/QueryExecutorImpl.java,v 1.20 2005/01/27 22:50:13 oliver Exp $
*
*-------------------------------------------------------------------------
*/
@@ -159,8 +159,11 @@ public class QueryExecutorImpl implements QueryExecutor {
if (parameters == null)
parameters = SimpleQuery.NO_PARAMETERS;
+ boolean describeOnly = (QUERY_DESCRIBE_ONLY & flags) != 0;
+
// Check parameters are all set..
- ((V3ParameterList)parameters).checkAllParametersSet();
+ if (!describeOnly)
+ ((V3ParameterList)parameters).checkAllParametersSet();
try
{
@@ -286,11 +289,14 @@ public class QueryExecutorImpl implements QueryExecutor {
", maxRows=" + maxRows + ", fetchSize=" + fetchSize + ", flags=" + flags);
}
+ boolean describeOnly = (QUERY_DESCRIBE_ONLY & flags) != 0;
// Check parameters and resolve OIDs.
- for (int i = 0; i < parameterLists.length; ++i)
- {
- if (parameterLists[i] != null)
- ((V3ParameterList)parameterLists[i]).checkAllParametersSet();
+ if (!describeOnly) {
+ for (int i = 0; i < parameterLists.length; ++i)
+ {
+ if (parameterLists[i] != null)
+ ((V3ParameterList)parameterLists[i]).checkAllParametersSet();
+ }
}
try
@@ -644,7 +650,8 @@ public class QueryExecutorImpl implements QueryExecutor {
// NB: Must clone the OID array, as it's a direct reference to
// the SimpleParameterList's internal array that might be modified
// under us.
- query.setStatementName(statementName, (int[])typeOIDs.clone());
+ query.setStatementName(statementName);
+ query.setStatementTypes((int[])typeOIDs.clone());
}
byte[] encodedStatementName = query.getEncodedStatementName();
@@ -826,7 +833,7 @@ public class QueryExecutorImpl implements QueryExecutor {
}
}
- private void sendDescribe(Portal portal) throws IOException {
+ private void sendDescribePortal(Portal portal) throws IOException {
//
// Send Describe.
//
@@ -849,6 +856,29 @@ public class QueryExecutorImpl implements QueryExecutor {
pgStream.SendChar(0); // end of portal name
}
+ private void sendDescribeStatement(SimpleQuery query, SimpleParameterList params, boolean describeOnly) throws IOException {
+ // Send Statement Describe
+
+ if (Driver.logDebug)
+ {
+ Driver.debug(" FE=> Describe(statement=" + query.getStatementName()+")");
+ }
+
+ byte[] encodedStatementName = query.getEncodedStatementName();
+
+ // Total size = 4 (size field) + 1 (describe type, 'S') + N + 1 (portal name)
+ int encodedSize = 4 + 1 + (encodedStatementName == null ? 0 : encodedStatementName.length) + 1;
+
+ pgStream.SendChar('D'); // Describe
+ pgStream.SendInteger4(encodedSize); // Message size
+ pgStream.SendChar('S'); // Describe (Statement);
+ if (encodedStatementName != null)
+ pgStream.Send(encodedStatementName); // Statement name
+ pgStream.SendChar(0); // end message
+
+ pendingDescribeStatementQueue.add(new Object[]{query, params, new Boolean(describeOnly)});
+ }
+
private void sendExecute(Query query, Portal portal, int limit) throws IOException {
//
// Send Execute.
@@ -934,8 +964,10 @@ public class QueryExecutorImpl implements QueryExecutor {
boolean noResults = (flags & QueryExecutor.QUERY_NO_RESULTS) != 0;
boolean noMeta = (flags & QueryExecutor.QUERY_NO_METADATA) != 0;
- boolean usePortal = (flags & QueryExecutor.QUERY_FORWARD_CURSOR) != 0 && !noResults && !noMeta && fetchSize > 0;
+ boolean describeOnly = (flags & QueryExecutor.QUERY_DESCRIBE_ONLY) != 0;
+ boolean usePortal = (flags & QueryExecutor.QUERY_FORWARD_CURSOR) != 0 && !noResults && !noMeta && fetchSize > 0 && !describeOnly;
boolean oneShot = (flags & QueryExecutor.QUERY_ONESHOT) != 0 && !usePortal;
+ boolean describeStatement = describeOnly || (params.hasUnresolvedTypes() && !oneShot);
// Work out how many rows to fetch in this pass.
@@ -959,6 +991,12 @@ public class QueryExecutorImpl implements QueryExecutor {
sendParse(query, params, oneShot);
+ if (describeStatement) {
+ sendDescribeStatement(query, params, describeOnly);
+ if (describeOnly)
+ return;
+ }
+
// Construct a new portal if needed.
Portal portal = null;
if (usePortal)
@@ -968,8 +1006,12 @@ public class QueryExecutorImpl implements QueryExecutor {
}
sendBind(query, params, portal);
- if (!noMeta)
- sendDescribe(portal);
+
+ // A statement describe will also output a RowDescription,
+ // so don't reissue it here if we've already done so.
+ //
+ if (!noMeta && !describeStatement)
+ sendDescribePortal(portal);
sendExecute(query, portal, rows);
}
@@ -1063,7 +1105,15 @@ public class QueryExecutorImpl implements QueryExecutor {
int c;
boolean endQuery = false;
+ // At the end of a command execution we have the CommandComplete
+ // message to tell us we're done, but with a describeOnly command
+ // we have no real flag to let us know we're done. We've got to
+ // look for the next RowDescription or NoData message and return
+ // from there.
+ boolean doneAfterRowDescNoData = false;
+
int parseIndex = 0;
+ int describeIndex = 0;
int bindIndex = 0;
int executeIndex = 0;
@@ -1086,6 +1136,32 @@ public class QueryExecutorImpl implements QueryExecutor {
registerParsedQuery(parsedQuery);
break;
+ case 't': // ParameterDescription
+ pgStream.ReceiveIntegerR(4); // len, discarded
+
+ if (Driver.logDebug)
+ Driver.debug(" <=BE ParameterDescription");
+
+ {
+ Object describeData[] = (Object[])pendingDescribeStatementQueue.get(describeIndex);
+ SimpleQuery query = (SimpleQuery)describeData[0];
+ SimpleParameterList params = (SimpleParameterList)describeData[1];
+ boolean describeOnly = ((Boolean)describeData[2]).booleanValue();
+
+ int numParams = pgStream.ReceiveIntegerR(2);
+ for (int i=1; i<=numParams; i++) {
+ int typeOid = pgStream.ReceiveIntegerR(4);
+ params.setResolvedType(i, typeOid);
+ }
+ query.setStatementTypes((int[])params.getTypeOIDs().clone());
+
+ if (describeOnly)
+ doneAfterRowDescNoData = true;
+ else
+ describeIndex++;
+ }
+ break;
+
case '2': // Bind Complete (response to Bind)
pgStream.ReceiveIntegerR(4); // len, discarded
@@ -1106,6 +1182,16 @@ public class QueryExecutorImpl implements QueryExecutor {
pgStream.ReceiveIntegerR(4); // len, discarded
if (Driver.logDebug)
Driver.debug(" <=BE NoData");
+
+ if (doneAfterRowDescNoData) {
+ Object describeData[] = (Object[])pendingDescribeStatementQueue.get(describeIndex++);
+ Query currentQuery = (Query)describeData[0];
+
+ if (fields != null || tuples != null)
+ { // There was a resultset.
+ handler.handleResultRows(currentQuery, fields, tuples, null);
+ }
+ }
break;
case 's': // Portal Suspended (end of Execute)
@@ -1131,6 +1217,8 @@ public class QueryExecutorImpl implements QueryExecutor {
// Handle status.
String status = receiveCommandStatus();
+ doneAfterRowDescNoData = false;
+
{
Object[] executeData = (Object[])pendingExecuteQueue.get(executeIndex++);
Query currentQuery = (Query)executeData[0];
@@ -1220,6 +1308,15 @@ public class QueryExecutorImpl implements QueryExecutor {
case 'T': // Row Description (response to Describe)
fields = receiveFields();
tuples = new Vector();
+ if (doneAfterRowDescNoData) {
+ Object describeData[] = (Object[])pendingDescribeStatementQueue.get(describeIndex++);
+ Query currentQuery = (Query)describeData[0];
+
+ if (fields != null || tuples != null)
+ { // There was a resultset.
+ handler.handleResultRows(currentQuery, fields, tuples, null);
+ }
+ }
break;
case 'Z': // Ready For Query (eventual response to Sync)
@@ -1233,9 +1330,10 @@ public class QueryExecutorImpl implements QueryExecutor {
failedQuery.unprepare();
}
- pendingParseQueue.clear(); // No more ParseComplete messages expected.
- pendingBindQueue.clear(); // No more BindComplete messages expected.
- pendingExecuteQueue.clear(); // No more query executions expected.
+ pendingParseQueue.clear(); // No more ParseComplete messages expected.
+ pendingDescribeStatementQueue.clear(); // No more ParameterDescription messages expected.
+ pendingBindQueue.clear(); // No more BindComplete messages expected.
+ pendingExecuteQueue.clear(); // No more query executions expected.
break;
case 'G': // CopyInResponse
@@ -1448,6 +1546,7 @@ public class QueryExecutorImpl implements QueryExecutor {
private final ArrayList pendingParseQueue = new ArrayList(); // list of SimpleQuery instances
private final ArrayList pendingBindQueue = new ArrayList(); // list of Portal instances
private final ArrayList pendingExecuteQueue = new ArrayList(); // list of {SimpleQuery,Portal} object arrays
+ private final ArrayList pendingDescribeStatementQueue = new ArrayList(); // list of {SimpleQuery, SimpleParameterList, Boolean} object arrays
private long nextUniqueID = 1;
private final ProtocolConnectionImpl protoConnection;
diff --git a/org/postgresql/core/v3/SimpleParameterList.java b/org/postgresql/core/v3/SimpleParameterList.java
index a8abbc79..ac2399bf 100644
--- a/org/postgresql/core/v3/SimpleParameterList.java
+++ b/org/postgresql/core/v3/SimpleParameterList.java
@@ -4,7 +4,7 @@
* Copyright (c) 2004, Open Cloud Limited.
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/core/v3/SimpleParameterList.java,v 1.6 2005/01/11 08:25:44 jurka Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/core/v3/SimpleParameterList.java,v 1.7 2005/01/27 22:50:14 oliver Exp $
*
*-------------------------------------------------------------------------
*/
@@ -41,6 +41,13 @@ class SimpleParameterList implements V3ParameterList {
encoded[index] = null;
paramValues[index] = value;
+
+ // If we are setting something to null, don't overwrite our existing type
+ // for it. We don't need the correct type info to send NULL and we
+ // don't want to overwrite and require a reparse.
+ if (oid == Oid.INVALID && paramTypes[index] != Oid.INVALID)
+ return;
+
paramTypes[index] = oid;
}
@@ -111,6 +118,10 @@ class SimpleParameterList implements V3ParameterList {
pgStream.SendStream(wrapper.getStream(), wrapper.getLength());
}
+ public int[] getTypeOIDs() {
+ return paramTypes;
+ }
+
//
// Package-private V3 accessors
//
@@ -119,8 +130,21 @@ class SimpleParameterList implements V3ParameterList {
return paramTypes[index -1];
}
- int[] getTypeOIDs() {
- return paramTypes;
+ boolean hasUnresolvedTypes() {
+ for (int i=0; i<paramTypes.length; i++) {
+ if (paramTypes[i] == Oid.INVALID)
+ return true;
+ }
+ return false;
+ }
+
+ void setResolvedType(int index, int oid) {
+ // only allow overwriting an unknown value
+ if (paramTypes[index-1] == Oid.INVALID) {
+ paramTypes[index-1] = oid;
+ } else if (paramTypes[index-1] != oid) {
+ throw new IllegalArgumentException("Can't change resolved type for param: " + index + " from " + paramTypes[index] + " to " + oid);
+ }
}
boolean isNull(int index) {
diff --git a/org/postgresql/core/v3/SimpleQuery.java b/org/postgresql/core/v3/SimpleQuery.java
index c52a1e28..9ebdc168 100644
--- a/org/postgresql/core/v3/SimpleQuery.java
+++ b/org/postgresql/core/v3/SimpleQuery.java
@@ -4,7 +4,7 @@
* Copyright (c) 2004, Open Cloud Limited.
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/core/v3/SimpleQuery.java,v 1.6 2005/01/27 20:59:02 oliver Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/core/v3/SimpleQuery.java,v 1.7 2005/01/27 22:50:14 oliver Exp $
*
*-------------------------------------------------------------------------
*/
@@ -70,9 +70,12 @@ class SimpleQuery implements V3Query {
return fragments;
}
- void setStatementName(String statementName, int[] paramTypes) {
+ void setStatementName(String statementName) {
this.statementName = statementName;
this.encodedStatementName = Utils.encodeUTF8(statementName);
+ }
+
+ void setStatementTypes(int[] paramTypes) {
this.preparedTypes = paramTypes;
}
diff --git a/org/postgresql/jdbc2/AbstractJdbc2Statement.java b/org/postgresql/jdbc2/AbstractJdbc2Statement.java
index 49fe815c..844f0490 100644
--- a/org/postgresql/jdbc2/AbstractJdbc2Statement.java
+++ b/org/postgresql/jdbc2/AbstractJdbc2Statement.java
@@ -3,7 +3,7 @@
* Copyright (c) 2004-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java,v 1.66 2005/01/25 06:21:21 jurka Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java,v 1.67 2005/01/27 11:30:41 jurka Exp $
*
*-------------------------------------------------------------------------
*/
@@ -996,8 +996,8 @@ public abstract class AbstractJdbc2Statement implements BaseStatement
}
break;
case Types.OTHER:
- // We cannot determine an appropriate OID in this case.
- throw new PSQLException(GT.tr("setNull(i,Types.OTHER) is not supported. Instead, use setObject(i,obj,Types.OTHER) where ((PGobject)obj).getValue()==null."), PSQLState.INVALID_PARAMETER_TYPE);
+ oid = Oid.INVALID;
+ break;
default:
// Bad Types value.
throw new PSQLException(GT.tr("Unknown Types value."), PSQLState.INVALID_PARAMETER_TYPE);
@@ -1634,12 +1634,8 @@ public abstract class AbstractJdbc2Statement implements BaseStatement
{
checkClosed();
if (x == null)
- {
- // We cannot determine an appropriate OID in this case.
- throw new PSQLException(GT.tr("setObject(i,null) is not supported. Instead, use setNull(i,type) or setObject(i,null,type)"), PSQLState.INVALID_PARAMETER_TYPE);
- }
-
- if (x instanceof String)
+ setNull(parameterIndex, Types.OTHER);
+ else if (x instanceof String)
setString(parameterIndex, (String)x);
else if (x instanceof BigDecimal)
setBigDecimal(parameterIndex, (BigDecimal)x);
@@ -2532,10 +2528,24 @@ public abstract class AbstractJdbc2Statement implements BaseStatement
{
checkClosed();
ResultSet rs = getResultSet();
+
+ if (rs == null) {
+ // OK, we haven't executed it yet, we've got to go to the backend
+ // for more info. We send the full query, but just don't
+ // execute it.
+
+ int flags = QueryExecutor.QUERY_ONESHOT | QueryExecutor.QUERY_DESCRIBE_ONLY | QueryExecutor.QUERY_SUPPRESS_BEGIN;
+ StatementResultHandler handler = new StatementResultHandler();
+ connection.getQueryExecutor().execute(preparedQuery, preparedParameters, handler, 0, 0, flags);
+ ResultWrapper wrapper = handler.getResults();
+ if (wrapper != null) {
+ rs = wrapper.getResultSet();
+ }
+ }
+
if (rs != null)
return rs.getMetaData();
- // Does anyone really know what this method does?
return null;
}
diff --git a/org/postgresql/jdbc3/AbstractJdbc3Statement.java b/org/postgresql/jdbc3/AbstractJdbc3Statement.java
index de730c81..ecbad08e 100644
--- a/org/postgresql/jdbc3/AbstractJdbc3Statement.java
+++ b/org/postgresql/jdbc3/AbstractJdbc3Statement.java
@@ -3,7 +3,7 @@
* Copyright (c) 2004-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/jdbc3/AbstractJdbc3Statement.java,v 1.14 2004/12/11 04:13:39 jurka Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/jdbc3/AbstractJdbc3Statement.java,v 1.15 2005/01/11 08:25:46 jurka Exp $
*
*-------------------------------------------------------------------------
*/
@@ -16,6 +16,7 @@ import java.util.Vector;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
+import org.postgresql.core.QueryExecutor;
import org.postgresql.core.Field;
import org.postgresql.core.Oid;
import org.postgresql.util.GT;
@@ -363,7 +364,16 @@ public abstract class AbstractJdbc3Statement extends org.postgresql.jdbc2.Abstra
*/
public ParameterMetaData getParameterMetaData() throws SQLException
{
- throw org.postgresql.Driver.notImplemented();
+ int flags = QueryExecutor.QUERY_ONESHOT | QueryExecutor.QUERY_DESCRIBE_ONLY | QueryExecutor.QUERY_SUPPRESS_BEGIN;
+ StatementResultHandler handler = new StatementResultHandler();
+ connection.getQueryExecutor().execute(preparedQuery, preparedParameters, handler, 0, 0, flags);
+
+ int oids[] = preparedParameters.getTypeOIDs();
+ if (oids != null)
+ return new PSQLParameterMetaData(connection, oids);
+
+ return null;
+
}
/**
diff --git a/org/postgresql/jdbc3/PSQLParameterMetaData.java b/org/postgresql/jdbc3/PSQLParameterMetaData.java
new file mode 100755
index 00000000..b3e456ad
--- /dev/null
+++ b/org/postgresql/jdbc3/PSQLParameterMetaData.java
@@ -0,0 +1,85 @@
+/*-------------------------------------------------------------------------
+*
+* Copyright (c) 2005, PostgreSQL Global Development Group
+*
+* IDENTIFICATION
+* $PostgreSQL$
+*
+*-------------------------------------------------------------------------
+*/
+package org.postgresql.jdbc3;
+
+import java.sql.SQLException;
+import java.sql.ParameterMetaData;
+
+import org.postgresql.util.GT;
+import org.postgresql.util.PSQLState;
+import org.postgresql.util.PSQLException;
+import org.postgresql.core.BaseConnection;
+
+public class PSQLParameterMetaData implements ParameterMetaData {
+
+ private final BaseConnection _connection;
+ private final int _oids[];
+
+ public PSQLParameterMetaData(BaseConnection connection, int oids[]) {
+ _connection = connection;
+ _oids = oids;
+ }
+
+ public String getParameterClassName(int param) throws SQLException {
+ checkParamIndex(param);
+ return null;
+ }
+
+ public int getParameterCount() {
+ return _oids.length;
+ }
+
+ // For now report all parameters as inputs. CallableStatements may
+ // have one output, but ignore that for now.
+ public int getParameterMode(int param) throws SQLException {
+ checkParamIndex(param);
+ return ParameterMetaData.parameterModeIn;
+ }
+
+ public int getParameterType(int param) throws SQLException {
+ checkParamIndex(param);
+ return _connection.getSQLType(_oids[param-1]);
+ }
+
+ public String getParameterTypeName(int param) throws SQLException {
+ checkParamIndex(param);
+ return _connection.getPGType(_oids[param-1]);
+ }
+
+ // we don't know this
+ public int getPrecision(int param) throws SQLException {
+ checkParamIndex(param);
+ return 0;
+ }
+
+ // we don't know this
+ public int getScale(int param) throws SQLException {
+ checkParamIndex(param);
+ return 0;
+ }
+
+ // we can't tell anything about nullability
+ public int isNullable(int param) throws SQLException {
+ checkParamIndex(param);
+ return ParameterMetaData.parameterNullableUnknown;
+ }
+
+ // pg doesn't have unsigned numbers
+ public boolean isSigned(int param) throws SQLException {
+ checkParamIndex(param);
+ return true;
+ }
+
+ private void checkParamIndex(int param) throws PSQLException {
+ if (param < 1 || param > _oids.length)
+ throw new PSQLException(GT.tr("The parameter index is out of range: {0}, number of parameters: {1}.", new Object[]{new Integer(param), new Integer(_oids.length)}), PSQLState.INVALID_PARAMETER_VALUE);
+ }
+
+}
diff --git a/org/postgresql/test/jdbc2/PreparedStatementTest.java b/org/postgresql/test/jdbc2/PreparedStatementTest.java
index be007402..d59aad46 100644
--- a/org/postgresql/test/jdbc2/PreparedStatementTest.java
+++ b/org/postgresql/test/jdbc2/PreparedStatementTest.java
@@ -3,7 +3,7 @@
* Copyright (c) 2004-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/test/jdbc2/PreparedStatementTest.java,v 1.8 2004/11/09 08:54:47 jurka Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/test/jdbc2/PreparedStatementTest.java,v 1.9 2005/01/11 08:25:48 jurka Exp $
*
*-------------------------------------------------------------------------
*/
@@ -229,35 +229,17 @@ public class PreparedStatementTest extends TestCase
pstmt.setObject(1, dummy, Types.OTHER);
pstmt.executeUpdate();
- // not valid: setObject() with no type info
- try
- {
- pstmt.setObject(1, null);
- fail("Expected an exception on setObject(1,null)");
- }
- catch (SQLException e)
- {
- }
+ // setObject() with no type info
+ pstmt.setObject(1, null);
+ pstmt.executeUpdate();
- // not valid: setObject() with insufficient type info
- try
- {
- pstmt.setObject(1, null, Types.OTHER);
- fail("Expected an exception on setObject(1,null,Types.OTHER)");
- }
- catch (SQLException e)
- {
- }
+ // setObject() with insufficient type info
+ pstmt.setObject(1, null, Types.OTHER);
+ pstmt.executeUpdate();
- // not valid: setNull() with insufficient type info
- try
- {
- pstmt.setNull(1, Types.OTHER);
- fail("Expected an exception on setNull(1,Types.OTHER)");
- }
- catch (SQLException e)
- {
- }
+ // setNull() with insufficient type info
+ pstmt.setNull(1, Types.OTHER);
+ pstmt.executeUpdate();
pstmt.close();
}
diff --git a/org/postgresql/test/jdbc2/ResultSetMetaDataTest.java b/org/postgresql/test/jdbc2/ResultSetMetaDataTest.java
index 0e377437..b0990c4b 100644
--- a/org/postgresql/test/jdbc2/ResultSetMetaDataTest.java
+++ b/org/postgresql/test/jdbc2/ResultSetMetaDataTest.java
@@ -3,7 +3,7 @@
* Copyright (c) 2004-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/test/jdbc2/ResultSetMetaDataTest.java,v 1.9 2005/01/05 00:53:08 jurka Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/test/jdbc2/ResultSetMetaDataTest.java,v 1.10 2005/01/11 08:25:48 jurka Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,10 +18,6 @@ public class ResultSetMetaDataTest extends TestCase
{
private Connection conn;
- private Statement stmt;
- private ResultSet rs;
- private ResultSetMetaData rsmd;
- private PGResultSetMetaData pgrsmd;
public ResultSetMetaDataTest(String name)
{
@@ -32,13 +28,6 @@ public class ResultSetMetaDataTest extends TestCase
{
conn = TestUtil.openDB();
TestUtil.createTable(conn, "rsmd1", "a int primary key, b text, c decimal(10,2)", true);
-
- stmt = conn.createStatement();
- rs = stmt.executeQuery("SELECT a,b,c,a+c as total,oid,b as d FROM rsmd1");
- rsmd = rs.getMetaData();
- pgrsmd = (PGResultSetMetaData)rsmd;
-
-
TestUtil.createTable(conn, "timetest", "tm time(3), tmtz timetz, ts timestamp without time zone, tstz timestamp(6) with time zone");
TestUtil.dropSequence( conn, "serialtest_a_seq");
@@ -53,25 +42,34 @@ public class ResultSetMetaDataTest extends TestCase
TestUtil.dropTable(conn, "serialtest");
TestUtil.dropSequence( conn, "serialtest_a_seq");
TestUtil.dropSequence( conn, "serialtest_b_seq");
+ TestUtil.closeDB(conn);
+ }
+
+ public void testStandardResultSet() throws SQLException {
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT a,b,c,a+c as total,oid,b as d FROM rsmd1");
+ runStandardTests(rs.getMetaData());
rs.close();
stmt.close();
- TestUtil.closeDB(conn);
- rsmd = null;
- rs = null;
- stmt = null;
- conn = null;
}
- public void testGetColumnCount() throws SQLException {
- assertEquals(6, rsmd.getColumnCount());
+ public void testPreparedResultSet() throws SQLException {
+ if (!TestUtil.haveMinimumServerVersion(conn, "7.4"))
+ return;
+
+ PreparedStatement pstmt = conn.prepareStatement("SELECT a,b,c,a+c as total,oid,b as d FROM rsmd1 WHERE b = ?");
+ runStandardTests(pstmt.getMetaData());
+ pstmt.close();
}
- public void testGetColumnLabel() throws SQLException {
+ private void runStandardTests(ResultSetMetaData rsmd) throws SQLException {
+ PGResultSetMetaData pgrsmd = (PGResultSetMetaData)rsmd;
+
+ assertEquals(6, rsmd.getColumnCount());
+
assertEquals("a", rsmd.getColumnLabel(1));
assertEquals("total", rsmd.getColumnLabel(4));
- }
- public void testGetColumnName() throws SQLException {
assertEquals("a", rsmd.getColumnName(1));
assertEquals("oid", rsmd.getColumnName(5));
if (TestUtil.haveMinimumServerVersion(conn, "7.4"))
@@ -79,27 +77,17 @@ public class ResultSetMetaDataTest extends TestCase
assertEquals("", pgrsmd.getBaseColumnName(4));
assertEquals("b", pgrsmd.getBaseColumnName(6));
}
- }
- public void testGetColumnType() throws SQLException {
assertEquals(Types.INTEGER, rsmd.getColumnType(1));
assertEquals(Types.VARCHAR, rsmd.getColumnType(2));
- }
- public void testGetColumnTypeName() throws SQLException {
assertEquals("int4", rsmd.getColumnTypeName(1));
assertEquals("text", rsmd.getColumnTypeName(2));
- }
- public void testGetPrecision() throws SQLException {
assertEquals(10, rsmd.getPrecision(3));
- }
- public void testGetScale() throws SQLException {
assertEquals(2, rsmd.getScale(3));
- }
- public void testGetSchemaName() throws SQLException {
assertEquals("", rsmd.getSchemaName(1));
assertEquals("", rsmd.getSchemaName(4));
if (TestUtil.haveMinimumServerVersion(conn, "7.4"))
@@ -107,9 +95,7 @@ public class ResultSetMetaDataTest extends TestCase
assertEquals("public", pgrsmd.getBaseSchemaName(1));
assertEquals("", pgrsmd.getBaseSchemaName(4));
}
- }
- public void testGetTableName() throws SQLException {
assertEquals("", rsmd.getTableName(1));
assertEquals("", rsmd.getTableName(4));
if (TestUtil.haveMinimumServerVersion(conn, "7.4"))
@@ -117,9 +103,7 @@ public class ResultSetMetaDataTest extends TestCase
assertEquals("rsmd1", pgrsmd.getBaseTableName(1));
assertEquals("", pgrsmd.getBaseTableName(4));
}
- }
- public void testIsNullable() throws SQLException {
if (TestUtil.haveMinimumServerVersion(conn, "7.4"))
{
assertEquals(ResultSetMetaData.columnNoNulls, rsmd.isNullable(1));
@@ -132,6 +116,25 @@ public class ResultSetMetaDataTest extends TestCase
}
}
+ // verify that a prepared update statement returns no metadata and doesn't execute.
+ public void testPreparedUpdate() throws SQLException {
+ PreparedStatement pstmt = conn.prepareStatement("INSERT INTO rsmd1(a,b) VALUES(?,?)");
+ pstmt.setInt(1,1);
+ pstmt.setString(2,"hello");
+ ResultSetMetaData rsmd = pstmt.getMetaData();
+ assertNull(rsmd);
+ pstmt.close();
+
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM rsmd1");
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ rs.close();
+ stmt.close();
+ }
+
+
+
public void testDatabaseMetaDataNames() throws SQLException {
DatabaseMetaData databaseMetaData = conn.getMetaData();
ResultSet resultSet = databaseMetaData.getTableTypes();
@@ -142,41 +145,41 @@ public class ResultSetMetaDataTest extends TestCase
}
public void testTimestampInfo() throws SQLException {
- Statement stmt2 = conn.createStatement();
- ResultSet rs2 = stmt2.executeQuery("SELECT tm, tmtz, ts, tstz FROM timetest");
- ResultSetMetaData rsmd2 = rs2.getMetaData();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT tm, tmtz, ts, tstz FROM timetest");
+ ResultSetMetaData rsmd = rs.getMetaData();
// For reference:
// TestUtil.createTable(conn, "timetest", "tm time(3), tmtz timetz, ts timestamp without time zone, tstz timestamp(6) with time zone");
- assertEquals(3, rsmd2.getScale(1));
- assertEquals(6, rsmd2.getScale(2));
- assertEquals(6, rsmd2.getScale(3));
- assertEquals(6, rsmd2.getScale(4));
+ assertEquals(3, rsmd.getScale(1));
+ assertEquals(6, rsmd.getScale(2));
+ assertEquals(6, rsmd.getScale(3));
+ assertEquals(6, rsmd.getScale(4));
- assertEquals(13, rsmd2.getColumnDisplaySize(1));
- assertEquals(21, rsmd2.getColumnDisplaySize(2));
- assertEquals(26, rsmd2.getColumnDisplaySize(3));
- assertEquals(32, rsmd2.getColumnDisplaySize(4));
+ assertEquals(13, rsmd.getColumnDisplaySize(1));
+ assertEquals(21, rsmd.getColumnDisplaySize(2));
+ assertEquals(26, rsmd.getColumnDisplaySize(3));
+ assertEquals(32, rsmd.getColumnDisplaySize(4));
- rs2.close();
- stmt2.close();
+ rs.close();
+ stmt.close();
}
public void testIsAutoIncrement() throws SQLException {
- Statement stmt2 = conn.createStatement();
- ResultSet rs2 = stmt2.executeQuery("SELECT c,b,a FROM serialtest");
- ResultSetMetaData rsmd2 = rs2.getMetaData();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT c,b,a FROM serialtest");
+ ResultSetMetaData rsmd = rs.getMetaData();
- assertTrue(!rsmd2.isAutoIncrement(1));
+ assertTrue(!rsmd.isAutoIncrement(1));
if (TestUtil.haveMinimumServerVersion(conn, "7.4"))
{
- assertTrue(rsmd2.isAutoIncrement(2));
- assertTrue(rsmd2.isAutoIncrement(3));
+ assertTrue(rsmd.isAutoIncrement(2));
+ assertTrue(rsmd.isAutoIncrement(3));
}
- rs2.close();
- stmt2.close();
+ rs.close();
+ stmt.close();
}
}
diff --git a/org/postgresql/test/jdbc3/Jdbc3TestSuite.java b/org/postgresql/test/jdbc3/Jdbc3TestSuite.java
index 13ef3c0f..62789c57 100644
--- a/org/postgresql/test/jdbc3/Jdbc3TestSuite.java
+++ b/org/postgresql/test/jdbc3/Jdbc3TestSuite.java
@@ -3,7 +3,7 @@
* Copyright (c) 2004-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
-* $PostgreSQL: pgjdbc/org/postgresql/test/jdbc3/Jdbc3TestSuite.java,v 1.10 2004/12/11 04:13:46 jurka Exp $
+* $PostgreSQL: pgjdbc/org/postgresql/test/jdbc3/Jdbc3TestSuite.java,v 1.11 2005/01/11 08:25:49 jurka Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,7 +25,8 @@ public class Jdbc3TestSuite extends TestSuite
TestSuite suite = new TestSuite();
suite.addTestSuite(Jdbc3SavepointTest.class);
suite.addTestSuite(TypesTest.class);
- suite.addTestSuite(ResultSetTest.class);
+ suite.addTestSuite(ResultSetTest.class);
+ suite.addTestSuite(ParameterMetaDataTest.class);
return suite;
}
}
diff --git a/org/postgresql/test/jdbc3/ParameterMetaDataTest.java b/org/postgresql/test/jdbc3/ParameterMetaDataTest.java
new file mode 100644
index 00000000..8b57a908
--- /dev/null
+++ b/org/postgresql/test/jdbc3/ParameterMetaDataTest.java
@@ -0,0 +1,85 @@
+/*-------------------------------------------------------------------------
+*
+* Copyright (c) 2005, PostgreSQL Global Development Group
+*
+* IDENTIFICATION
+* $PostgreSQL$
+*
+*-------------------------------------------------------------------------
+*/
+package org.postgresql.test.jdbc3;
+
+import java.sql.*;
+import junit.framework.TestCase;
+import org.postgresql.test.TestUtil;
+
+public class ParameterMetaDataTest extends TestCase {
+
+ private Connection _conn;
+
+ public ParameterMetaDataTest(String name) {
+ super(name);
+ }
+
+ protected void setUp() throws SQLException {
+ _conn = TestUtil.openDB();
+ TestUtil.createTable(_conn, "parametertest", "a int4, b float8, c text");
+ }
+
+ protected void tearDown() throws SQLException {
+ TestUtil.dropTable(_conn, "parametertest");
+ TestUtil.closeDB(_conn);
+ }
+
+ public void testParameterMD() throws SQLException {
+ if (!TestUtil.haveMinimumServerVersion(_conn, "7.4"))
+ return;
+
+ PreparedStatement pstmt = _conn.prepareStatement("SELECT a FROM parametertest WHERE b = ? AND c = ?");
+ ParameterMetaData pmd = pstmt.getParameterMetaData();
+
+ assertEquals(2, pmd.getParameterCount());
+ assertEquals(Types.DOUBLE, pmd.getParameterType(1));
+ assertEquals("float8", pmd.getParameterTypeName(1));
+ assertEquals(Types.VARCHAR, pmd.getParameterType(2));
+ assertEquals("text", pmd.getParameterTypeName(2));
+
+ pstmt.close();
+ }
+
+ public void testFailsOnBadIndex() throws SQLException {
+ if (!TestUtil.haveMinimumServerVersion(_conn, "7.4"))
+ return;
+
+ PreparedStatement pstmt = _conn.prepareStatement("SELECT a FROM parametertest WHERE b = ? AND c = ?");
+ ParameterMetaData pmd = pstmt.getParameterMetaData();
+ try {
+ pmd.getParameterType(0);
+ fail("Can't get parameter for index < 1.");
+ } catch (SQLException sqle) { }
+ try {
+ pmd.getParameterType(3);
+ fail("Can't get parameter for index 3 with only two parameters.");
+ } catch (SQLException sqle) { }
+ }
+
+ // Make sure we work when mashing two queries into a single statement.
+ public void testMultiStatement() throws SQLException {
+ if (!TestUtil.haveMinimumServerVersion(_conn, "7.4"))
+ return;
+
+ PreparedStatement pstmt = _conn.prepareStatement("SELECT a FROM parametertest WHERE b = ? AND c = ? ; SELECT b FROM parametertest WHERE a = ?");
+ ParameterMetaData pmd = pstmt.getParameterMetaData();
+
+ assertEquals(3, pmd.getParameterCount());
+ assertEquals(Types.DOUBLE, pmd.getParameterType(1));
+ assertEquals("float8", pmd.getParameterTypeName(1));
+ assertEquals(Types.VARCHAR, pmd.getParameterType(2));
+ assertEquals("text", pmd.getParameterTypeName(2));
+ assertEquals(Types.INTEGER, pmd.getParameterType(3));
+ assertEquals("int4", pmd.getParameterTypeName(3));
+
+ pstmt.close();
+
+ }
+}
[parent: 76c8e5bb272e]