# This file tests behavior that differs from Postgres wrt portal
# handling. That is, the -rewrite flag, when used with Postgres, will
# produce different results than Cockroach.

only crdb
----

##############################################################################
# Deviations from Postgres in how we handle portals' suspension and attempts #
# to execute exhausted portals.                                              #
##############################################################################

# Execute a statement of "Rows" statement types. We will execute an UPDATE
# twice and then will use SELECT to verify that the UPDATE only happened once.
# Note that this test case is in this file rather than in 'portals' because we
# deviate from Postgres in the command tag (Postgres returns "UPDATE 3").

send
Query {"String": "DROP TABLE IF EXISTS foo; CREATE TABLE foo (id INT8); INSERT INTO foo (id) VALUES (1), (2), (3)"}
Query {"String": "BEGIN"}
Parse {"Name": "rows_stmt", "Query": "UPDATE foo SET id = id * 10 WHERE true RETURNING id"}
Bind {"DestinationPortal": "rows_portal", "PreparedStatement": "rows_stmt"}
Execute {"Portal": "rows_portal", "MaxRows": 2}
Sync
----

until
ReadyForQuery
ReadyForQuery
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"DROP TABLE"}
{"Type":"CommandComplete","CommandTag":"CREATE TABLE"}
{"Type":"CommandComplete","CommandTag":"INSERT 0 3"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"CommandComplete","CommandTag":"BEGIN"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"ParseComplete"}
{"Type":"BindComplete"}
{"Type":"DataRow","Values":[{"text":"10"}]}
{"Type":"DataRow","Values":[{"text":"20"}]}
{"Type":"PortalSuspended"}
{"Type":"ReadyForQuery","TxStatus":"T"}

send
Execute {"Portal": "rows_portal"}
Sync
----

until
ReadyForQuery
----
{"Type":"DataRow","Values":[{"text":"30"}]}
{"Type":"CommandComplete","CommandTag":"UPDATE 1"}
{"Type":"ReadyForQuery","TxStatus":"T"}

send
Execute {"Portal": "rows_portal"}
Sync
----

until
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"UPDATE 0"}
{"Type":"ReadyForQuery","TxStatus":"T"}

send
Execute {"Portal": "rows_portal"}
Sync
----

until
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"UPDATE 0"}
{"Type":"ReadyForQuery","TxStatus":"T"}

send
Query {"String": "SELECT * FROM foo ORDER BY id"}
----

until ignore=RowDescription
ReadyForQuery
----
{"Type":"DataRow","Values":[{"text":"10"}]}
{"Type":"DataRow","Values":[{"text":"20"}]}
{"Type":"DataRow","Values":[{"text":"30"}]}
{"Type":"CommandComplete","CommandTag":"SELECT 3"}
{"Type":"ReadyForQuery","TxStatus":"T"}

send
Query {"String": "COMMIT"}
----

until
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"COMMIT"}
{"Type":"ReadyForQuery","TxStatus":"I"}

# When attempting to execute portals of statements that don't return row sets
# for the second and consequent times, Postgres returns an error whereas we
# silently do nothing.

# Execute a statement of "DDL" statement type. We will try to execute DROP ROLE
# twice, but on the second attempt we silently do nothing.

send
Query {"String": "CREATE ROLE foo"}
----

until
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"CREATE ROLE"}
{"Type":"ReadyForQuery","TxStatus":"I"}

send
Query {"String": "BEGIN"}
Parse {"Name": "ddl_stmt", "Query": "DROP ROLE foo"}
Bind {"DestinationPortal": "ddl_portal", "PreparedStatement": "ddl_stmt"}
Execute {"Portal": "ddl_portal", "MaxRows": 1}
Sync
----

until
ReadyForQuery
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"BEGIN"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"ParseComplete"}
{"Type":"BindComplete"}
{"Type":"CommandComplete","CommandTag":"DROP ROLE"}
{"Type":"ReadyForQuery","TxStatus":"T"}

send
Execute {"Portal": "ddl_portal"}
Sync
----

until
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"DROP ROLE"}
{"Type":"ReadyForQuery","TxStatus":"T"}

send
Query {"String": "COMMIT"}
----

until
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"COMMIT"}
{"Type":"ReadyForQuery","TxStatus":"I"}

# Execute a statement of "RowsAffected" statement type. We will try to execute
# an UPDATE twice and will confirm that it was executed only once.

send
Query {"String": "DROP TABLE IF EXISTS foo"}
Query {"String": "CREATE TABLE foo (id INT8)"}
Query {"String": "INSERT INTO foo (id) VALUES (1), (2), (3)"}
Query {"String": "BEGIN"}
Parse {"Name": "rows_affected_stmt", "Query": "UPDATE foo SET id = id * 10 WHERE true"}
Bind {"DestinationPortal": "rows_affected_portal", "PreparedStatement": "rows_affected_stmt"}
Execute {"Portal": "rows_affected_portal", "MaxRows": 1}
Sync
----

until
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"DROP TABLE"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"CommandComplete","CommandTag":"CREATE TABLE"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"CommandComplete","CommandTag":"INSERT 0 3"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"CommandComplete","CommandTag":"BEGIN"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"ParseComplete"}
{"Type":"BindComplete"}
{"Type":"CommandComplete","CommandTag":"UPDATE 3"}
{"Type":"ReadyForQuery","TxStatus":"T"}

send
Execute {"Portal": "rows_affected_portal"}
Sync
----

until
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"UPDATE 0"}
{"Type":"ReadyForQuery","TxStatus":"T"}

send
Query {"String": "SELECT * FROM foo ORDER BY id"}
Query {"String": "COMMIT"}
Sync
----

