Skip to content

Commit ed98093

Browse files
committed
pgwire: show RowDescription for EXECUTE when needed
Release note (bug fix): Fixed a bug where we would not show the pgwire RowDescription for EXECUTE statements that were themselves prepared using the pgwire Parse command.
1 parent 004984b commit ed98093

File tree

3 files changed

+93
-4
lines changed

3 files changed

+93
-4
lines changed

pkg/sql/conn_executor.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3430,8 +3430,19 @@ func (ex *connExecutor) execCopyIn(
34303430

34313431
// stmtHasNoData returns true if describing a result of the input statement
34323432
// type should return NoData.
3433-
func stmtHasNoData(stmt tree.Statement) bool {
3434-
return stmt == nil || stmt.StatementReturnType() != tree.Rows
3433+
func stmtHasNoData(stmt tree.Statement, resultColumns colinfo.ResultColumns) bool {
3434+
if stmt == nil {
3435+
return true
3436+
}
3437+
if stmt.StatementReturnType() == tree.Rows {
3438+
return false
3439+
}
3440+
// The statement may not always return rows (e.g. EXECUTE), but if it does,
3441+
// resultColumns will be non-empty.
3442+
if stmt.StatementReturnType() == tree.Unknown {
3443+
return len(resultColumns) == 0
3444+
}
3445+
return true
34353446
}
34363447

34373448
// commitPrepStmtNamespace deallocates everything in

pkg/sql/conn_executor_prepare.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ func (ex *connExecutor) execDescribe(
632632
return retErr(sqlerrors.NewTransactionAbortedError("" /* customMsg */))
633633
}
634634
res.SetInferredTypes(ps.InferredTypes)
635-
if stmtHasNoData(ast) {
635+
if stmtHasNoData(ast, ps.Columns) {
636636
res.SetNoDataRowDescription()
637637
} else {
638638
res.SetPrepStmtOutput(ctx, ps.Columns)
@@ -661,7 +661,7 @@ func (ex *connExecutor) execDescribe(
661661
if isAbortedTxn && !ex.isAllowedInAbortedTxn(ast) {
662662
return retErr(sqlerrors.NewTransactionAbortedError("" /* customMsg */))
663663
}
664-
if stmtHasNoData(ast) {
664+
if stmtHasNoData(ast, portal.Stmt.Columns) {
665665
res.SetNoDataRowDescription()
666666
} else {
667667
res.SetPortalOutput(ctx, portal.Stmt.Columns, portal.OutFormats)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# This test verifies that the Parse/Bind/Describe/Execute protocol works with
2+
# prepared statements created with SQL-level PREPARE (so therefore must be
3+
# run with the EXECUTE statement).
4+
5+
send
6+
Query {"String": "DROP TABLE IF EXISTS t0;"}
7+
Query {"String": "CREATE TABLE t0(c0 INT8);"}
8+
----
9+
10+
until ignore=NoticeResponse
11+
ReadyForQuery
12+
ReadyForQuery
13+
----
14+
{"Type":"CommandComplete","CommandTag":"DROP TABLE"}
15+
{"Type":"ReadyForQuery","TxStatus":"I"}
16+
{"Type":"CommandComplete","CommandTag":"CREATE TABLE"}
17+
{"Type":"ReadyForQuery","TxStatus":"I"}
18+
19+
# Prepare INSERT statement with a SQL-level PREPARE.
20+
send
21+
Query {"String": "PREPARE insert_query (int8) AS INSERT INTO t0 VALUES ($1);"}
22+
----
23+
24+
until
25+
ReadyForQuery
26+
----
27+
{"Type":"CommandComplete","CommandTag":"PREPARE"}
28+
{"Type":"ReadyForQuery","TxStatus":"I"}
29+
30+
# Execute INSERT statement using Parse/Bind/Describe/Execute protocol. The
31+
# Describe should return NoData, since INSERT doesn't return rows.
32+
send
33+
Parse {"Name": "insert_query_stmt", "Query": "EXECUTE insert_query(1)"}
34+
Bind {"DestinationPortal": "insert_query_portal", "PreparedStatement": "insert_query_stmt"}
35+
Describe {"ObjectType": "P", "Name": "insert_query_portal"}
36+
Execute {"Portal": "insert_query_portal"}
37+
Sync
38+
----
39+
40+
until ignore_table_oids
41+
ReadyForQuery
42+
----
43+
{"Type":"ParseComplete"}
44+
{"Type":"BindComplete"}
45+
{"Type":"NoData"}
46+
{"Type":"CommandComplete","CommandTag":"INSERT 0 1"}
47+
{"Type":"ReadyForQuery","TxStatus":"I"}
48+
49+
# Prepare SELECT statement with a SQL-level PREPARE.
50+
send
51+
Query {"String": "PREPARE select_query (int8) AS SELECT * FROM t0 WHERE c0 = $1;"}
52+
----
53+
54+
until
55+
ReadyForQuery
56+
----
57+
{"Type":"CommandComplete","CommandTag":"PREPARE"}
58+
{"Type":"ReadyForQuery","TxStatus":"I"}
59+
60+
# Execute SELECT statement using Parse/Bind/Describe/Execute protocol. The
61+
# Describe should return a RowDescription, since SELECT returns rows.
62+
send
63+
Parse {"Name": "select_query_stmt", "Query": "EXECUTE select_query(1)"}
64+
Bind {"DestinationPortal": "select_query_portal", "PreparedStatement": "select_query_stmt"}
65+
Describe {"ObjectType": "P", "Name": "select_query_portal"}
66+
Execute {"Portal": "select_query_portal"}
67+
Sync
68+
----
69+
70+
until ignore_table_oids
71+
ReadyForQuery
72+
----
73+
{"Type":"ParseComplete"}
74+
{"Type":"BindComplete"}
75+
{"Type":"RowDescription","Fields":[{"Name":"c0","TableOID":0,"TableAttributeNumber":1,"DataTypeOID":20,"DataTypeSize":8,"TypeModifier":-1,"Format":0}]}
76+
{"Type":"DataRow","Values":[{"text":"1"}]}
77+
{"Type":"CommandComplete","CommandTag":"SELECT 1"}
78+
{"Type":"ReadyForQuery","TxStatus":"I"}

0 commit comments

Comments
 (0)