# LogicTest: local

subtest DeleteCascade_Basic
### Basic Delete Cascade
#     a
#    / \
#   b1 b2
#  / \   \
# c1  c2  c3

statement ok
CREATE TABLE a (
  id STRING PRIMARY KEY
)

statement ok
CREATE TABLE b1 (
  id STRING PRIMARY KEY,
  delete_cascade STRING NOT NULL REFERENCES a ON DELETE CASCADE,
  FAMILY (id, delete_cascade)
)

statement ok
CREATE TABLE b2 (
  id STRING PRIMARY KEY,
  delete_cascade STRING NOT NULL REFERENCES a ON DELETE CASCADE,
  FAMILY (id, delete_cascade)
)

statement ok
CREATE TABLE c1 (
  id STRING PRIMARY KEY,
  delete_cascade STRING NOT NULL REFERENCES b1 ON DELETE CASCADE,
  FAMILY (id, delete_cascade)
)

statement ok
CREATE TABLE c2 (
  id STRING PRIMARY KEY,
  delete_cascade STRING NOT NULL REFERENCES b1 ON DELETE CASCADE,
  FAMILY (id, delete_cascade)
)

statement ok
CREATE TABLE c3 (
  id STRING PRIMARY KEY REFERENCES b2 ON DELETE CASCADE
)

statement ok
INSERT INTO a VALUES ('a-pk1');
INSERT INTO b1 VALUES ('b1-pk1', 'a-pk1'), ('b1-pk2', 'a-pk1');
INSERT INTO b2 VALUES ('b2-pk1', 'a-pk1'), ('b2-pk2', 'a-pk1');
INSERT INTO c1 VALUES
  ('c1-pk1-b1-pk1', 'b1-pk1')
 ,('c1-pk2-b1-pk1', 'b1-pk1')
 ,('c1-pk3-b1-pk2', 'b1-pk2')
 ,('c1-pk4-b1-pk2', 'b1-pk2')
;
INSERT INTO c2 VALUES
  ('c2-pk1-b1-pk1', 'b1-pk1')
 ,('c2-pk2-b1-pk1', 'b1-pk1')
 ,('c2-pk3-b1-pk2', 'b1-pk2')
 ,('c2-pk4-b1-pk2', 'b1-pk2')
;
INSERT INTO c3 VALUES ('b2-pk1'), ('b2-pk2');

statement ok
SET tracing = on,kv,results; DELETE FROM a WHERE id = 'a-pk1'; SET tracing = off

query T rowsort
SELECT message FROM [SHOW TRACE FOR SESSION]
WHERE message LIKE '%executing cascade %' OR message LIKE 'Del%'
----
Del /Table/106/1/"a-pk1"/0
executing cascade for constraint b1_delete_cascade_fkey
Del /Table/107/1/"b1-pk1"/0
Del /Table/107/1/"b1-pk2"/0
executing cascade for constraint b2_delete_cascade_fkey
Del /Table/108/1/"b2-pk1"/0
Del /Table/108/1/"b2-pk2"/0
executing cascade for constraint c1_delete_cascade_fkey
Del /Table/109/1/"c1-pk1-b1-pk1"/0
Del /Table/109/1/"c1-pk2-b1-pk1"/0
Del /Table/109/1/"c1-pk3-b1-pk2"/0
Del /Table/109/1/"c1-pk4-b1-pk2"/0
executing cascade for constraint c2_delete_cascade_fkey
Del /Table/110/1/"c2-pk1-b1-pk1"/0
Del /Table/110/1/"c2-pk2-b1-pk1"/0
Del /Table/110/1/"c2-pk3-b1-pk2"/0
Del /Table/110/1/"c2-pk4-b1-pk2"/0
executing cascade for constraint c3_id_fkey
Del /Table/111/1/"b2-pk1"/0
Del /Table/111/1/"b2-pk2"/0

# Clean up after the test.
statement ok
DROP TABLE c3, c2, c1, b2, b1, a;

subtest UpdateCascade_Basic
### Basic Update Cascade
#     a
#    / \
#   b1 b2
#  / \   \
# c1  c2  c3

statement ok
CREATE TABLE a (
  id STRING PRIMARY KEY
);

statement ok
CREATE TABLE b1 (
  id STRING PRIMARY KEY,
  update_cascade STRING NOT NULL UNIQUE REFERENCES a ON UPDATE CASCADE,
  FAMILY (id, update_cascade)
);

statement ok
CREATE TABLE b2 (
  id STRING PRIMARY KEY,
  update_cascade STRING NOT NULL UNIQUE REFERENCES a ON UPDATE CASCADE,
  FAMILY (id, update_cascade)
);

statement ok
CREATE TABLE c1 (
  id STRING PRIMARY KEY,
  update_cascade STRING NOT NULL REFERENCES b1 (update_cascade) ON UPDATE CASCADE,
  FAMILY (id, update_cascade)
);

statement ok
CREATE TABLE c2 (
  id STRING PRIMARY KEY,
  update_cascade STRING NOT NULL REFERENCES b1 (update_cascade) ON UPDATE CASCADE,
  FAMILY (id, update_cascade)
);

statement ok
CREATE TABLE c3 (
  id STRING PRIMARY KEY REFERENCES b2(update_cascade) ON UPDATE CASCADE
);

statement ok
INSERT INTO a VALUES ('original');
INSERT INTO b1 VALUES ('b1-pk1', 'original');
INSERT INTO b2 VALUES ('b2-pk1', 'original');
INSERT INTO c1 VALUES
  ('c1-pk1', 'original')
 ,('c1-pk2', 'original')
 ,('c1-pk3', 'original')
 ,('c1-pk4', 'original')
;
INSERT INTO c2 VALUES
  ('c2-pk1', 'original')
 ,('c2-pk2', 'original')
 ,('c2-pk3', 'original')
 ,('c2-pk4', 'original')
;
INSERT INTO c3 VALUES ('original');

# ON UPDATE CASCADE
statement ok
UPDATE a SET id = 'updated' WHERE id = 'original';

statement ok
SET tracing = on,kv,results; UPDATE a SET id = 'updated2' WHERE id = 'updated'; SET tracing = off

