Skip to content
Browse files

fix: "Received resultset tuples, but no field structure for them" whe…

…n bind failure happens on 5th execution of a statement

Current "describe" approach is to set "statement/portal described" flags at "send describe" phase.
It turns out the describe might be skipped (e.g. if bind fails), then query is marked as "described"
however no field information is known.

The fix is to reset "described" flags in "Ready For Query" for the queries in
pendingDescribeStatementQueue / pendingDescribePortalQueue

fixes #811
  • Loading branch information
vlsi committed Jan 2, 2018
1 parent 0d31d46 commit 082d00941ad5f8abf44a0785a6f086c106b3c746
@@ -53,6 +53,7 @@
import java.sql.Statement;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
@@ -1394,6 +1395,7 @@ private void sendSync() throws IOException {
pgStream.sendChar('S'); // Sync
pgStream.sendInteger4(4); // Length
// Below "add queues" are likely not required at all
pendingExecuteQueue.add(new ExecuteRequest(sync, null, true));
@@ -2331,8 +2333,19 @@ protected void processResults(ResultHandler handler, int flags) throws IOExcepti

pendingParseQueue.clear(); // No more ParseComplete messages expected.
pendingDescribeStatementQueue.clear(); // No more ParameterDescription messages expected.
pendingDescribePortalQueue.clear(); // No more RowDescription messages expected.
// Pending "describe" requests might be there in case of error
// If that is the case, reset "described" status, so the statement is properly
// described on next execution
while (!pendingDescribeStatementQueue.isEmpty()) {
DescribeRequest request = pendingDescribeStatementQueue.removeFirst();
LOGGER.log(Level.FINEST, " FE marking setStatementDescribed(false) for query {0}", request.query);
while (!pendingDescribePortalQueue.isEmpty()) {
SimpleQuery describePortalQuery = pendingDescribePortalQueue.removeFirst();
LOGGER.log(Level.FINEST, " FE marking setPortalDescribed(false) for query {0}", describePortalQuery);
pendingBindQueue.clear(); // No more BindComplete messages expected.
pendingExecuteQueue.clear(); // No more query executions expected.
@@ -51,6 +51,7 @@

@@ -12,8 +12,10 @@
import static org.junit.Assert.assertTrue;
import static;

import org.postgresql.PGStatement;
import org.postgresql.test.TestUtil;
import org.postgresql.test.jdbc2.BaseTest4;
import org.postgresql.util.PSQLState;

import org.junit.Test;
import org.junit.runner.RunWith;
@@ -86,7 +88,7 @@ public GeneratedKeysTest(ReturningInQuery returningInQuery, BinaryMode binaryMod
public void setUp() throws Exception {
TestUtil.createTempTable(con, "genkeys", "a serial, b text, c int");
TestUtil.createTempTable(con, "genkeys", "a serial, b varchar(5), c int");

@@ -450,5 +452,36 @@ public void selectWithGeneratedKeysViaNonPrepared() throws SQLException {

public void breakDescribeOnFirstServerPreparedExecution() throws SQLException {
// Test code is adapted from

PreparedStatement ps =
con.prepareStatement("insert into genkeys(b) values(?)" + returningClause,
ps.setString(1, "TEST");

// The below "prepareThreshold - 1" executions ensure that bind failure would happen
// exactly on prepareThreshold execution (the first one when server flips to server-prepared)
int prepareThreshold = ps.unwrap(PGStatement.class).getPrepareThreshold();
for (int i = 0; i < prepareThreshold - 1; i++) {
try {
// Send a value that's too long on the 5th request
ps.setString(1, "TESTTESTTEST");
} catch (SQLException e) {
// Expected error: org.postgresql.util.PSQLException: ERROR: value
// too long for type character varying(10)
if (!PSQLState.STRING_DATA_RIGHT_TRUNCATION.getState().equals(e.getSQLState())) {
throw e;
// Send a valid value on the next request
ps.setString(1, "TEST");


0 comments on commit 082d009

Please sign in to comment.