github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/exec/execbuilder/testdata/fk_opt (about)

     1  # LogicTest: local
     2  
     3  statement ok
     4  SET optimizer_foreign_keys = true
     5  
     6  # We will test the fast path later.
     7  statement ok
     8  SET enable_insert_fast_path = false
     9  
    10  # -- Tests with INSERT --
    11  
    12  statement ok
    13  CREATE TABLE parent (p INT PRIMARY KEY, other INT UNIQUE, FAMILY (p, other))
    14  
    15  statement ok
    16  CREATE TABLE child (c INT PRIMARY KEY, p INT NOT NULL REFERENCES parent(p), FAMILY (c, p))
    17  
    18  query TTT
    19  EXPLAIN INSERT INTO child VALUES (1,1), (2,2)
    20  ----
    21  ·                                          distributed            false
    22  ·                                          vectorized             false
    23  root                                       ·                      ·
    24   ├── count                                 ·                      ·
    25   │    └── insert                           ·                      ·
    26   │         │                               into                   child(c, p)
    27   │         │                               strategy               inserter
    28   │         └── buffer node                 ·                      ·
    29   │              │                          label                  buffer 1
    30   │              └── values                 ·                      ·
    31   │                                         size                   2 columns, 2 rows
    32   └── fk-check                              ·                      ·
    33        └── error if rows                    ·                      ·
    34             └── lookup-join                 ·                      ·
    35                  │                          table                  parent@primary
    36                  │                          type                   anti
    37                  │                          equality               (column2) = (p)
    38                  │                          equality cols are key  ·
    39                  │                          parallel               ·
    40                  └── render                 ·                      ·
    41                       └── scan buffer node  ·                      ·
    42  ·                                          label                  buffer 1
    43  
    44  # Use data from a different table as input.
    45  statement ok
    46  CREATE TABLE xy (x INT, y INT)
    47  
    48  query TTT
    49  EXPLAIN INSERT INTO child SELECT x,y FROM xy
    50  ----
    51  ·                                          distributed         false
    52  ·                                          vectorized          false
    53  root                                       ·                   ·
    54   ├── count                                 ·                   ·
    55   │    └── insert                           ·                   ·
    56   │         │                               into                child(c, p)
    57   │         │                               strategy            inserter
    58   │         └── buffer node                 ·                   ·
    59   │              │                          label               buffer 1
    60   │              └── scan                   ·                   ·
    61   │                                         table               xy@primary
    62   │                                         spans               FULL SCAN
    63   └── fk-check                              ·                   ·
    64        └── error if rows                    ·                   ·
    65             └── hash-join                   ·                   ·
    66                  │                          type                anti
    67                  │                          equality            (y) = (p)
    68                  │                          right cols are key  ·
    69                  ├── render                 ·                   ·
    70                  │    └── scan buffer node  ·                   ·
    71                  │                          label               buffer 1
    72                  └── scan                   ·                   ·
    73  ·                                          table               parent@primary
    74  ·                                          spans               FULL SCAN
    75  
    76  statement ok
    77  CREATE TABLE child_nullable (c INT PRIMARY KEY, p INT REFERENCES parent(p));
    78  
    79  # Because the input column can be NULL (in which case it requires no FK match),
    80  # we have to add an extra filter.
    81  query TTT
    82  EXPLAIN INSERT INTO child_nullable VALUES (100, 1), (200, NULL)
    83  ----
    84  ·                                               distributed            false
    85  ·                                               vectorized             false
    86  root                                            ·                      ·
    87   ├── count                                      ·                      ·
    88   │    └── insert                                ·                      ·
    89   │         │                                    into                   child_nullable(c, p)
    90   │         │                                    strategy               inserter
    91   │         └── buffer node                      ·                      ·
    92   │              │                               label                  buffer 1
    93   │              └── values                      ·                      ·
    94   │                                              size                   2 columns, 2 rows
    95   └── fk-check                                   ·                      ·
    96        └── error if rows                         ·                      ·
    97             └── lookup-join                      ·                      ·
    98                  │                               table                  parent@primary
    99                  │                               type                   anti
   100                  │                               equality               (column2) = (p)
   101                  │                               equality cols are key  ·
   102                  │                               parallel               ·
   103                  └── filter                      ·                      ·
   104                       │                          filter                 column2 IS NOT NULL
   105                       └── render                 ·                      ·
   106                            └── scan buffer node  ·                      ·
   107  ·                                               label                  buffer 1
   108  
   109  # Tests with multicolumn FKs.
   110  statement ok
   111  CREATE TABLE multi_col_parent (p INT, q INT, r INT, other INT, PRIMARY KEY (p, q, r))
   112  
   113  statement ok
   114  CREATE TABLE multi_col_child  (
   115    c INT PRIMARY KEY,
   116    p INT, q INT, r INT,
   117    CONSTRAINT fk FOREIGN KEY (p,q,r) REFERENCES multi_col_parent(p,q,r) MATCH SIMPLE
   118  )
   119  
   120  # Only p and q are nullable.
   121  query TTT
   122  EXPLAIN INSERT INTO multi_col_child VALUES (2, NULL, 20, 20), (3, 20, NULL, 20)
   123  ----
   124  ·                                               distributed            false
   125  ·                                               vectorized             false
   126  root                                            ·                      ·
   127   ├── count                                      ·                      ·
   128   │    └── insert                                ·                      ·
   129   │         │                                    into                   multi_col_child(c, p, q, r)
   130   │         │                                    strategy               inserter
   131   │         └── buffer node                      ·                      ·
   132   │              │                               label                  buffer 1
   133   │              └── values                      ·                      ·
   134   │                                              size                   4 columns, 2 rows
   135   └── fk-check                                   ·                      ·
   136        └── error if rows                         ·                      ·
   137             └── lookup-join                      ·                      ·
   138                  │                               table                  multi_col_parent@primary
   139                  │                               type                   anti
   140                  │                               equality               (column2, column3, column4) = (p, q, r)
   141                  │                               equality cols are key  ·
   142                  │                               parallel               ·
   143                  └── filter                      ·                      ·
   144                       │                          filter                 (column2 IS NOT NULL) AND (column3 IS NOT NULL)
   145                       └── render                 ·                      ·
   146                            └── scan buffer node  ·                      ·
   147  ·                                               label                  buffer 1
   148  
   149  statement ok
   150  CREATE TABLE multi_ref_parent_a (a INT PRIMARY KEY, other INT)
   151  
   152  statement ok
   153  CREATE TABLE multi_ref_parent_bc (b INT, c INT, PRIMARY KEY (b,c), other INT)
   154  
   155  statement ok
   156  CREATE TABLE multi_ref_child (
   157    k INT PRIMARY KEY,
   158    a INT,
   159    b INT,
   160    c INT,
   161    CONSTRAINT fk1 FOREIGN KEY (a) REFERENCES multi_ref_parent_a(a),
   162    CONSTRAINT fk2 FOREIGN KEY (b,c) REFERENCES multi_ref_parent_bc(b,c)
   163  )
   164  
   165  query TTT
   166  EXPLAIN INSERT INTO multi_ref_child VALUES (1, NULL, NULL, NULL), (2, 3, 4, 5)
   167  ----
   168  ·                                               distributed            false
   169  ·                                               vectorized             false
   170  root                                            ·                      ·
   171   ├── count                                      ·                      ·
   172   │    └── insert                                ·                      ·
   173   │         │                                    into                   multi_ref_child(k, a, b, c)
   174   │         │                                    strategy               inserter
   175   │         └── buffer node                      ·                      ·
   176   │              │                               label                  buffer 1
   177   │              └── values                      ·                      ·
   178   │                                              size                   4 columns, 2 rows
   179   ├── fk-check                                   ·                      ·
   180   │    └── error if rows                         ·                      ·
   181   │         └── lookup-join                      ·                      ·
   182   │              │                               table                  multi_ref_parent_a@primary
   183   │              │                               type                   anti
   184   │              │                               equality               (column2) = (a)
   185   │              │                               equality cols are key  ·
   186   │              │                               parallel               ·
   187   │              └── filter                      ·                      ·
   188   │                   │                          filter                 column2 IS NOT NULL
   189   │                   └── render                 ·                      ·
   190   │                        └── scan buffer node  ·                      ·
   191   │                                              label                  buffer 1
   192   └── fk-check                                   ·                      ·
   193        └── error if rows                         ·                      ·
   194             └── lookup-join                      ·                      ·
   195                  │                               table                  multi_ref_parent_bc@primary
   196                  │                               type                   anti
   197                  │                               equality               (column3, column4) = (b, c)
   198                  │                               equality cols are key  ·
   199                  │                               parallel               ·
   200                  └── filter                      ·                      ·
   201                       │                          filter                 (column3 IS NOT NULL) AND (column4 IS NOT NULL)
   202                       └── render                 ·                      ·
   203                            └── scan buffer node  ·                      ·
   204  ·                                               label                  buffer 1
   205  
   206  # FK check can be omitted when we are inserting only NULLs.
   207  query TTT
   208  EXPLAIN INSERT INTO multi_ref_child VALUES (1, NULL, NULL, NULL)
   209  ----
   210  ·                 distributed  false
   211  ·                 vectorized   false
   212  count             ·            ·
   213   └── insert       ·            ·
   214        │           into         multi_ref_child(k, a, b, c)
   215        │           strategy     inserter
   216        │           auto commit  ·
   217        └── values  ·            ·
   218  ·                 size         4 columns, 1 row
   219  
   220  # -- Tests with DELETE --
   221  
   222  query TTT
   223  EXPLAIN DELETE FROM parent WHERE p = 3
   224  ----
   225  ·                                          distributed  false
   226  ·                                          vectorized   false
   227  root                                       ·            ·
   228   ├── count                                 ·            ·
   229   │    └── delete                           ·            ·
   230   │         │                               from         parent
   231   │         │                               strategy     deleter
   232   │         └── buffer node                 ·            ·
   233   │              │                          label        buffer 1
   234   │              └── scan                   ·            ·
   235   │                                         table        parent@primary
   236   │                                         spans        /3-/3/#
   237   ├── fk-check                              ·            ·
   238   │    └── error if rows                    ·            ·
   239   │         └── lookup-join                 ·            ·
   240   │              │                          table        child@child_auto_index_fk_p_ref_parent
   241   │              │                          type         semi
   242   │              │                          equality     (p) = (p)
   243   │              └── render                 ·            ·
   244   │                   └── scan buffer node  ·            ·
   245   │                                         label        buffer 1
   246   └── fk-check                              ·            ·
   247        └── error if rows                    ·            ·
   248             └── lookup-join                 ·            ·
   249                  │                          table        child_nullable@child_nullable_auto_index_fk_p_ref_parent
   250                  │                          type         semi
   251                  │                          equality     (p) = (p)
   252                  └── render                 ·            ·
   253                       └── scan buffer node  ·            ·
   254  ·                                          label        buffer 1
   255  
   256  statement ok
   257  CREATE TABLE child2 (c INT PRIMARY KEY, p INT NOT NULL REFERENCES parent(other))
   258  
   259  query TTT
   260  EXPLAIN DELETE FROM parent WHERE p = 3
   261  ----
   262  ·                                          distributed  false
   263  ·                                          vectorized   false
   264  root                                       ·            ·
   265   ├── count                                 ·            ·
   266   │    └── delete                           ·            ·
   267   │         │                               from         parent
   268   │         │                               strategy     deleter
   269   │         └── buffer node                 ·            ·
   270   │              │                          label        buffer 1
   271   │              └── scan                   ·            ·
   272   │                                         table        parent@primary
   273   │                                         spans        /3-/3/#
   274   ├── fk-check                              ·            ·
   275   │    └── error if rows                    ·            ·
   276   │         └── lookup-join                 ·            ·
   277   │              │                          table        child@child_auto_index_fk_p_ref_parent
   278   │              │                          type         semi
   279   │              │                          equality     (p) = (p)
   280   │              └── render                 ·            ·
   281   │                   └── scan buffer node  ·            ·
   282   │                                         label        buffer 1
   283   ├── fk-check                              ·            ·
   284   │    └── error if rows                    ·            ·
   285   │         └── lookup-join                 ·            ·
   286   │              │                          table        child_nullable@child_nullable_auto_index_fk_p_ref_parent
   287   │              │                          type         semi
   288   │              │                          equality     (p) = (p)
   289   │              └── render                 ·            ·
   290   │                   └── scan buffer node  ·            ·
   291   │                                         label        buffer 1
   292   └── fk-check                              ·            ·
   293        └── error if rows                    ·            ·
   294             └── lookup-join                 ·            ·
   295                  │                          table        child2@child2_auto_index_fk_p_ref_parent
   296                  │                          type         semi
   297                  │                          equality     (other) = (p)
   298                  └── render                 ·            ·
   299                       └── scan buffer node  ·            ·
   300  ·                                          label        buffer 1
   301  
   302  statement ok
   303  CREATE TABLE doubleparent (p1 INT, p2 INT, other INT, PRIMARY KEY (p1, p2))
   304  
   305  statement ok
   306  CREATE TABLE doublechild (
   307    c INT8 PRIMARY KEY,
   308    p1 INT8,
   309    p2 INT8,
   310    FOREIGN KEY (p1, p2) REFERENCES doubleparent (p1, p2)
   311  )
   312  
   313  query TTT
   314  EXPLAIN DELETE FROM doubleparent WHERE p1 = 10
   315  ----
   316  ·                                     distributed  false
   317  ·                                     vectorized   false
   318  root                                  ·            ·
   319   ├── count                            ·            ·
   320   │    └── delete                      ·            ·
   321   │         │                          from         doubleparent
   322   │         │                          strategy     deleter
   323   │         └── buffer node            ·            ·
   324   │              │                     label        buffer 1
   325   │              └── scan              ·            ·
   326   │                                    table        doubleparent@primary
   327   │                                    spans        /10-/11
   328   └── fk-check                         ·            ·
   329        └── error if rows               ·            ·
   330             └── lookup-join            ·            ·
   331                  │                     table        doublechild@doublechild_auto_index_fk_p1_ref_doubleparent
   332                  │                     type         semi
   333                  │                     equality     (p1, p2) = (p1, p2)
   334                  └── scan buffer node  ·            ·
   335  ·                                     label        buffer 1
   336  
   337  # -- Tests with UPDATE --
   338  
   339  query TTT
   340  EXPLAIN UPDATE child SET p = 4
   341  ----
   342  ·                                          distributed         false
   343  ·                                          vectorized          false
   344  root                                       ·                   ·
   345   ├── count                                 ·                   ·
   346   │    └── update                           ·                   ·
   347   │         │                               table               child
   348   │         │                               set                 p
   349   │         │                               strategy            updater
   350   │         └── buffer node                 ·                   ·
   351   │              │                          label               buffer 1
   352   │              └── render                 ·                   ·
   353   │                   └── scan              ·                   ·
   354   │                                         table               child@primary
   355   │                                         spans               FULL SCAN
   356   │                                         locking strength    for update
   357   └── fk-check                              ·                   ·
   358        └── error if rows                    ·                   ·
   359             └── hash-join                   ·                   ·
   360                  │                          type                anti
   361                  │                          equality            (p_new) = (p)
   362                  │                          right cols are key  ·
   363                  ├── render                 ·                   ·
   364                  │    └── scan buffer node  ·                   ·
   365                  │                          label               buffer 1
   366                  └── scan                   ·                   ·
   367  ·                                          table               parent@primary
   368  ·                                          spans               FULL SCAN
   369  
   370  query TTT
   371  EXPLAIN UPDATE child SET p = 4 WHERE c = 10
   372  ----
   373  ·                                          distributed            false
   374  ·                                          vectorized             false
   375  root                                       ·                      ·
   376   ├── count                                 ·                      ·
   377   │    └── update                           ·                      ·
   378   │         │                               table                  child
   379   │         │                               set                    p
   380   │         │                               strategy               updater
   381   │         └── buffer node                 ·                      ·
   382   │              │                          label                  buffer 1
   383   │              └── render                 ·                      ·
   384   │                   └── scan              ·                      ·
   385   │                                         table                  child@primary
   386   │                                         spans                  /10-/10/#
   387   │                                         locking strength       for update
   388   └── fk-check                              ·                      ·
   389        └── error if rows                    ·                      ·
   390             └── lookup-join                 ·                      ·
   391                  │                          table                  parent@primary
   392                  │                          type                   anti
   393                  │                          equality               (p_new) = (p)
   394                  │                          equality cols are key  ·
   395                  │                          parallel               ·
   396                  └── render                 ·                      ·
   397                       └── scan buffer node  ·                      ·
   398  ·                                          label                  buffer 1
   399  
   400  query TTT
   401  EXPLAIN UPDATE parent SET p = p+1
   402  ----
   403  ·                                                    distributed         false
   404  ·                                                    vectorized          false
   405  root                                                 ·                   ·
   406   ├── count                                           ·                   ·
   407   │    └── update                                     ·                   ·
   408   │         │                                         table               parent
   409   │         │                                         set                 p
   410   │         │                                         strategy            updater
   411   │         └── buffer node                           ·                   ·
   412   │              │                                    label               buffer 1
   413   │              └── render                           ·                   ·
   414   │                   └── scan                        ·                   ·
   415   │                                                   table               parent@primary
   416   │                                                   spans               FULL SCAN
   417   │                                                   locking strength    for update
   418   ├── fk-check                                        ·                   ·
   419   │    └── error if rows                              ·                   ·
   420   │         └── render                                ·                   ·
   421   │              └── hash-join                        ·                   ·
   422   │                   │                               type                inner
   423   │                   │                               equality            (p) = (p)
   424   │                   │                               left cols are key   ·
   425   │                   │                               right cols are key  ·
   426   │                   ├── union                       ·                   ·
   427   │                   │    ├── render                 ·                   ·
   428   │                   │    │    └── scan buffer node  ·                   ·
   429   │                   │    │                          label               buffer 1
   430   │                   │    └── render                 ·                   ·
   431   │                   │         └── scan buffer node  ·                   ·
   432   │                   │                               label               buffer 1
   433   │                   └── distinct                    ·                   ·
   434   │                        │                          distinct on         p
   435   │                        │                          order key           p
   436   │                        └── scan                   ·                   ·
   437   │                                                   table               child@child_auto_index_fk_p_ref_parent
   438   │                                                   spans               FULL SCAN
   439   └── fk-check                                        ·                   ·
   440        └── error if rows                              ·                   ·
   441             └── render                                ·                   ·
   442                  └── hash-join                        ·                   ·
   443                       │                               type                inner
   444                       │                               equality            (p) = (p)
   445                       │                               left cols are key   ·
   446                       │                               right cols are key  ·
   447                       ├── union                       ·                   ·
   448                       │    ├── render                 ·                   ·
   449                       │    │    └── scan buffer node  ·                   ·
   450                       │    │                          label               buffer 1
   451                       │    └── render                 ·                   ·
   452                       │         └── scan buffer node  ·                   ·
   453                       │                               label               buffer 1
   454                       └── distinct                    ·                   ·
   455                            │                          distinct on         p
   456                            │                          order key           p
   457                            └── scan                   ·                   ·
   458  ·                                                    table               child_nullable@child_nullable_auto_index_fk_p_ref_parent
   459  ·                                                    spans               FULL SCAN
   460  
   461  query TTT
   462  EXPLAIN UPDATE parent SET p = p+1 WHERE other = 10
   463  ----
   464  ·                                               distributed       false
   465  ·                                               vectorized        false
   466  root                                            ·                 ·
   467   ├── count                                      ·                 ·
   468   │    └── update                                ·                 ·
   469   │         │                                    table             parent
   470   │         │                                    set               p
   471   │         │                                    strategy          updater
   472   │         └── buffer node                      ·                 ·
   473   │              │                               label             buffer 1
   474   │              └── render                      ·                 ·
   475   │                   └── scan                   ·                 ·
   476   │                                              table             parent@parent_other_key
   477   │                                              spans             /10-/11
   478   │                                              locking strength  for update
   479   ├── fk-check                                   ·                 ·
   480   │    └── error if rows                         ·                 ·
   481   │         └── lookup-join                      ·                 ·
   482   │              │                               table             child@child_auto_index_fk_p_ref_parent
   483   │              │                               type              semi
   484   │              │                               equality          (p) = (p)
   485   │              └── union                       ·                 ·
   486   │                   ├── render                 ·                 ·
   487   │                   │    └── scan buffer node  ·                 ·
   488   │                   │                          label             buffer 1
   489   │                   └── render                 ·                 ·
   490   │                        └── scan buffer node  ·                 ·
   491   │                                              label             buffer 1
   492   └── fk-check                                   ·                 ·
   493        └── error if rows                         ·                 ·
   494             └── lookup-join                      ·                 ·
   495                  │                               table             child_nullable@child_nullable_auto_index_fk_p_ref_parent
   496                  │                               type              semi
   497                  │                               equality          (p) = (p)
   498                  └── union                       ·                 ·
   499                       ├── render                 ·                 ·
   500                       │    └── scan buffer node  ·                 ·
   501                       │                          label             buffer 1
   502                       └── render                 ·                 ·
   503                            └── scan buffer node  ·                 ·
   504  ·                                               label             buffer 1
   505  
   506  statement ok
   507  CREATE TABLE grandchild (g INT PRIMARY KEY, c INT NOT NULL REFERENCES child(c))
   508  
   509  query TTT
   510  EXPLAIN UPDATE child SET c = 4
   511  ----
   512  ·                                                    distributed         false
   513  ·                                                    vectorized          false
   514  root                                                 ·                   ·
   515   ├── count                                           ·                   ·
   516   │    └── update                                     ·                   ·
   517   │         │                                         table               child
   518   │         │                                         set                 c
   519   │         │                                         strategy            updater
   520   │         └── buffer node                           ·                   ·
   521   │              │                                    label               buffer 1
   522   │              └── render                           ·                   ·
   523   │                   └── scan                        ·                   ·
   524   │                                                   table               child@primary
   525   │                                                   spans               FULL SCAN
   526   │                                                   locking strength    for update
   527   └── fk-check                                        ·                   ·
   528        └── error if rows                              ·                   ·
   529             └── render                                ·                   ·
   530                  └── hash-join                        ·                   ·
   531                       │                               type                inner
   532                       │                               equality            (c) = (c)
   533                       │                               left cols are key   ·
   534                       │                               right cols are key  ·
   535                       ├── union                       ·                   ·
   536                       │    ├── render                 ·                   ·
   537                       │    │    └── scan buffer node  ·                   ·
   538                       │    │                          label               buffer 1
   539                       │    └── render                 ·                   ·
   540                       │         └── scan buffer node  ·                   ·
   541                       │                               label               buffer 1
   542                       └── distinct                    ·                   ·
   543                            │                          distinct on         c
   544                            │                          order key           c
   545                            └── scan                   ·                   ·
   546  ·                                                    table               grandchild@grandchild_auto_index_fk_c_ref_child
   547  ·                                                    spans               FULL SCAN
   548  
   549  # This update shouldn't emit checks for c, since it's unchanged.
   550  query TTT
   551  EXPLAIN UPDATE child SET p = 4
   552  ----
   553  ·                                          distributed         false
   554  ·                                          vectorized          false
   555  root                                       ·                   ·
   556   ├── count                                 ·                   ·
   557   │    └── update                           ·                   ·
   558   │         │                               table               child
   559   │         │                               set                 p
   560   │         │                               strategy            updater
   561   │         └── buffer node                 ·                   ·
   562   │              │                          label               buffer 1
   563   │              └── render                 ·                   ·
   564   │                   └── scan              ·                   ·
   565   │                                         table               child@primary
   566   │                                         spans               FULL SCAN
   567   │                                         locking strength    for update
   568   └── fk-check                              ·                   ·
   569        └── error if rows                    ·                   ·
   570             └── hash-join                   ·                   ·
   571                  │                          type                anti
   572                  │                          equality            (p_new) = (p)
   573                  │                          right cols are key  ·
   574                  ├── render                 ·                   ·
   575                  │    └── scan buffer node  ·                   ·
   576                  │                          label               buffer 1
   577                  └── scan                   ·                   ·
   578  ·                                          table               parent@primary
   579  ·                                          spans               FULL SCAN
   580  
   581  query TTT
   582  EXPLAIN UPDATE child SET p = p
   583  ----
   584  ·                                          distributed         false
   585  ·                                          vectorized          false
   586  root                                       ·                   ·
   587   ├── count                                 ·                   ·
   588   │    └── update                           ·                   ·
   589   │         │                               table               child
   590   │         │                               set                 p
   591   │         │                               strategy            updater
   592   │         └── buffer node                 ·                   ·
   593   │              │                          label               buffer 1
   594   │              └── render                 ·                   ·
   595   │                   └── scan              ·                   ·
   596   │                                         table               child@primary
   597   │                                         spans               FULL SCAN
   598   │                                         locking strength    for update
   599   └── fk-check                              ·                   ·
   600        └── error if rows                    ·                   ·
   601             └── hash-join                   ·                   ·
   602                  │                          type                anti
   603                  │                          equality            (p) = (p)
   604                  │                          right cols are key  ·
   605                  ├── render                 ·                   ·
   606                  │    └── scan buffer node  ·                   ·
   607                  │                          label               buffer 1
   608                  └── scan                   ·                   ·
   609  ·                                          table               parent@primary
   610  ·                                          spans               FULL SCAN
   611  
   612  query TTT
   613  EXPLAIN UPDATE child SET p = p+1, c = c+1
   614  ----
   615  ·                                                    distributed         false
   616  ·                                                    vectorized          false
   617  root                                                 ·                   ·
   618   ├── count                                           ·                   ·
   619   │    └── update                                     ·                   ·
   620   │         │                                         table               child
   621   │         │                                         set                 c, p
   622   │         │                                         strategy            updater
   623   │         └── buffer node                           ·                   ·
   624   │              │                                    label               buffer 1
   625   │              └── render                           ·                   ·
   626   │                   └── scan                        ·                   ·
   627   │                                                   table               child@primary
   628   │                                                   spans               FULL SCAN
   629   │                                                   locking strength    for update
   630   ├── fk-check                                        ·                   ·
   631   │    └── error if rows                              ·                   ·
   632   │         └── hash-join                             ·                   ·
   633   │              │                                    type                anti
   634   │              │                                    equality            (p_new) = (p)
   635   │              │                                    right cols are key  ·
   636   │              ├── render                           ·                   ·
   637   │              │    └── scan buffer node            ·                   ·
   638   │              │                                    label               buffer 1
   639   │              └── scan                             ·                   ·
   640   │                                                   table               parent@primary
   641   │                                                   spans               FULL SCAN
   642   └── fk-check                                        ·                   ·
   643        └── error if rows                              ·                   ·
   644             └── render                                ·                   ·
   645                  └── hash-join                        ·                   ·
   646                       │                               type                inner
   647                       │                               equality            (c) = (c)
   648                       │                               left cols are key   ·
   649                       │                               right cols are key  ·
   650                       ├── union                       ·                   ·
   651                       │    ├── render                 ·                   ·
   652                       │    │    └── scan buffer node  ·                   ·
   653                       │    │                          label               buffer 1
   654                       │    └── render                 ·                   ·
   655                       │         └── scan buffer node  ·                   ·
   656                       │                               label               buffer 1
   657                       └── distinct                    ·                   ·
   658                            │                          distinct on         c
   659                            │                          order key           c
   660                            └── scan                   ·                   ·
   661  ·                                                    table               grandchild@grandchild_auto_index_fk_c_ref_child
   662  ·                                                    spans               FULL SCAN
   663  
   664  # Multiple grandchild tables
   665  statement ok
   666  CREATE TABLE grandchild2 (g INT PRIMARY KEY, c INT NOT NULL REFERENCES child(c))
   667  
   668  query TTT
   669  EXPLAIN UPDATE child SET p = 4
   670  ----
   671  ·                                          distributed         false
   672  ·                                          vectorized          false
   673  root                                       ·                   ·
   674   ├── count                                 ·                   ·
   675   │    └── update                           ·                   ·
   676   │         │                               table               child
   677   │         │                               set                 p
   678   │         │                               strategy            updater
   679   │         └── buffer node                 ·                   ·
   680   │              │                          label               buffer 1
   681   │              └── render                 ·                   ·
   682   │                   └── scan              ·                   ·
   683   │                                         table               child@primary
   684   │                                         spans               FULL SCAN
   685   │                                         locking strength    for update
   686   └── fk-check                              ·                   ·
   687        └── error if rows                    ·                   ·
   688             └── hash-join                   ·                   ·
   689                  │                          type                anti
   690                  │                          equality            (p_new) = (p)
   691                  │                          right cols are key  ·
   692                  ├── render                 ·                   ·
   693                  │    └── scan buffer node  ·                   ·
   694                  │                          label               buffer 1
   695                  └── scan                   ·                   ·
   696  ·                                          table               parent@primary
   697  ·                                          spans               FULL SCAN
   698  
   699  statement ok
   700  CREATE TABLE self (x INT PRIMARY KEY, y INT NOT NULL REFERENCES self(x))
   701  
   702  query TTT
   703  EXPLAIN UPDATE self SET y = 3
   704  ----
   705  ·                                          distributed         false
   706  ·                                          vectorized          false
   707  root                                       ·                   ·
   708   ├── count                                 ·                   ·
   709   │    └── update                           ·                   ·
   710   │         │                               table               self
   711   │         │                               set                 y
   712   │         │                               strategy            updater
   713   │         └── buffer node                 ·                   ·
   714   │              │                          label               buffer 1
   715   │              └── render                 ·                   ·
   716   │                   └── scan              ·                   ·
   717   │                                         table               self@primary
   718   │                                         spans               FULL SCAN
   719   │                                         locking strength    for update
   720   └── fk-check                              ·                   ·
   721        └── error if rows                    ·                   ·
   722             └── hash-join                   ·                   ·
   723                  │                          type                anti
   724                  │                          equality            (y_new) = (x)
   725                  │                          right cols are key  ·
   726                  ├── render                 ·                   ·
   727                  │    └── scan buffer node  ·                   ·
   728                  │                          label               buffer 1
   729                  └── scan                   ·                   ·
   730  ·                                          table               self@primary
   731  ·                                          spans               FULL SCAN
   732  
   733  query TTT
   734  EXPLAIN UPDATE self SET x = 3
   735  ----
   736  ·                                                    distributed         false
   737  ·                                                    vectorized          false
   738  root                                                 ·                   ·
   739   ├── count                                           ·                   ·
   740   │    └── update                                     ·                   ·
   741   │         │                                         table               self
   742   │         │                                         set                 x
   743   │         │                                         strategy            updater
   744   │         └── buffer node                           ·                   ·
   745   │              │                                    label               buffer 1
   746   │              └── render                           ·                   ·
   747   │                   └── scan                        ·                   ·
   748   │                                                   table               self@primary
   749   │                                                   spans               FULL SCAN
   750   │                                                   locking strength    for update
   751   └── fk-check                                        ·                   ·
   752        └── error if rows                              ·                   ·
   753             └── render                                ·                   ·
   754                  └── hash-join                        ·                   ·
   755                       │                               type                inner
   756                       │                               equality            (x) = (y)
   757                       │                               left cols are key   ·
   758                       │                               right cols are key  ·
   759                       ├── union                       ·                   ·
   760                       │    ├── render                 ·                   ·
   761                       │    │    └── scan buffer node  ·                   ·
   762                       │    │                          label               buffer 1
   763                       │    └── render                 ·                   ·
   764                       │         └── scan buffer node  ·                   ·
   765                       │                               label               buffer 1
   766                       └── distinct                    ·                   ·
   767                            │                          distinct on         y
   768                            │                          order key           y
   769                            └── scan                   ·                   ·
   770  ·                                                    table               self@self_auto_index_fk_y_ref_self
   771  ·                                                    spans               FULL SCAN
   772  
   773  # Tests for the insert fast path.
   774  statement ok
   775  SET enable_insert_fast_path = true
   776  
   777  # Simple insert with VALUES should use the fast path.
   778  query TTTTT
   779  EXPLAIN (VERBOSE) INSERT INTO child VALUES (1,1), (2,2)
   780  ----
   781  ·                      distributed    false              ·   ·
   782  ·                      vectorized     false              ·   ·
   783  count                  ·              ·                  ()  ·
   784   └── insert-fast-path  ·              ·                  ()  ·
   785  ·                      into           child(c, p)        ·   ·
   786  ·                      strategy       inserter           ·   ·
   787  ·                      auto commit    ·                  ·   ·
   788  ·                      FK check       parent@primary     ·   ·
   789  ·                      size           2 columns, 2 rows  ·   ·
   790  ·                      row 0, expr 0  1                  ·   ·
   791  ·                      row 0, expr 1  1                  ·   ·
   792  ·                      row 1, expr 0  2                  ·   ·
   793  ·                      row 1, expr 1  2                  ·   ·
   794  
   795  # We shouldn't use the fast path if the VALUES columns are not in order.
   796  query B 
   797  SELECT count(*) > 0 FROM [
   798    EXPLAIN INSERT INTO child (SELECT b, a FROM (VALUES (1,2)) AS v(a,b))
   799  ] WHERE tree LIKE '%insert-fast-path'
   800  ----
   801  false
   802  
   803  # Multiple mutations shouldn't use the fast-path.
   804  query B
   805  SELECT count(*) > 0 FROM [
   806    EXPLAIN WITH cte AS (INSERT INTO child VALUES (1, 1) RETURNING p)
   807    INSERT INTO parent VALUES (2, 3)
   808  ] WHERE tree LIKE '%insert-fast-path'
   809  ----
   810  false
   811  
   812  # Self-referencing FKs should not use the fast-path.
   813  query B
   814  SELECT count(*) > 0 FROM [
   815    EXPLAIN INSERT INTO self VALUES (1, 1)
   816  ] WHERE tree LIKE '%insert-fast-path'
   817  ----
   818  false
   819  
   820  # We should not use the fast path If the best FK check plan is not a lookup
   821  # join. We do this by adding statistics that make a hash join more desirable.
   822  statement ok
   823  ALTER TABLE parent INJECT STATISTICS '[
   824    {
   825      "columns": ["p"],
   826      "created_at": "2018-01-01 1:00:00.00000+00:00",
   827      "row_count": 1,
   828      "distinct_count": 1
   829    }
   830  ]'
   831  
   832  query B
   833  SELECT count(*) > 0 FROM [
   834    EXPLAIN (VERBOSE) INSERT INTO child VALUES (1,1), (2,2), (3,3), (4,4)
   835  ] WHERE tree LIKE '%insert-fast-path'
   836  ----
   837  false
   838  
   839  # Test FK check that is using a non-unique index (#43969). In this case the
   840  # index on k2 is preferred because it doesn't contain unnecessary column v.
   841  statement ok
   842  CREATE TABLE nonunique_idx_parent (
   843    k1 INT,
   844    k2 INT,
   845    v INT,
   846    CONSTRAINT "primary" PRIMARY KEY (k1, k2),
   847    INDEX (k2)
   848  )
   849  
   850  statement ok
   851  CREATE TABLE nonunique_idx_child (
   852    k INT PRIMARY KEY,
   853    ref1 INT,
   854    ref2 INT,
   855    CONSTRAINT "fk" FOREIGN KEY (ref1, ref2) REFERENCES nonunique_idx_parent (k1, k2)
   856  )
   857  
   858  query TTT
   859  EXPLAIN INSERT INTO nonunique_idx_child VALUES (0, 1, 10)
   860  ----
   861  ·                      distributed  false
   862  ·                      vectorized   false
   863  count                  ·            ·
   864   └── insert-fast-path  ·            ·
   865  ·                      into         nonunique_idx_child(k, ref1, ref2)
   866  ·                      strategy     inserter
   867  ·                      auto commit  ·
   868  ·                      FK check     nonunique_idx_parent@nonunique_idx_parent_k2_idx
   869  ·                      size         3 columns, 1 row
   870  
   871  # Regression test for #46397: upserter was looking at the incorrect ordinal for
   872  # the check because of an extra input column used by the FK check.
   873  statement ok
   874  CREATE TABLE t46397_parent(p INT PRIMARY KEY)
   875  
   876  statement ok
   877  CREATE TABLE t46397_child (
   878    c INT PRIMARY KEY,
   879    p INT DEFAULT 0 REFERENCES t46397_parent (p),
   880    CONSTRAINT foo CHECK (c != 1)
   881  )
   882  
   883  statement error failed to satisfy CHECK constraint
   884  UPSERT INTO t46397_child(c) VALUES (1)
   885  
   886  statement error upsert on table "t46397_child" violates foreign key constraint "fk_p_ref_t46397_parent"
   887  UPSERT INTO t46397_child(c) VALUES (2)
   888  
   889  statement ok
   890  INSERT INTO t46397_parent VALUES (0)
   891  
   892  statement ok
   893  UPSERT INTO t46397_child(c) VALUES (2)
   894  
   895  
   896  # Verify that cascade information shows up in EXPLAIN.
   897  statement ok
   898  SET experimental_optimizer_foreign_key_cascades = true
   899  
   900  statement ok
   901  CREATE TABLE cascadeparent (p INT PRIMARY KEY);
   902  CREATE TABLE cascadechild (
   903    c INT PRIMARY KEY,
   904    p INT NOT NULL REFERENCES cascadeparent(p) ON DELETE CASCADE
   905  )
   906  
   907  query TTTTT
   908  EXPLAIN (VERBOSE) DELETE FROM cascadeparent WHERE p > 1
   909  ----
   910  ·                           distributed  false                   ·    ·
   911  ·                           vectorized   false                   ·    ·
   912  root                        ·            ·                       ()   ·
   913   ├── count                  ·            ·                       ()   ·
   914   │    └── delete            ·            ·                       ()   ·
   915   │         │                from         cascadeparent           ·    ·
   916   │         │                strategy     deleter                 ·    ·
   917   │         └── buffer node  ·            ·                       (p)  ·
   918   │              │           label        buffer 1                ·    ·
   919   │              └── scan    ·            ·                       (p)  ·
   920   │                          table        cascadeparent@primary   ·    ·
   921   │                          spans        /2-                     ·    ·
   922   └── fk-cascade             ·            ·                       ·    ·
   923  ·                           fk           fk_p_ref_cascadeparent  ·    ·
   924  ·                           input        buffer 1                ·    ·