query T
SELECT message FROM [SHOW TRACE FOR SESSION]
WHERE message LIKE '%executing cascade %' OR message LIKE 'Del%' OR message LIKE 'CPut%'
----
Del /Table/112/1/"updated"/0
CPut /Table/112/1/"updated2"/0 -> /TUPLE/
executing cascade for constraint b1_update_cascade_fkey
Del /Table/113/2/"updated"/0
CPut /Table/113/2/"updated2"/0 -> /BYTES/0x1262312d706b310001 (expecting does not exist)
executing cascade for constraint b2_update_cascade_fkey
Del /Table/114/2/"updated"/0
CPut /Table/114/2/"updated2"/0 -> /BYTES/0x1262322d706b310001 (expecting does not exist)
executing cascade for constraint c1_update_cascade_fkey
executing cascade for constraint c2_update_cascade_fkey
executing cascade for constraint c3_id_fkey
Del /Table/117/1/"updated"/0
CPut /Table/117/1/"updated2"/0 -> /TUPLE/

# Clean up after the test.
statement ok
DROP TABLE c3, c2, c1, b2, b1, a;

subtest DeleteSetNull_Basic1
### Basic Delete Set Null
#        a
#      // \\
#    / |  |  \
#   b1 b2 b3 b4

statement ok
CREATE TABLE a (
  id STRING PRIMARY KEY
);
CREATE TABLE b1 (
  id STRING PRIMARY KEY,
  delete_set_null STRING REFERENCES a ON DELETE SET NULL,
  FAMILY (id, delete_set_null)
);
CREATE TABLE b2 (
  id STRING PRIMARY KEY,
  delete_set_null STRING REFERENCES a ON DELETE SET NULL,
  FAMILY (id, delete_set_null)
);
CREATE TABLE b3 (
  id STRING PRIMARY KEY,
  delete_set_null STRING REFERENCES a ON DELETE SET NULL,
  FAMILY (id, delete_set_null)
);
CREATE TABLE b4 (
  id STRING PRIMARY KEY,
  delete_set_null STRING REFERENCES a ON DELETE SET NULL,
  FAMILY (id, delete_set_null)
);

statement ok
INSERT INTO a VALUES ('delete_me'), ('untouched');
INSERT INTO b1 VALUES ('b1-pk1', 'untouched'), ('b1-pk2', 'untouched');
INSERT INTO b2 VALUES ('b2-pk1', 'untouched'), ('b2-pk2', 'delete_me');
INSERT INTO b3 VALUES ('b3-pk1', 'delete_me'), ('b3-pk2', 'untouched');
INSERT INTO b4 VALUES ('b4-pk1', 'delete_me'), ('b4-pk2', 'delete_me');

# Ensure that show trace adds a cascade message for each of the tables that is
# cascaded into.
statement ok
SET tracing = on,kv,results; DELETE FROM a WHERE id = 'delete_me'; SET tracing = off

query T
SELECT message FROM [SHOW TRACE FOR SESSION]
WHERE message LIKE '%executing cascade %' OR message LIKE 'Del%'
----
Del /Table/118/1/"delete_me"/0
executing cascade for constraint b1_delete_set_null_fkey
executing cascade for constraint b2_delete_set_null_fkey
executing cascade for constraint b3_delete_set_null_fkey
executing cascade for constraint b4_delete_set_null_fkey

# Clean up after the test.
statement ok
DROP TABLE b4, b3, b2, b1, a;

subtest DeleteSetNull_Basic2
### Basic Delete Set Null
#     a
#    / \
#   b1 b2
#  / \   \
# c1  c2  c3

statement ok
CREATE TABLE a (
  id STRING PRIMARY KEY
);
CREATE TABLE b1 (
  id STRING PRIMARY KEY,
  delete_cascade STRING NOT NULL REFERENCES a ON DELETE CASCADE,
  FAMILY (id, delete_cascade)
);
CREATE TABLE b2 (
  id STRING PRIMARY KEY,
  delete_cascade STRING NOT NULL REFERENCES a ON DELETE CASCADE,
  FAMILY (id, delete_cascade)
);
CREATE TABLE c1 (
  id STRING PRIMARY KEY,
  delete_set_null STRING REFERENCES b1 ON DELETE SET NULL,
  FAMILY (id, delete_set_null)
);
CREATE TABLE c2 (
  id STRING PRIMARY KEY,
  delete_set_null STRING REFERENCES b1 ON DELETE SET NULL,
  FAMILY (id, delete_set_null)
);
CREATE TABLE c3 (
  id STRING PRIMARY KEY,
  delete_set_null STRING REFERENCES b2 ON DELETE SET NULL,
  FAMILY (id, delete_set_null)
);

statement ok
INSERT INTO a VALUES ('a-pk1');
INSERT INTO b1 VALUES ('b1-pk1', 'a-pk1'), ('b1-pk2', 'a-pk1');
INSERT INTO b2 VALUES ('b2-pk1', 'a-pk1'), ('b2-pk2', 'a-pk1');
INSERT INTO c1 VALUES
  ('c1-pk1-b1-pk1', 'b1-pk1')
 ,('c1-pk2-b1-pk1', 'b1-pk1')
 ,('c1-pk3-b1-pk2', 'b1-pk2')
 ,('c1-pk4-b1-pk2', 'b1-pk2')
;
INSERT INTO c2 VALUES
  ('c2-pk1-b1-pk1', 'b1-pk1')
 ,('c2-pk2-b1-pk1', 'b1-pk1')
 ,('c2-pk3-b1-pk2', 'b1-pk2')
 ,('c2-pk4-b1-pk2', 'b1-pk2')
;
INSERT INTO c3 VALUES
  ('c3-pk1-b2-pk1', 'b2-pk1')
 ,('c3-pk2-b2-pk1', 'b2-pk1')
 ,('c3-pk3-b2-pk2', 'b2-pk2')
 ,('c3-pk4-b2-pk2', 'b2-pk2')
;

statement ok
SET tracing = on,kv,results; DELETE FROM a WHERE id = 'a-pk1'; SET tracing = off