until ignore=RowDescription
ReadyForQuery
ReadyForQuery
ReadyForQuery
----
{"Type":"DataRow","Values":[{"text":"10"}]}
{"Type":"DataRow","Values":[{"text":"20"}]}
{"Type":"DataRow","Values":[{"text":"30"}]}
{"Type":"CommandComplete","CommandTag":"SELECT 3"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"CommandComplete","CommandTag":"COMMIT"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"ReadyForQuery","TxStatus":"I"}


subtest bind_before_update
# In pg13.3, for portal bind before the update, the execution of the portal gives
# value before the update. While CRDB gives the updated value. This may means we
# need to modify the how the bind statement's execution.

send
Query {"String": "BEGIN"}
Query {"String": "CREATE TABLE mytable (x int)"}
Query {"String": "INSERT INTO mytable VALUES (1),(2),(3)"}
Parse {"Name": "q3", "Query": "SELECT * FROM mytable"}
Bind {"DestinationPortal": "p3", "PreparedStatement": "q3"}
Query {"String": "UPDATE mytable SET x = 10"}
----

until
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"BEGIN"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"CommandComplete","CommandTag":"CREATE TABLE"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"CommandComplete","CommandTag":"INSERT 0 3"}
{"Type":"ReadyForQuery","TxStatus":"T"}
{"Type":"ParseComplete"}
{"Type":"BindComplete"}
{"Type":"CommandComplete","CommandTag":"UPDATE 3"}
{"Type":"ReadyForQuery","TxStatus":"T"}

send
Execute {"Portal": "p3", "MaxRows": 2}
Close {"ObjectType": "P", "Name": "p3"}
Sync
----

# PG shows DataRow 1 and 2 here.

until
ReadyForQuery
----
{"Type":"DataRow","Values":[{"text":"10"}]}
{"Type":"DataRow","Values":[{"text":"10"}]}
{"Type":"PortalSuspended"}
{"Type":"CloseComplete"}
{"Type":"ReadyForQuery","TxStatus":"T"}


send
Parse {"Name": "q4", "Query": "SELECT * FROM mytable"}
Bind {"DestinationPortal": "p4", "PreparedStatement": "q4"}
Execute {"Portal": "p4", "MaxRows": 2}
Sync
----


until
ReadyForQuery
----
{"Type":"ParseComplete"}
{"Type":"BindComplete"}
{"Type":"DataRow","Values":[{"text":"10"}]}
{"Type":"DataRow","Values":[{"text":"10"}]}
{"Type":"PortalSuspended"}
{"Type":"ReadyForQuery","TxStatus":"T"}

send
Query {"String": "COMMIT"}
Sync
----

until
ReadyForQuery
----
{"Type":"CommandComplete","CommandTag":"COMMIT"}
{"Type":"ReadyForQuery","TxStatus":"I"}


subtest end

subtest functions_not_supported

# We don't support UDFs in pausable portals since we don't allow mutations, and
# UDFs may contain mutations. The following test results in a duplicate key
# violation in postgres.

send
Query {"String": "SET multiple_active_portals_enabled = true"}
----

send
Query {"String": "DROP TABLE IF EXISTS xy;"}
Query {"String": "DROP FUNCTION IF EXISTS f;"}
Query {"String": "DROP FUNCTION IF EXISTS g;"}
Query {"String": "DEALLOCATE ALL;"}
Query {"String": "CREATE TABLE xy (x INT PRIMARY KEY, y INT);"}
Query {"String": "CREATE FUNCTION f() RETURNS SETOF RECORD LANGUAGE SQL AS $$ INSERT INTO xy VALUES (1, 1), (2, 2) RETURNING *; $$"}
Query {"String": "CREATE FUNCTION g() RETURNS SETOF RECORD LANGUAGE SQL AS $$ INSERT INTO xy VALUES (2, 1), (3, 3) RETURNING *; $$"}
Parse {"Name": "q1", "Query": "SELECT f();"}
Parse {"Name": "q2", "Query": "SELECT g();"}
Bind {"DestinationPortal": "p1", "PreparedStatement": "q1"}
Bind {"DestinationPortal": "p2", "PreparedStatement": "q2"}
Execute {"Portal": "p1", "MaxRows": 1}
Execute {"Portal": "p2", "MaxRows": 1}
Execute {"Portal": "p1", "MaxRows": 1}
Execute {"Portal": "p2", "MaxRows": 1}
Sync
----

until keepErrMessage
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
ReadyForQuery
ErrorResponse
ReadyForQuery
----
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"CommandComplete","CommandTag":"SET"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"CommandComplete","CommandTag":"DROP TABLE"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"CommandComplete","CommandTag":"DROP FUNCTION"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"CommandComplete","CommandTag":"DROP FUNCTION"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"CommandComplete","CommandTag":"DEALLOCATE ALL"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"CommandComplete","CommandTag":"CREATE TABLE"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"CommandComplete","CommandTag":"CREATE FUNCTION"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"CommandComplete","CommandTag":"CREATE FUNCTION"}
{"Type":"ReadyForQuery","TxStatus":"I"}
{"Type":"ParseComplete"}
{"Type":"ParseComplete"}
{"Type":"BindComplete"}
{"Type":"BindComplete"}
{"Type":"DataRow","Values":[{"text":"(1,1)"}]}
{"Type":"PortalSuspended"}
{"Type":"ErrorResponse","Code":"0A000","Message":"unimplemented: the statement for a pausable portal must be a read-only SELECT query with no sub-queries or post-queries","Detail":"cannot execute a portal while a different one is open","Hint":"You have attempted to use a feature that is not yet implemented.\nSee: https://go.crdb.dev/issue-v/98911/v25.1"}
{"Type":"ReadyForQuery","TxStatus":"I"}

subtest end
