setup
{
	ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 8429800;
    CREATE TABLE ref_table_1(id int PRIMARY KEY, value int);
	SELECT create_reference_table('ref_table_1');

    CREATE TABLE ref_table_2(id int PRIMARY KEY, value int REFERENCES ref_table_1(id) ON DELETE CASCADE ON UPDATE CASCADE);
	SELECT create_reference_table('ref_table_2');

    CREATE TABLE ref_table_3(id int PRIMARY KEY, value int REFERENCES ref_table_2(id) ON DELETE CASCADE ON UPDATE CASCADE);
	SELECT create_reference_table('ref_table_3');

    INSERT INTO ref_table_1 VALUES (1, 1), (3, 3), (5, 5);
    INSERT INTO ref_table_2 SELECT * FROM ref_table_1;
    INSERT INTO ref_table_3 SELECT * FROM ref_table_2;
}

teardown
{
	DROP TABLE ref_table_1, ref_table_2, ref_table_3;
}

session "s1"

step "s1-begin"
{
	BEGIN;
}

step "s1-delete-table-2"
{
    DELETE FROM ref_table_2 WHERE value = 2;
}

step "s1-insert-table-2"
{
    INSERT INTO ref_table_2 VALUES (7, 2);
}

step "s1-update-table-2"
{
    UPDATE ref_table_2 SET id = 0 WHERE value = 2;
}

step "s1-delete-table-3"
{
    DELETE FROM ref_table_3 WHERE value = 1 RETURNING id;
}

step "s1-insert-table-3"
{
    INSERT INTO ref_table_3 VALUES (7, 1);
}

step "s1-update-table-3"
{
    UPDATE ref_table_3 SET id = 2 WHERE value = 1 RETURNING id;
}

step "s1-select-table-1"
{
    SELECT * FROM ref_table_1 ORDER BY id, value;
}

step "s1-select-table-2"
{
    SELECT * FROM ref_table_2 ORDER BY id, value;
}

step "s1-select-table-3"
{
    SELECT * FROM ref_table_3 ORDER BY id, value;
}

step "s1-view-locks"
{
    -- The following output changed in PG versions 13.6 and 14.2. The output is expected
    -- to change in earlier versions of PG as the and application_name format did not use
    -- to include session name at the end.
    SELECT classid,
        objid,
        objsubid,
        mode,
        application_name,
        backend_type,
        regexp_replace(query, E'[\\n\\r\\u2028]+', ' ', 'g' ) query
    FROM pg_locks l
    JOIN pg_stat_activity a
    ON l.pid = a.pid
    WHERE locktype='advisory'
        AND application_name <> 'Citus Maintenance Daemon'
    ORDER BY 1, 2, 3, 4;
}

step "s1-rollback"
{
    ROLLBACK;
}

step "s1-commit"
{
    COMMIT;
}

session "s2"

step "s2-begin"
{
	BEGIN;
}

step "s2-insert-table-1"
{
    INSERT INTO ref_table_1 VALUES (7, 7);
}

step "s2-update-table-1"
{
    UPDATE ref_table_1 SET id = 2 WHERE id = 1;
}

step "s2-delete-table-1"
{
    DELETE FROM ref_table_1 WHERE id = 1;
}

step "s2-insert-table-2"
{
    INSERT INTO ref_table_2 VALUES (7, 5);
}

step "s2-update-table-2"
{
    UPDATE ref_table_2 SET id = 2 WHERE id = 1;
}

step "s2-delete-table-2"
{
    DELETE FROM ref_table_2 WHERE id = 1;
}

step "s2-insert-table-3"
{
    INSERT INTO ref_table_3 VALUES (7, 5);
}

step "s2-update-table-3"
{
    UPDATE ref_table_3 SET id = 2 WHERE id = 1;
}

step "s2-delete-table-3"
{
    DELETE FROM ref_table_3 WHERE id = 1;
}

step "s2-rollback"
{
    ROLLBACK;
}

step "s2-commit"
{
    COMMIT;
}

// Check that we get necessary resource locks

// Case 1. UPDATE/DELETE ref_table_1 should only lock its own shard in Exclusive mode.
permutation "s2-begin" "s2-update-table-1" "s1-begin" "s1-view-locks" "s1-rollback" "s2-rollback" "s1-view-locks"
permutation "s2-begin" "s2-delete-table-1" "s1-view-locks" "s2-rollback" "s1-view-locks"
// Case 2. Modifying ref_table_2 should also lock ref_table_1 shard in Exclusive mode.
permutation "s2-begin" "s2-update-table-2" "s1-view-locks" "s2-rollback" "s1-view-locks"
permutation "s2-begin" "s2-delete-table-2" "s1-view-locks" "s2-rollback" "s1-view-locks"
// Case 3. Modifying ref_table_3 should also lock ref_table_1 and ref_table_2 shards in Exclusive mode.
permutation "s2-begin" "s2-update-table-3" "s1-begin" "s1-view-locks" "s1-rollback" "s2-rollback" "s1-view-locks"
permutation "s2-begin" "s2-delete-table-3" "s1-begin" "s1-view-locks" "s1-rollback" "s2-rollback" "s1-view-locks"
// Case 4. Inserting into ref_table_1 should only lock its own shard in RowExclusive mode.
permutation "s2-begin" "s2-insert-table-1" "s1-view-locks" "s2-rollback" "s1-view-locks"
// Case 5. Modifying ref_table_2 should also lock ref_table_1 in RowExclusive mode.
permutation "s2-begin" "s2-insert-table-2" "s1-view-locks" "s2-rollback" "s1-view-locks"
// Case 6. Modifying ref_table_2 should also lock ref_table_1 in RowExclusive mode.
permutation "s2-begin" "s2-insert-table-3" "s1-view-locks" "s2-rollback" "s1-view-locks"