query T
SELECT message FROM [SHOW TRACE FOR SESSION]
WHERE message LIKE '%executing cascade %' OR message LIKE 'Del%'
----
Del /Table/123/1/"a-pk1"/0
executing cascade for constraint b1_delete_cascade_fkey
Del /Table/124/1/"b1-pk1"/0
Del /Table/124/1/"b1-pk2"/0
executing cascade for constraint b2_delete_cascade_fkey
Del /Table/125/1/"b2-pk1"/0
Del /Table/125/1/"b2-pk2"/0
executing cascade for constraint c1_delete_set_null_fkey
executing cascade for constraint c2_delete_set_null_fkey
executing cascade for constraint c3_delete_set_null_fkey

# Clean up after the test.
statement ok
DROP TABLE c3, c2, c1, b2, b1, a;

subtest UpdateSetNull_Basic1
### Basic Update Set Null
#        a
#      // \\
#    / |  |  \
#   b1 b2 b3 b4

statement ok
CREATE TABLE a (
  id STRING PRIMARY KEY
);
CREATE TABLE b1 (
  id STRING PRIMARY KEY,
  update_set_null STRING REFERENCES a ON UPDATE SET NULL,
  FAMILY (id, update_set_null)
);
CREATE TABLE b2 (
  id STRING PRIMARY KEY,
  update_set_null STRING REFERENCES a ON UPDATE SET NULL,
  FAMILY (id, update_set_null)
);
CREATE TABLE b3 (
  id STRING PRIMARY KEY,
  update_set_null STRING REFERENCES a ON UPDATE SET NULL,
  FAMILY (id, update_set_null)
);
CREATE TABLE b4 (
  id STRING PRIMARY KEY,
  update_set_null STRING REFERENCES a ON UPDATE SET NULL,
  FAMILY (id, update_set_null)
);

statement ok
INSERT INTO a VALUES ('original'), ('untouched');
INSERT INTO b1 VALUES ('b1-pk1', 'untouched'), ('b1-pk2', 'untouched');
INSERT INTO b2 VALUES ('b2-pk1', 'untouched'), ('b2-pk2', 'original');
INSERT INTO b3 VALUES ('b3-pk1', 'original'), ('b3-pk2', 'untouched');
INSERT INTO b3 VALUES ('b4-pk1', 'original'), ('b4-pk2', 'original');

# Ensure that show trace adds a cascade message for each of the tables that is
# cascaded into.
statement ok
SET tracing = on,kv,results; UPDATE a SET id = 'updated' WHERE id = 'original'; SET tracing = off

query T
SELECT message FROM [SHOW TRACE FOR SESSION]
WHERE message LIKE '%executing cascade %' OR message LIKE 'Del%' OR message LIKE 'CPut%'
----
Del /Table/129/1/"original"/0
CPut /Table/129/1/"updated"/0 -> /TUPLE/
executing cascade for constraint b1_update_set_null_fkey
executing cascade for constraint b2_update_set_null_fkey
executing cascade for constraint b3_update_set_null_fkey
executing cascade for constraint b4_update_set_null_fkey

# Clean up after the test.
statement ok
DROP TABLE b4, b3, b2, b1, a;

subtest UpdateSetNull_Basic2
### Basic Update Set Null
#     a
#    / \
#   b1 b2
#  / \   \
# c1  c2  c3

statement ok
CREATE TABLE a (
  id STRING PRIMARY KEY
);
CREATE TABLE b1 (
  id STRING PRIMARY KEY,
  update_cascade STRING UNIQUE NOT NULL REFERENCES a ON UPDATE CASCADE,
  FAMILY (id, update_cascade)
);
CREATE TABLE b2 (
  id STRING PRIMARY KEY,
  update_cascade STRING UNIQUE NOT NULL REFERENCES a ON UPDATE CASCADE,
  FAMILY (id, update_cascade)
);
CREATE TABLE c1 (
  id STRING PRIMARY KEY,
  update_set_null STRING REFERENCES b1(update_cascade) ON UPDATE SET NULL,
  FAMILY (id, update_set_null)
);
CREATE TABLE c2 (
  id STRING PRIMARY KEY,
  update_set_null STRING REFERENCES b1(update_cascade) ON UPDATE SET NULL,
  FAMILY (id, update_set_null)
);
CREATE TABLE c3 (
  id STRING PRIMARY KEY,
  update_set_null STRING REFERENCES b2(update_cascade) ON UPDATE SET NULL,
  FAMILY (id, update_set_null)
);

statement ok
INSERT INTO a VALUES ('original'), ('untouched');
INSERT INTO b1 VALUES ('b1-pk1', 'original'), ('b1-pk2', 'untouched');
INSERT INTO b2 VALUES ('b2-pk1', 'original'), ('b2-pk2', 'untouched');
INSERT INTO c1 VALUES
  ('c1-pk1-b1-pk1', 'original')
 ,('c1-pk2-b1-pk1', 'original')
 ,('c1-pk3-b1-pk2', 'untouched')
 ,('c1-pk4-b1-pk2', 'untouched')
;
INSERT INTO c2 VALUES
  ('c2-pk1-b1-pk1', 'original')
 ,('c2-pk2-b1-pk1', 'original')
 ,('c2-pk3-b1-pk2', 'untouched')
 ,('c2-pk4-b1-pk2', 'untouched')
;
INSERT INTO c3 VALUES
  ('c3-pk1-b2-pk1', 'original')
 ,('c3-pk2-b2-pk1', 'original')
 ,('c3-pk3-b2-pk2', 'untouched')
 ,('c3-pk4-b2-pk2', 'untouched')
;

# Ensure that show trace adds a cascade message for each of the tables that is
# cascaded into.
statement ok
SET tracing = on,kv,results; UPDATE a SET id = 'updated' WHERE id = 'original'; SET tracing = off

query T
SELECT message FROM [SHOW TRACE FOR SESSION]
WHERE message LIKE '%executing cascade %' OR message LIKE 'Del%' OR message LIKE 'CPut%'
----
Del /Table/134/1/"original"/0
CPut /Table/134/1/"updated"/0 -> /TUPLE/
executing cascade for constraint b1_update_cascade_fkey
Del /Table/135/2/"original"/0
CPut /Table/135/2/"updated"/0 -> /BYTES/0x1262312d706b310001 (expecting does not exist)
executing cascade for constraint b2_update_cascade_fkey
Del /Table/136/2/"original"/0
CPut /Table/136/2/"updated"/0 -> /BYTES/0x1262322d706b310001 (expecting does not exist)
executing cascade for constraint c1_update_set_null_fkey
executing cascade for constraint c2_update_set_null_fkey
executing cascade for constraint c3_update_set_null_fkey

