postgresql-jdbc.git / summary / log / commit / refs

commit    a6bd36faaedc779f932fa76f52bab9550f0fcd6d
Author:   Craig Ringer <craig@2ndquadrant.com>
Date:     Mon Mar 16 07:14:12 2015 +0000

    Always Describe a query in a batch that returns generated keys
    
    This is a workaround for issue #267, where we unprepare a statement
    in a batch when the parameter types change but we expect to still
    know the field types in the statement we just unprepared.
    
    It's much more complicated than that, see
    https://github.com/pgjdbc/pgjdbc/issues/267 for the gory details.


org/postgresql/core/QueryExecutor.java | 7 ++++++ org/postgresql/core/v3/QueryExecutorImpl.java | 29 +++++++++++++++--------- org/postgresql/jdbc2/AbstractJdbc2Statement.java | 6 +++++ 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/org/postgresql/core/QueryExecutor.java b/org/postgresql/core/QueryExecutor.java index 10bd5025..4f860b1c 100644 --- a/org/postgresql/core/QueryExecutor.java +++ b/org/postgresql/core/QueryExecutor.java @@ -88,6 +88,13 @@ public interface QueryExecutor { * both the ResultSet and associated update count from the command status. */ static int QUERY_BOTH_ROWS_AND_STATUS = 64; + + /** + * Force this query to be described at each execution. This is done in + * pipelined batches where we might need to detect mismatched result + * types. + */ + static int QUERY_FORCE_DESCRIBE_PORTAL = 128; /** * Flag to disable batch execution when we expect results (generated keys) diff --git a/org/postgresql/core/v3/QueryExecutorImpl.java b/org/postgresql/core/v3/QueryExecutorImpl.java index 735a227f..76d5e34d 100644 --- a/org/postgresql/core/v3/QueryExecutorImpl.java +++ b/org/postgresql/core/v3/QueryExecutorImpl.java @@ -1624,6 +1624,7 @@ public class QueryExecutorImpl implements QueryExecutor { boolean usePortal = (flags & QueryExecutor.QUERY_FORWARD_CURSOR) != 0 && !noResults && !noMeta && fetchSize > 0 && !describeOnly; boolean oneShot = (flags & QueryExecutor.QUERY_ONESHOT) != 0 && !usePortal; boolean noBinaryTransfer = (flags & QUERY_NO_BINARY_TRANSFER) != 0; + boolean forceDescribePortal = (flags & QUERY_FORCE_DESCRIBE_PORTAL) != 0; // Work out how many rows to fetch in this pass. @@ -1692,18 +1693,24 @@ public class QueryExecutorImpl implements QueryExecutor { /* * don't send describe if we already have cached the row description * from previous executions - * - * XXX Clearing the fields / unpreparing the query is incorrect, see - * bug #267. We might clear the cached fields in a later execution - * of this query if the bind parameter types change, but we're - * assuming here that they'll still be valid when we come to process - * the results of this query, so we don't send a new describe here. - * We re-describe after the fields are cleared, but the result of - * that gets processed after processing the results from earlier - * executions that we didn't describe because we didn't think we had - * to. + * + * XXX Clearing the fields / unpreparing the query (in sendParse) is + * incorrect, see bug #267. We might clear the cached fields in a + * later execution of this query if the bind parameter types change, + * but we're assuming here that they'll still be valid when we come + * to process the results of this query, so we don't send a new + * describe here. We re-describe after the fields are cleared, but + * the result of that gets processed after processing the results + * from earlier executions that we didn't describe because we didn't + * think we had to. + * + * To work around this, force a Describe at each execution in + * batches where this can be a problem. It won't cause more round + * trips so the performance impact is low, and it'll ensure that the + * field information available when we decoded the results. This + * is undeniably a hack, but there aren't many good alternatives. */ - if (query.getFields() == null) { + if (query.getFields() == null || forceDescribePortal) { sendDescribePortal(query, portal); } } diff --git a/org/postgresql/jdbc2/AbstractJdbc2Statement.java b/org/postgresql/jdbc2/AbstractJdbc2Statement.java index e801cf51..adbd65e1 100644 --- a/org/postgresql/jdbc2/AbstractJdbc2Statement.java +++ b/org/postgresql/jdbc2/AbstractJdbc2Statement.java @@ -2905,6 +2905,12 @@ public abstract class AbstractJdbc2Statement implements BaseStatement // we'll be able to queue up before we risk a deadlock. // (see v3.QueryExecutorImpl's MAX_BUFFERED_RECV_BYTES) preDescribe = wantsGeneratedKeysAlways && !queries[0].isStatementDescribed(); + /* + * It's also necessary to force a Describe on the first execution of the + * new statement, even though we already described it, to work around + * bug #267. + */ + flags |= QueryExecutor.QUERY_FORCE_DESCRIBE_PORTAL; } if (connection.getAutoCommit()) [parent: 4d2b0466e677]