// Now some concurrent operations

// Updates/Deletes from ref_table_1 cascade to ref_table_2, so DML on ref_table_2 should block
// Case 1. UPDATE -> DELETE
permutation "s1-begin" "s2-begin" "s2-update-table-1" "s1-delete-table-2" "s2-commit" "s1-commit" "s1-select-table-2"
// Case 2. UPDATE -> INSERT
permutation "s1-begin" "s2-begin" "s2-update-table-1" "s1-insert-table-2" "s2-commit" "s1-commit" "s1-select-table-2"
// Case 3. UPDATE -> UPDATE
permutation "s1-begin" "s2-begin" "s2-update-table-1" "s1-update-table-2" "s2-commit" "s1-commit" "s1-select-table-2"
// Case 4. DELETE -> DELETE
permutation "s1-begin" "s2-begin" "s2-delete-table-1" "s1-delete-table-2" "s2-commit" "s1-commit" "s1-select-table-2"
// Case 5. DELETE -> INSERT
permutation "s1-begin" "s2-begin" "s2-delete-table-1" "s1-insert-table-2" "s2-commit" "s1-commit" "s1-select-table-2"
// Case 6. DELETE -> UPDATE
permutation "s1-begin" "s2-begin" "s2-delete-table-1" "s1-update-table-2" "s2-commit" "s1-commit" "s1-select-table-2"

// Deletes from ref_table_1 can transitively cascade to ref_table_3, so DML on ref_table_3 should block
// Case 1. DELETE -> DELETE
permutation "s1-begin" "s2-begin" "s2-delete-table-1" "s1-delete-table-3" "s2-commit" "s1-commit" "s1-select-table-3"
// Case 2. DELETE -> INSERT, should error out
permutation "s1-begin" "s2-begin" "s2-delete-table-1" "s1-insert-table-3" "s2-commit" "s1-commit" "s1-select-table-3"
// Case 3. DELETE -> UPDATE
permutation "s1-begin" "s2-begin" "s2-delete-table-1" "s1-update-table-3" "s2-commit" "s1-commit" "s1-select-table-3"

// Any DML on any of ref_table_{1,2,3} should block others from DML in the foreign constraint graph ...
permutation "s1-begin" "s2-begin" "s2-insert-table-1" "s1-update-table-3" "s2-commit" "s1-commit" "s1-select-table-3"
permutation "s1-begin" "s2-begin" "s1-update-table-3" "s2-insert-table-1" "s1-commit" "s2-commit" "s1-select-table-3"
permutation "s1-begin" "s2-begin" "s2-insert-table-1" "s1-update-table-2" "s2-commit" "s1-commit" "s1-select-table-3"
permutation "s1-begin" "s2-begin" "s1-update-table-2" "s2-insert-table-1" "s1-commit" "s2-commit" "s1-select-table-3"
permutation "s1-begin" "s2-begin" "s2-insert-table-2" "s1-update-table-3" "s2-commit" "s1-commit" "s1-select-table-3"
permutation "s1-begin" "s2-begin" "s1-update-table-3" "s2-insert-table-2" "s1-commit" "s2-commit" "s1-select-table-3"

// DMLs shouldn't block select on tables in the same foreign constraint graph
permutation "s1-begin" "s2-begin" "s2-insert-table-1" "s1-select-table-1" "s2-commit" "s1-commit"
permutation "s1-begin" "s2-begin" "s2-insert-table-1" "s1-select-table-2" "s2-commit" "s1-commit"
permutation "s1-begin" "s2-begin" "s2-insert-table-1" "s1-select-table-3" "s2-commit" "s1-commit"
permutation "s1-begin" "s2-begin" "s2-delete-table-2" "s1-select-table-1" "s2-commit" "s1-commit"
permutation "s1-begin" "s2-begin" "s2-delete-table-2" "s1-select-table-2" "s2-commit" "s1-commit"
permutation "s1-begin" "s2-begin" "s2-delete-table-2" "s1-select-table-3" "s2-commit" "s1-commit"
permutation "s1-begin" "s2-begin" "s2-update-table-3" "s1-select-table-1" "s2-commit" "s1-commit"
permutation "s1-begin" "s2-begin" "s2-update-table-3" "s1-select-table-2" "s2-commit" "s1-commit"
permutation "s1-begin" "s2-begin" "s2-update-table-3" "s1-select-table-3" "s2-commit" "s1-commit"