# Clean up after the test.
statement ok
DROP TABLE c3, c2, c1, b2, b1, a;

##############

subtest DeleteSetDefault_Basic1
### Basic Delete Set Default
#        a
#      // \\
#    / |  |  \
#   b1 b2 b3 b4

statement ok
CREATE TABLE a (
  id STRING PRIMARY KEY
);
CREATE TABLE b1 (
  id STRING PRIMARY KEY,
  delete_set_default STRING DEFAULT 'b1-default' REFERENCES a ON DELETE SET DEFAULT,
  FAMILY (id, delete_set_default)
);
CREATE TABLE b2 (
  id STRING PRIMARY KEY,
  delete_set_default STRING DEFAULT 'b2-default' REFERENCES a ON DELETE SET DEFAULT,
  FAMILY (id, delete_set_default)
);
CREATE TABLE b3 (
  id STRING PRIMARY KEY,
  delete_set_default STRING DEFAULT 'b3-default' REFERENCES a ON DELETE SET DEFAULT,
  FAMILY (id, delete_set_default)
);
CREATE TABLE b4 (
  id STRING PRIMARY KEY,
  delete_set_default STRING DEFAULT 'b4-default' REFERENCES a ON DELETE SET DEFAULT,
  FAMILY (id, delete_set_default)
);

statement ok
INSERT INTO a VALUES ('delete_me'), ('untouched'), ('b2-default'), ('b3-default'), ('b4-default');
INSERT INTO b1 VALUES ('b1-pk1', 'untouched'), ('b1-pk2', 'untouched');
INSERT INTO b2 VALUES ('b2-pk1', 'untouched'), ('b2-pk2', 'delete_me');
INSERT INTO b3 VALUES ('b3-pk1', 'delete_me'), ('b3-pk2', 'untouched');
INSERT INTO b4 VALUES ('b4-pk1', 'delete_me'), ('b4-pk2', 'delete_me');

# Ensure that show trace adds a cascade message for each of the tables that is
# cascaded into.
statement ok
SET tracing = on,kv,results; DELETE FROM a WHERE id = 'delete_me'; SET tracing = off

query T
SELECT message FROM [SHOW TRACE FOR SESSION]
WHERE message LIKE '%executing cascade %' OR message LIKE 'Del%'
----
Del /Table/140/1/"delete_me"/0
executing cascade for constraint b1_delete_set_default_fkey
executing cascade for constraint b2_delete_set_default_fkey
executing cascade for constraint b3_delete_set_default_fkey
executing cascade for constraint b4_delete_set_default_fkey

# Clean up after the test.
statement ok
DROP TABLE b4, b3, b2, b1, a;

subtest DeleteSetDefault_Basic2
### Basic Delete Set Null via an ON DELETE CASCADE
#     a
#    / \
#   b1 b2
#  / \   \
# c1  c2  c3

statement ok
CREATE TABLE a (
  id STRING PRIMARY KEY
);
CREATE TABLE b1 (
  id STRING PRIMARY KEY,
  delete_cascade STRING NOT NULL REFERENCES a ON DELETE CASCADE,
  FAMILY (id, delete_cascade)
);
CREATE TABLE b2 (
  id STRING PRIMARY KEY,
  delete_cascade STRING NOT NULL REFERENCES a ON DELETE CASCADE,
  FAMILY (id, delete_cascade)
);
CREATE TABLE c1 (
  id STRING PRIMARY KEY,
  delete_set_default STRING DEFAULT 'b1-default' REFERENCES b1 ON DELETE SET DEFAULT,
  FAMILY (id, delete_set_default)
);
CREATE TABLE c2 (
  id STRING PRIMARY KEY,
  delete_set_default STRING DEFAULT 'b1-default' REFERENCES b1 ON DELETE SET DEFAULT,
  FAMILY (id, delete_set_default)
);
CREATE TABLE c3 (
  id STRING PRIMARY KEY,
  delete_set_default STRING DEFAULT 'b2-default' REFERENCES b2 ON DELETE SET DEFAULT,
  FAMILY (id, delete_set_default)
);

statement ok
INSERT INTO a VALUES ('a-pk1'), ('a-default');
INSERT INTO b1 VALUES ('b1-pk1', 'a-pk1'), ('b1-pk2', 'a-pk1'), ('b1-default', 'a-default');
INSERT INTO b2 VALUES ('b2-pk1', 'a-pk1'), ('b2-pk2', 'a-pk1'), ('b2-default', 'a-default');
INSERT INTO c1 VALUES
  ('c1-pk1-b1-pk1', 'b1-pk1')
 ,('c1-pk2-b1-pk1', 'b1-pk1')
 ,('c1-pk3-b1-pk2', 'b1-pk2')
 ,('c1-pk4-b1-pk2', 'b1-pk2')
;
INSERT INTO c2 VALUES
  ('c2-pk1-b1-pk1', 'b1-pk1')
 ,('c2-pk2-b1-pk1', 'b1-pk1')
 ,('c2-pk3-b1-pk2', 'b1-pk2')
 ,('c2-pk4-b1-pk2', 'b1-pk2')
;
INSERT INTO c3 VALUES
  ('c3-pk1-b2-pk1', 'b2-pk1')
 ,('c3-pk2-b2-pk1', 'b2-pk1')
 ,('c3-pk3-b2-pk2', 'b2-pk2')
 ,('c3-pk4-b2-pk2', 'b2-pk2')
;

statement ok
SET tracing = on,kv,results; DELETE FROM a WHERE id = 'a-pk1'; SET tracing = off

query T
SELECT message FROM [SHOW TRACE FOR SESSION]
WHERE message LIKE '%executing cascade %' OR message LIKE 'Del%'
----
Del /Table/145/1/"a-pk1"/0
executing cascade for constraint b1_delete_cascade_fkey
Del /Table/146/1/"b1-pk1"/0
Del /Table/146/1/"b1-pk2"/0
executing cascade for constraint b2_delete_cascade_fkey
Del /Table/147/1/"b2-pk1"/0
Del /Table/147/1/"b2-pk2"/0
executing cascade for constraint c1_delete_set_default_fkey
executing cascade for constraint c2_delete_set_default_fkey
executing cascade for constraint c3_delete_set_default_fkey

