cockroachdb / cockroachdb/cockroach

pgwire: some results might be delivered with limited portal execution before an error is encountered

Open
#96,398 0 comments 0 reactions 0 assignees View on GitHub
A-pausable-portals C-bug T-sql-queries
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.