From 3ce6d75be4baa8d1e4b4f880da5f9ad68ab14e7f Mon Sep 17 00:00:00 2001 From: Dirkjan Bussink Date: Sun, 22 Mar 2026 14:58:55 +0100 Subject: [PATCH] Add failing test: batch scan corrupted in cache_statement mode sendBatchExtendedWithDescription passes eqb.ResultFormats to SendQueryStatement by reference. The next Build call reuses the backing array (reset sets len=0, append overwrites), corrupting the stored format codes for earlier queries. The server sends binary data (Bind was encoded correctly), but the local scan plan uses the corrupted text format code. Introduced in v5.9.0 by c3a17505 ("pgx batch uses SendQueryStatement"), part of "Skip Describe Portal for cached prepared statements reducing network round trips". The previous SendQueryPrepared path sent Describe Portal, so the server's RowDescription provided correct format codes regardless of the stored slice contents. Signed-off-by: Dirkjan Bussink --- batch_test.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/batch_test.go b/batch_test.go index 4dbe82a1e..dd90ff803 100644 --- a/batch_test.go +++ b/batch_test.go @@ -1267,3 +1267,35 @@ func ExampleConn_SendBatch() { // 3 // 5 } + +// TestSendBatchPreservesResultFormats verifies that each batched query retains +// its own result format codes. When the first query has a binary-preferred +// column (int4) and the second has all text columns, scanning the first +// query's int4 column must still work correctly. +func TestSendBatchPreservesResultFormats(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) + defer cancel() + + pgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) { + batch := &pgx.Batch{} + batch.Queue("SELECT 1::int4") + batch.Queue("SELECT 'hello'::text") + + br := conn.SendBatch(ctx, batch) + + var n int32 + err := br.QueryRow().Scan(&n) + require.NoError(t, err) + assert.Equal(t, int32(1), n) + + var s string + err = br.QueryRow().Scan(&s) + require.NoError(t, err) + assert.Equal(t, "hello", s) + + err = br.Close() + require.NoError(t, err) + }) +}