# Clean up after the test.
statement ok
DROP TABLE c3, c2, c1, b2, b1, a;

subtest UpdateSetDefault_Basic1
### Basic Update Set Default
#        a
#      // \\
#    / |  |  \
#   b1 b2 b3 b4

statement ok
CREATE TABLE a (
  id STRING PRIMARY KEY
);
CREATE TABLE b1 (
  id STRING PRIMARY KEY,
  update_set_null STRING DEFAULT 'b1-default' REFERENCES a ON UPDATE SET DEFAULT,
  FAMILY (id, update_set_null)
);
CREATE TABLE b2 (
  id STRING PRIMARY KEY,
  update_set_null STRING DEFAULT 'b2-default' REFERENCES a ON UPDATE SET DEFAULT,
  FAMILY (id, update_set_null)
);
CREATE TABLE b3 (
  id STRING PRIMARY KEY,
  update_set_null STRING DEFAULT 'b3-default' REFERENCES a ON UPDATE SET DEFAULT,
  FAMILY (id, update_set_null)
);
CREATE TABLE b4 (
  id STRING PRIMARY KEY,
  update_set_null STRING DEFAULT 'b4-default' REFERENCES a ON UPDATE SET DEFAULT,
  FAMILY (id, update_set_null)
);

statement ok
INSERT INTO a VALUES ('original'), ('untouched'), ('b1-default'), ('b2-default'), ('b3-default'), ('b4-default');
INSERT INTO b1 VALUES ('b1-pk1', 'untouched'), ('b1-pk2', 'untouched');
INSERT INTO b2 VALUES ('b2-pk1', 'untouched'), ('b2-pk2', 'original');
INSERT INTO b3 VALUES ('b3-pk1', 'original'), ('b3-pk2', 'untouched');
INSERT INTO b3 VALUES ('b4-pk1', 'original'), ('b4-pk2', 'original');

# Ensure that show trace adds a cascade message for each of the tables that is
# cascaded into.
statement ok
SET tracing = on,kv,results; UPDATE a SET id = 'updated' WHERE id = 'original'; SET tracing = off

query T
SELECT message FROM [SHOW TRACE FOR SESSION]
WHERE message LIKE '%executing cascade %' OR message LIKE 'Del%' OR message LIKE 'CPut%'
----
Del /Table/151/1/"original"/0
CPut /Table/151/1/"updated"/0 -> /TUPLE/
executing cascade for constraint b1_update_set_null_fkey
executing cascade for constraint b2_update_set_null_fkey
executing cascade for constraint b3_update_set_null_fkey
executing cascade for constraint b4_update_set_null_fkey

# Clean up after the test.
statement ok
DROP TABLE b4, b3, b2, b1, a;

subtest UpdateSetDefault_Basic2
### Basic UPDATE SET DEFAULT via an UPDATE CASCADE
#     a
#    / \
#   b1 b2
#  / \   \
# c1  c2  c3

statement ok
CREATE TABLE a (
  id STRING PRIMARY KEY
);
CREATE TABLE b1 (
  id STRING PRIMARY KEY,
  update_cascade STRING UNIQUE NOT NULL REFERENCES a ON UPDATE CASCADE,
  FAMILY (id, update_cascade)
);
CREATE TABLE b2 (
  id STRING PRIMARY KEY,
  update_cascade STRING UNIQUE NOT NULL REFERENCES a ON UPDATE CASCADE,
  FAMILY (id, update_cascade)
);
CREATE TABLE c1 (
  id STRING PRIMARY KEY,
  update_set_null STRING DEFAULT 'b1-default' REFERENCES b1(update_cascade) ON UPDATE SET DEFAULT,
  FAMILY (id, update_set_null)
);
CREATE TABLE c2 (
  id STRING PRIMARY KEY,
  update_set_null STRING DEFAULT 'b1-default' REFERENCES b1(update_cascade) ON UPDATE SET DEFAULT,
  FAMILY (id, update_set_null)
);
CREATE TABLE c3 (
  id STRING PRIMARY KEY,
  update_set_null STRING DEFAULT 'b2-default' REFERENCES b2(update_cascade) ON UPDATE SET DEFAULT,
  FAMILY (id, update_set_null)
);

statement ok
INSERT INTO a VALUES ('original'), ('untouched'), ('b1-default'), ('b2-default');
INSERT INTO b1 VALUES ('b1-pk1', 'original'), ('b1-pk2', 'untouched'), ('b1-default', 'b1-default');
INSERT INTO b2 VALUES ('b2-pk1', 'original'), ('b2-pk2', 'untouched'), ('b2-default', 'b2-default');
INSERT INTO c1 VALUES
  ('c1-pk1-b1-pk1', 'original')
 ,('c1-pk2-b1-pk1', 'original')
 ,('c1-pk3-b1-pk2', 'untouched')
 ,('c1-pk4-b1-pk2', 'untouched')
;
INSERT INTO c2 VALUES
  ('c2-pk1-b1-pk1', 'original')
 ,('c2-pk2-b1-pk1', 'original')
 ,('c2-pk3-b1-pk2', 'untouched')
 ,('c2-pk4-b1-pk2', 'untouched')
;
INSERT INTO c3 VALUES
  ('c3-pk1-b2-pk1', 'original')
 ,('c3-pk2-b2-pk1', 'original')
 ,('c3-pk3-b2-pk2', 'untouched')
 ,('c3-pk4-b2-pk2', 'untouched')
;

# Ensure that show trace adds a cascade message for each of the tables that is
# cascaded into.
statement ok
SET tracing = on,kv,results; UPDATE a SET id = 'updated' WHERE id = 'original'; SET tracing = off

query T
SELECT message FROM [SHOW TRACE FOR SESSION]
WHERE message LIKE '%executing cascade %' OR message LIKE 'Del%' OR message LIKE 'CPut%'
----
Del /Table/156/1/"original"/0
CPut /Table/156/1/"updated"/0 -> /TUPLE/
executing cascade for constraint b1_update_cascade_fkey
Del /Table/157/2/"original"/0
CPut /Table/157/2/"updated"/0 -> /BYTES/0x1262312d706b310001 (expecting does not exist)
executing cascade for constraint b2_update_cascade_fkey
Del /Table/158/2/"original"/0
CPut /Table/158/2/"updated"/0 -> /BYTES/0x1262322d706b310001 (expecting does not exist)
executing cascade for constraint c1_update_set_null_fkey
executing cascade for constraint c2_update_set_null_fkey
executing cascade for constraint c3_update_set_null_fkey

