cockroachdb / cockroachdb/cockroach
pgwire: some results might be delivered with limited portal execution before an error is encountered
- Dominant language
- Go
- Stars
- 32.5k
- Forks
- 4.1k
- PR merge metrics
- PR metrics pending
Description
Found this while discussing multiple active portals with @ZhouXing19. In CRDB we execute some checks (e.g. foreign key checks) as "post-queries" (i.e. after the main query). If we execute a mutation statement with the `RETURNING` clause as a limited portal, before the mutation is fully consumed, then we will deliver partial results to the client without running into a possible violation later.
Consider the following PGTest example. First, the setup part:
```
send
Query {"String": "BEGIN"}
Query {"String": "DROP TABLE IF EXISTS child"}
Query {"String": "DROP TABLE IF EXISTS parent"}
Query {"String": "CREATE TABLE parent (id INT PRIMARY KEY)"}
Query {"String": "INSERT INTO parent VALUES (1)"}
Query {"String": "CREATE TABLE child (id INT PRIMARY KEY, parent_id INT NOT NULL, FOREIGN KEY (parent_id) REFERENCES parent(id))"}
Sync
----
until
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"BEGIN"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"CommandComplete","CommandTag":"DROP TABLE"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"CommandComplete","CommandTag":"DROP TABLE"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"CommandComplete","CommandTag":"CREATE TABLE"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"CommandComplete","CommandTag":"INSERT 0 1"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"CommandComplete","CommandTag":"CREATE TABLE"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"ReadyForQuery","TxStatus":"T"}
```
Now here is the expected output for executing INSERT ... RETURNING via limited portal (we get this behavior with "insert fast path" enabled):
```
send
Parse {"Name": "p", "Query": "INSERT INTO child VALUES (1, 1), (2, 2) RETURNING (id)"}
Bind {"DestinationPortal": "p", "PreparedStatement": "p"}
Execute {"Portal": "p", "MaxRows": 1}
Sync
----
until keepErrMessage
ErrorResponse
ReadyForQuery
----
{"Type":"ParseComplete"}
{"Type":"BindComplete"}
{"Type":"ErrorResponse","Code":"23503","Message":"insert on table \"child\" violates foreign key constraint \"child_parent_id_fkey\"","ConstraintName":"child_parent_id_fkey"}
{"Type":"ReadyForQuery","TxStatus":"E"}
```
However, if we disable the insert fast path, then this part succeeds in returning a single row:
```
send
Query {"String": "SET enable_insert_fast_path = false"}
Parse {"Name": "p", "Query": "INSERT INTO child VALUES (1, 1), (2, 2) RETURNING (id)"}
Bind {"DestinationPortal": "p", "PreparedStatement": "p"}
Execute {"Portal": "p", "MaxRows": 1}
Sync
----
until
ReadyForQuery
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"SET"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"ParseComplete"}
{"Type":"BindComplete"}
{"Type":"DataRow","Values":[{"text":"1"}]}
{"Type":"PortalSuspended"}
{"Type":"ReadyForQuery","TxStatus":"T"}
```
and the error will occur only later (when we either exhaust the portal or attempt to commit it).
Postgres returns an error before pushing out any rows, but I'm not sure how important this deviation is.
Jira issue: CRDB-24102
Epic CRDB-25183
Contributor guide
Assessment
This issue has not been assessed yet.