# Clean up after the test.
statement ok
DROP TABLE c3, c2, c1, b2, b1, a;

# Regression for #46094.

statement ok
CREATE TABLE parent (x INT PRIMARY KEY);
CREATE TABLE child1 (
  id INT PRIMARY KEY,
  x INT REFERENCES parent (x) ON DELETE CASCADE,
  FAMILY (id, x)
);
CREATE TABLE child2 (
  id INT PRIMARY KEY,
  x INT REFERENCES parent (x) ON DELETE SET NULL,
  FAMILY (id, x)
);
INSERT INTO parent VALUES (1), (2);
INSERT INTO child1 VALUES (1, 1), (2, 1);
INSERT INTO child2 VALUES (1, 1), (2, 1)

# Here we ensure that after the cascaded deletes we don't need
# to perform additional and unneeded Scan operations after
# cascade deleting or setting null to referencing rows.
query T kvtrace(Del)
DELETE FROM parent WHERE x = 1
----
Del /Table/162/1/1/0
Del /Table/163/1/1/0
Del /Table/163/1/2/0

subtest DelRange

# Verify use of del-range for cascades, with schema similar to what an
# interleaved hierarchy would look like.
statement ok
CREATE TABLE delrng1 (
  p INT PRIMARY KEY,
  data INT,
  FAMILY (p, data)
);
CREATE TABLE delrng2 (
  p INT,
  q INT,
  data INT,
  PRIMARY KEY (p,q),
  FOREIGN KEY (p) REFERENCES delrng1(p) ON DELETE CASCADE
);
CREATE TABLE delrng2a (
  p INT,
  q INT,
  data INT,
  PRIMARY KEY (p,q),
  FOREIGN KEY (p) REFERENCES delrng1(p) ON DELETE CASCADE
);
CREATE TABLE delrng3 (
  p INT,
  q INT,
  r INT,
  data INT,
  PRIMARY KEY (p,q,r),
  FOREIGN KEY (p,q) REFERENCES delrng2(p,q) ON DELETE CASCADE
);

query T
EXPLAIN DELETE FROM delrng1 WHERE p = 1
----
distribution: local
vectorized: true
·
• root
│
├── • delete range
│     from: delrng1
│     spans: [/1 - /1]
│
├── • fk-cascade
│   │ fk: delrng2_p_fkey
│   │
│   └── • root
│       │
│       ├── • delete range
│       │     from: delrng2
│       │     spans: [/1 - /1]
│       │
│       └── • fk-cascade
│           │ fk: delrng3_p_q_fkey
│           │
│           └── • delete range
│                 from: delrng3
│                 spans: [/1 - /1]
│
└── • fk-cascade
    │ fk: delrng2a_p_fkey
    │
    └── • delete range
          from: delrng2a
          spans: [/1 - /1]

query T kvtrace(Del,DelRange)
DELETE FROM delrng1 WHERE p = 1
----
Del /Table/165/1/1/0
DelRange /Table/166/1/1 - /Table/166/1/2
DelRange /Table/167/1/1 - /Table/167/1/2
DelRange /Table/168/1/1 - /Table/168/1/2

query T kvtrace(DelRange)
DELETE FROM delrng1 WHERE p >= 10 AND p <= 20
----
DelRange /Table/165/1/10 - /Table/165/1/21
DelRange /Table/166/1/10 - /Table/166/1/21
DelRange /Table/167/1/10 - /Table/167/1/21
DelRange /Table/168/1/10 - /Table/168/1/21

statement ok
CREATE TABLE self (a INT PRIMARY KEY, b INT REFERENCES self(a) ON DELETE CASCADE, FAMILY (a, b))

statement ok
INSERT INTO self VALUES (1, NULL);
INSERT INTO self SELECT x, x-1 FROM generate_series(2, 100) AS g(x)

query T
EXPLAIN DELETE FROM self WHERE a = 4
----
distribution: local
vectorized: true
·
• root
│
├── • delete
│   │ from: self
│   │
│   └── • buffer
│       │ label: buffer 1
│       │
│       └── • scan
│             missing stats
│             table: self@self_pkey
│             spans: [/4 - /4]
│             locking strength: for update
│
└── • fk-cascade
    │ fk: self_b_fkey
    │
    └── • root
        │
        ├── • delete
        │   │ from: self
        │   │
        │   └── • buffer
        │       │ label: buffer 1
        │       │
        │       └── • hash join
        │           │ equality: (b) = (a)
        │           │ right cols are key
        │           │
        │           ├── • scan
        │           │     missing stats
        │           │     table: self@self_pkey
        │           │     spans: FULL SCAN
        │           │
        │           └── • distinct
        │               │ estimated row count: 10
        │               │ distinct on: a
        │               │
        │               └── • scan buffer
        │                     estimated row count: 100
        │                     label: buffer 1000000
        │
        └── • fk-cascade
              fk: self_b_fkey
              input: buffer 1

### Delete cascade loop between two tables
# loop_a <- loop_b
# loop_b <- loop_a

statement ok
CREATE TABLE loop_a (
  id STRING PRIMARY KEY
 ,cascade_delete STRING
 ,INDEX(cascade_delete)
);

statement ok
CREATE TABLE loop_b (
  id STRING PRIMARY KEY
 ,cascade_delete STRING REFERENCES loop_a ON DELETE CASCADE
);

statement ok
ALTER TABLE loop_a ADD CONSTRAINT cascade_delete_constraint
  FOREIGN KEY (cascade_delete) REFERENCES loop_b (id)
  ON DELETE CASCADE;

statement ok
INSERT INTO loop_a (id, cascade_delete) VALUES ('loop_a-pk1', NULL);
INSERT INTO loop_b (id, cascade_delete) VALUES ('loop_b-pk1', 'loop_a-pk1');
INSERT INTO loop_a (id, cascade_delete) VALUES ('loop_a-pk2', 'loop_b-pk1');
INSERT INTO loop_b (id, cascade_delete) VALUES ('loop_b-pk2', 'loop_a-pk2');
INSERT INTO loop_a (id, cascade_delete) VALUES ('loop_a-pk3', 'loop_b-pk2');
INSERT INTO loop_b (id, cascade_delete) VALUES ('loop_b-pk3', 'loop_a-pk3');

statement ok
UPDATE loop_a SET cascade_delete = 'loop_b-pk3' WHERE id = 'loop_a-pk1';

query T
EXPLAIN DELETE FROM loop_a WHERE id = 'loop_a-pk1';
----
distribution: local
vectorized: true
·
• root
│
├── • delete
│   │ from: loop_a
│   │
│   └── • buffer
│       │ label: buffer 1
│       │
│       └── • scan
│             missing stats
│             table: loop_a@loop_a_pkey
│             spans: [/'loop_a-pk1' - /'loop_a-pk1']
│             locking strength: for update
│
└── • fk-cascade
    │ fk: loop_b_cascade_delete_fkey
    │
    └── • root
        │
        ├── • delete
        │   │ from: loop_b
        │   │
        │   └── • buffer
        │       │ label: buffer 1
        │       │
        │       └── • hash join
        │           │ equality: (cascade_delete) = (id)
        │           │ right cols are key
        │           │
        │           ├── • scan
        │           │     missing stats
        │           │     table: loop_b@loop_b_pkey
        │           │     spans: FULL SCAN
        │           │
        │           └── • distinct
        │               │ estimated row count: 10
        │               │ distinct on: id
        │               │
        │               └── • scan buffer
        │                     estimated row count: 100
        │                     label: buffer 1000000
        │
        └── • fk-cascade
            │ fk: cascade_delete_constraint
            │
            └── • root
                │
                ├── • delete
                │   │ from: loop_a
                │   │
                │   └── • buffer
                │       │ label: buffer 1
                │       │
                │       └── • lookup join
                │           │ table: loop_a@loop_a_cascade_delete_idx
                │           │ equality: (id) = (cascade_delete)
                │           │
                │           └── • distinct
                │               │ estimated row count: 10
                │               │ distinct on: id
                │               │
                │               └── • scan buffer
                │                     estimated row count: 100
                │                     label: buffer 1000000
                │
                └── • fk-cascade
                      fk: loop_b_cascade_delete_fkey
                      input: buffer 1

query T
EXPLAIN ANALYZE DELETE FROM loop_a WHERE id = 'loop_a-pk1';
----
planning time: 10µs
execution time: 100µs
distribution: <hidden>
vectorized: <hidden>
plan type: custom
rows decoded from KV: 9 (72 B, 18 KVs, 9 gRPC calls)
maximum memory usage: <hidden>
network usage: <hidden>
regions: <hidden>
isolation level: serializable
priority: normal
quality of service: regular
·
• root
│
├── • delete
│   │ sql nodes: <hidden>
│   │ regions: <hidden>
│   │ actual row count: 1
│   │ from: loop_a
│   │
│   └── • buffer
│       │ sql nodes: <hidden>
│       │ regions: <hidden>
│       │ actual row count: 1
│       │ label: buffer 1
│       │
│       └── • scan
│             sql nodes: <hidden>
│             kv nodes: <hidden>
│             regions: <hidden>
│             actual row count: 1
│             KV time: 0µs
│             KV contention time: 0µs
│             KV rows decoded: 1
│             KV pairs read: 2
│             KV bytes read: 8 B
│             KV gRPC calls: 1
│             estimated max memory allocated: 0 B
│             missing stats
│             table: loop_a@loop_a_pkey
│             spans: [/'loop_a-pk1' - /'loop_a-pk1']
│             locking strength: for update
│
└── • fk-cascade
    │ fk: loop_b_cascade_delete_fkey
    │
    └── • root
        │
        ├── • delete
        │   │ sql nodes: <hidden>
        │   │ regions: <hidden>
        │   │ actual row count: 0
        │   │ from: loop_b
        │   │
        │   └── • buffer
        │       │ sql nodes: <hidden>
        │       │ regions: <hidden>
        │       │ actual row count: 1
        │       │ label: buffer 1
        │       │
        │       └── • hash join (semi)
        │           │ sql nodes: <hidden>
        │           │ regions: <hidden>
        │           │ actual row count: 1
        │           │ estimated max memory allocated: 0 B
        │           │ estimated max sql temp disk usage: 0 B
        │           │ equality: (cascade_delete) = (id)
        │           │
        │           ├── • scan
        │           │     sql nodes: <hidden>
        │           │     kv nodes: <hidden>
        │           │     regions: <hidden>
        │           │     actual row count: 3
        │           │     KV time: 0µs
        │           │     KV contention time: 0µs
        │           │     KV rows decoded: 3
        │           │     KV pairs read: 6
        │           │     KV bytes read: 24 B
        │           │     KV gRPC calls: 3
        │           │     estimated max memory allocated: 0 B
        │           │     missing stats
        │           │     table: loop_b@loop_b_pkey
        │           │     spans: FULL SCAN
        │           │
        │           └── • scan buffer
        │                 sql nodes: <hidden>
        │                 regions: <hidden>
        │                 actual row count: 1
        │                 estimated row count: 1
        │                 label: buffer 1000000
        │
        └── • fk-cascade
            │ fk: cascade_delete_constraint
            │
            └── • root
                │
                ├── • delete
                │   │ sql nodes: <hidden>
                │   │ regions: <hidden>
                │   │ actual row count: 0
                │   │ from: loop_a
                │   │
                │   └── • buffer
                │       │ sql nodes: <hidden>
                │       │ regions: <hidden>
                │       │ actual row count: 1
                │       │ label: buffer 1
                │       │
                │       └── • lookup join
                │           │ sql nodes: <hidden>
                │           │ kv nodes: <hidden>
                │           │ regions: <hidden>
                │           │ actual row count: 1
                │           │ KV time: 0µs
                │           │ KV contention time: 0µs
                │           │ KV rows decoded: 1
                │           │ KV pairs read: 2
                │           │ KV bytes read: 8 B
                │           │ KV gRPC calls: 1
                │           │ estimated max memory allocated: 0 B
                │           │ table: loop_a@loop_a_cascade_delete_idx
                │           │ equality: (id) = (cascade_delete)
                │           │
                │           └── • distinct
                │               │ sql nodes: <hidden>
                │               │ regions: <hidden>
                │               │ actual row count: 1
                │               │ estimated max memory allocated: 0 B
                │               │ estimated max sql temp disk usage: 0 B
                │               │ estimated row count: 1
                │               │ distinct on: id
                │               │
                │               └── • scan buffer
                │                     sql nodes: <hidden>
                │                     regions: <hidden>
                │                     actual row count: 1
                │                     estimated row count: 1
                │                     label: buffer 1000000
                │
                └── • fk-cascade
                      fk: loop_b_cascade_delete_fkey
                      input: buffer 1

query II
SELECT
  (SELECT count(*) FROM loop_a)
 ,(SELECT count(*) FROM loop_b)
;
----
0 0

statement ok
CREATE TABLE t3 (
    id3 INT PRIMARY KEY
);
CREATE TABLE t4 (
    id4 INT PRIMARY KEY,
    t4_id3_1 INT NOT NULL,
    t4_id3_2 INT NOT NULL,
    CONSTRAINT fk1 FOREIGN KEY (t4_id3_1) REFERENCES t3(id3) ON DELETE CASCADE,
    CONSTRAINT fk2 FOREIGN KEY (t4_id3_2) REFERENCES t3(id3) ON DELETE CASCADE
);
CREATE TABLE t5 (
    id5 INT PRIMARY KEY,
    t5_id4 INT DEFAULT NULL,
    t5_id3_1 INT NOT NULL,
    t5_id3_2 INT NOT NULL,
    CONSTRAINT fk1 FOREIGN KEY (t5_id3_1) REFERENCES t3(id3) ON DELETE CASCADE,
    CONSTRAINT fk2 FOREIGN KEY (t5_id3_2) REFERENCES t3(id3) ON DELETE CASCADE,
    CONSTRAINT fk3 FOREIGN KEY (t5_id4) REFERENCES t4(id4) ON DELETE SET NULL
);

# Ensure that the plan is provided for each FK cascade, even if they originate
# from the same table or share names with constraints on other tables.
query T
EXPLAIN DELETE FROM t3 WHERE id3 = 1
----
distribution: local
vectorized: true
·
• root
│
├── • delete
│   │ from: t3
│   │
│   └── • buffer
│       │ label: buffer 1
│       │
│       └── • scan
│             missing stats
│             table: t3@t3_pkey
│             spans: [/1 - /1]
│             locking strength: for update
│
├── • fk-cascade
│   │ fk: fk1
│   │
│   └── • root
│       │
│       ├── • delete
│       │   │ from: t4
│       │   │
│       │   └── • buffer
│       │       │ label: buffer 1
│       │       │
│       │       └── • hash join
│       │           │ equality: (t4_id3_1) = (id3)
│       │           │ right cols are key
│       │           │
│       │           ├── • scan
│       │           │     missing stats
│       │           │     table: t4@t4_pkey
│       │           │     spans: FULL SCAN
│       │           │
│       │           └── • distinct
│       │               │ estimated row count: 10
│       │               │ distinct on: id3
│       │               │
│       │               └── • scan buffer
│       │                     estimated row count: 100
│       │                     label: buffer 1000000
│       │
│       └── • fk-cascade
│           │ fk: fk3
│           │
│           └── • update
│               │ table: t5
│               │ set: t5_id4
│               │
│               └── • render
│                   │
│                   └── • hash join
│                       │ equality: (t5_id4) = (id4)
│                       │ right cols are key
│                       │
│                       ├── • scan
│                       │     missing stats
│                       │     table: t5@t5_pkey
│                       │     spans: FULL SCAN
│                       │
│                       └── • distinct
│                           │ estimated row count: 10
│                           │ distinct on: id4
│                           │
│                           └── • scan buffer
│                                 estimated row count: 100
│                                 label: buffer 1000000
│
├── • fk-cascade
│   │ fk: fk2
│   │
│   └── • root
│       │
│       ├── • delete
│       │   │ from: t4
│       │   │
│       │   └── • buffer
│       │       │ label: buffer 1
│       │       │
│       │       └── • hash join
│       │           │ equality: (t4_id3_2) = (id3)
│       │           │ right cols are key
│       │           │
│       │           ├── • scan
│       │           │     missing stats
│       │           │     table: t4@t4_pkey
│       │           │     spans: FULL SCAN
│       │           │
│       │           └── • distinct
│       │               │ estimated row count: 10
│       │               │ distinct on: id3
│       │               │
│       │               └── • scan buffer
│       │                     estimated row count: 100
│       │                     label: buffer 1000000
│       │
│       └── • fk-cascade
│           │ fk: fk3
│           │
│           └── • update
│               │ table: t5
│               │ set: t5_id4
│               │
│               └── • render
│                   │
│                   └── • hash join
│                       │ equality: (t5_id4) = (id4)
│                       │ right cols are key
│                       │
│                       ├── • scan
│                       │     missing stats
│                       │     table: t5@t5_pkey
│                       │     spans: FULL SCAN
│                       │
│                       └── • distinct
│                           │ estimated row count: 10
│                           │ distinct on: id4
│                           │
│                           └── • scan buffer
│                                 estimated row count: 100
│                                 label: buffer 1000000
│
├── • fk-cascade
│   │ fk: fk1
│   │
│   └── • delete
│       │ from: t5
│       │
│       └── • hash join
│           │ equality: (t5_id3_1) = (id3)
│           │ right cols are key
│           │
│           ├── • scan
│           │     missing stats
│           │     table: t5@t5_pkey
│           │     spans: FULL SCAN
│           │
│           └── • distinct
│               │ estimated row count: 10
│               │ distinct on: id3
│               │
│               └── • scan buffer
│                     estimated row count: 100
│                     label: buffer 1000000
│
└── • fk-cascade
    │ fk: fk2
    │
    └── • delete
        │ from: t5
        │
        └── • hash join
            │ equality: (t5_id3_2) = (id3)
            │ right cols are key
            │
            ├── • scan
            │     missing stats
            │     table: t5@t5_pkey
            │     spans: FULL SCAN
            │
            └── • distinct
                │ estimated row count: 10
                │ distinct on: id3
                │
                └── • scan buffer
                      estimated row count: 100
                      label: buffer 1000000
