From a848e460b44cc272b057a700fe8b2e4023d78768 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 27 Oct 2026 20:37:44 +0700 Subject: [PATCH 1/6] docs(features): foreign key checks during Copy To and the On error setting --- features/copy-objects.mdx | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/features/copy-objects.mdx b/features/copy-objects.mdx index 72f73240..c9f2e3c9 100644 --- a/features/copy-objects.mdx +++ b/features/copy-objects.mdx @@ -28,6 +28,9 @@ written until you have read the script. The funnel beside a table narrows the rows it contributes. See [Copying part of a table](#copying-part-of-a-table). + + **On error** chooses what a failure does: stop and roll back, stop and keep what ran, or skip + that object and continue. **Continue** reads both databases and shows the DDL that will run, the rows each table expects, @@ -232,8 +235,20 @@ A copied foreign key is repointed at the copy. A key that referenced the source' references the target's afterwards, so the duplicate stands on its own; one that referenced a third schema is left as it was. -Two tables that reference each other cannot be ordered. The second `CREATE TABLE` fails, its error -appears in the result, and the rest of the copy is unaffected. +Order alone cannot place two tables that reference each other, a table that references itself +with a child row ahead of its parent, or a source row whose parent is already gone. Rows are +therefore written with the target's foreign key checks off where the engine can turn them off for +one session, as MySQL and MariaDB can, and the session gets back the setting it had. PostgreSQL keeps +its checks on, because its switch also stops the target's own triggers and needs a superuser, so +there such a row fails and the result names its table. **Skip and continue** keeps them on too: a +table that failed can leave its children's parents missing, and only the checks say so. + +Emptying a table for a Replace keeps the checks on, so a table that references it, selected or not, +applies its own `ON DELETE` rule. + +Two tables that reference each other are created with checks off on MySQL and MariaDB. On +PostgreSQL the second `CREATE TABLE` fails, its error appears in the result, and the rest of the +copy is unaffected. ## Stopping a copy @@ -242,9 +257,11 @@ supports one, so the table being copied rolls back and the tables already finish blocked inside a network call finishes that batch first. A Replace that removes the target's rows is the exception: every table is emptied before any of them -is filled, so that a cascading foreign key cannot reach a table you did not select, and the whole -data phase is then one transaction. Stopping it, or a failure under **Stop and roll back**, puts -every row back. **Skip and continue** promises no rollback, so there the clearing stands. +is filled, so that a cascading foreign key cannot reach a table you did not select. Under **Stop and +roll back** the whole data phase is then one transaction, and stopping it or a failure puts every row +back. The other two settings promise no rollback, so there the clearing stands, and a table whose +emptying failed gets no rows. The structure is not one transaction under them either, so what was +created before a failure stays. A stopped copy reports only what was committed. The table it was in the middle of counts as neither copied nor failed. From d418949bcb5de2b0b18d2c0a65313917bc80e4d1 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 27 Oct 2026 23:13:33 +0700 Subject: [PATCH 2/6] docs(features): checks off only for a whole copy under Stop and roll back --- features/copy-objects.mdx | 47 ++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/features/copy-objects.mdx b/features/copy-objects.mdx index c9f2e3c9..d70bec8a 100644 --- a/features/copy-objects.mdx +++ b/features/copy-objects.mdx @@ -236,35 +236,42 @@ references the target's afterwards, so the duplicate stands on its own; one that schema is left as it was. Order alone cannot place two tables that reference each other, a table that references itself -with a child row ahead of its parent, or a source row whose parent is already gone. Rows are -therefore written with the target's foreign key checks off where the engine can turn them off for -one session, as MySQL and MariaDB can, and the session gets back the setting it had. PostgreSQL keeps -its checks on, because its switch also stops the target's own triggers and needs a superuser, so -there such a row fails and the result names its table. **Skip and continue** keeps them on too: a -table that failed can leave its children's parents missing, and only the checks say so. +with a child row ahead of its parent, or a source row whose parent is already gone. Under **Stop and +roll back**, when every key the copied tables hold points at a table the copy fills with all of its +rows, those rows are written with the target's foreign key checks off where the engine can turn them +off for one session, as MySQL and MariaDB can, and the session gets back the setting it had. + +The checks stay on otherwise, and such a row fails with the result naming its table: + +- A key to a table outside the copy, or to one narrowed by a filter or a row limit. Turning the + checks back on checks nothing already written, so the row would point at nothing. +- PostgreSQL, because its switch also stops the target's own triggers and needs a superuser. +- **Stop and keep what ran** and **Skip and continue**, since what they keep after a failure can + leave a child's parent missing. Emptying a table for a Replace keeps the checks on, so a table that references it, selected or not, applies its own `ON DELETE` rule. -Two tables that reference each other are created with checks off on MySQL and MariaDB. On -PostgreSQL the second `CREATE TABLE` fails, its error appears in the result, and the rest of the -copy is unaffected. +Two tables that reference each other are created with checks off on MySQL and MariaDB when the copy +holds both. On PostgreSQL the second `CREATE TABLE` fails, its error appears in the result, and the +rest of the copy is unaffected. ## Stopping a copy -**Stop** takes effect at the next batch boundary. Each table is its own transaction where the engine -supports one, so the table being copied rolls back and the tables already finished stay. A driver -blocked inside a network call finishes that batch first. +**Stop** takes effect at the next batch boundary. A driver blocked inside a network call finishes that +batch first. + +Under **Stop and roll back** the whole data phase is one transaction where the engine supports one, +so stopping it or a failure puts every row back. A Replace that removes the target's rows empties +every table inside it before any of them is filled, so that a cascading foreign key cannot reach a +table you did not select. -A Replace that removes the target's rows is the exception: every table is emptied before any of them -is filled, so that a cascading foreign key cannot reach a table you did not select. Under **Stop and -roll back** the whole data phase is then one transaction, and stopping it or a failure puts every row -back. The other two settings promise no rollback, so there the clearing stands, and a table whose -emptying failed gets no rows. The structure is not one transaction under them either, so what was -created before a failure stays. +The other two settings commit each batch as it is written, so stopping or a failure keeps what was +written. There the clearing stands, a table whose emptying failed gets no rows, and the structure is +not one transaction either, so what was created before a failure stays. -A stopped copy reports only what was committed. The table it was in the middle of counts as neither -copied nor failed. +The result counts only the rows the target kept, including the batches a failed table wrote before +its error. When the target connection already holds a transaction, the copy runs inside it and opens none of its own. Nothing it wrote is committed until you commit that transaction, and the result says so. From 3bde8b8cc0f305d6f8e96f607d537e6d2ae6780d Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 27 Oct 2026 23:24:06 +0700 Subject: [PATCH 3/6] docs(features): tables the target keeps and targets with no transactions keep the checks --- features/copy-objects.mdx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/features/copy-objects.mdx b/features/copy-objects.mdx index d70bec8a..0ba0fe99 100644 --- a/features/copy-objects.mdx +++ b/features/copy-objects.mdx @@ -237,14 +237,18 @@ schema is left as it was. Order alone cannot place two tables that reference each other, a table that references itself with a child row ahead of its parent, or a source row whose parent is already gone. Under **Stop and -roll back**, when every key the copied tables hold points at a table the copy fills with all of its -rows, those rows are written with the target's foreign key checks off where the engine can turn them -off for one session, as MySQL and MariaDB can, and the session gets back the setting it had. +roll back**, when the copy creates every table it fills and every key those tables hold points at a +table the copy fills with all of its rows, the rows are written with the target's foreign key checks +off where the engine can turn them off for one session, as MySQL and MariaDB can, and the session +gets back the setting it had. The checks stay on otherwise, and such a row fails with the result naming its table: - A key to a table outside the copy, or to one narrowed by a filter or a row limit. Turning the checks back on checks nothing already written, so the row would point at nothing. +- **Data only**, or **Add rows to it** on a table the target already has. The target keeps its own + keys there, and the source cannot say where they point. +- A target with no transactions, where a failure could not take the rows back. - PostgreSQL, because its switch also stops the target's own triggers and needs a superuser. - **Stop and keep what ran** and **Skip and continue**, since what they keep after a failure can leave a child's parent missing. From ef5ba34e1e6ae8ac5b69fb958ba74bb3c0909266 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 27 Oct 2026 23:42:38 +0700 Subject: [PATCH 4/6] docs(features): a relaxed copy checks every key before it commits --- features/copy-objects.mdx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/features/copy-objects.mdx b/features/copy-objects.mdx index 0ba0fe99..594a2a1a 100644 --- a/features/copy-objects.mdx +++ b/features/copy-objects.mdx @@ -235,17 +235,20 @@ A copied foreign key is repointed at the copy. A key that referenced the source' references the target's afterwards, so the duplicate stands on its own; one that referenced a third schema is left as it was. -Order alone cannot place two tables that reference each other, a table that references itself -with a child row ahead of its parent, or a source row whose parent is already gone. Under **Stop and -roll back**, when the copy creates every table it fills and every key those tables hold points at a -table the copy fills with all of its rows, the rows are written with the target's foreign key checks -off where the engine can turn them off for one session, as MySQL and MariaDB can, and the session -gets back the setting it had. +Order alone cannot place two tables that reference each other, or a table that references itself +with a child row ahead of its parent. Under **Stop and roll back**, when the copy creates every table +it fills and every key those tables hold points at a table the copy fills with all of its rows, the +rows are written with the target's foreign key checks off where the engine can turn them off for one +session, as MySQL and MariaDB can, and the session gets back the setting it had. + +Turning the checks back on checks nothing already written, so before it commits the copy counts, for +every key, the rows that point at nothing. A source row whose parent is already gone, or one read +while the source was changing, fails the copy there, the result names the table and the key, and +every row is put back. The checks stay on otherwise, and such a row fails with the result naming its table: -- A key to a table outside the copy, or to one narrowed by a filter or a row limit. Turning the - checks back on checks nothing already written, so the row would point at nothing. +- A key to a table outside the copy, or to one narrowed by a filter or a row limit. - **Data only**, or **Add rows to it** on a table the target already has. The target keeps its own keys there, and the source cannot say where they point. - A target with no transactions, where a failure could not take the rows back. From f5fbf07dfe46eaed56f046c6c054049ae4da9873 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 27 Oct 2026 23:53:37 +0700 Subject: [PATCH 5/6] docs(features): a MyISAM table keeps its rows through a rolled back copy --- features/copy-objects.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/copy-objects.mdx b/features/copy-objects.mdx index 594a2a1a..8ba153c0 100644 --- a/features/copy-objects.mdx +++ b/features/copy-objects.mdx @@ -269,7 +269,8 @@ rest of the copy is unaffected. batch first. Under **Stop and roll back** the whole data phase is one transaction where the engine supports one, -so stopping it or a failure puts every row back. A Replace that removes the target's rows empties +so stopping it or a failure puts every row back. A MySQL table on a storage engine without +transactions, such as MyISAM, keeps its rows through that, and the result counts them. A Replace that removes the target's rows empties every table inside it before any of them is filled, so that a cascading foreign key cannot reach a table you did not select. From 57cd970bf52615124c408abde8f707a86ef534fc Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Wed, 28 Oct 2026 00:04:59 +0700 Subject: [PATCH 6/6] docs(features): an account that cannot check the keys keeps them on --- features/copy-objects.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/features/copy-objects.mdx b/features/copy-objects.mdx index 8ba153c0..6aa58641 100644 --- a/features/copy-objects.mdx +++ b/features/copy-objects.mdx @@ -252,6 +252,8 @@ The checks stay on otherwise, and such a row fails with the result naming its ta - **Data only**, or **Add rows to it** on a table the target already has. The target keeps its own keys there, and the source cannot say where they point. - A target with no transactions, where a failure could not take the rows back. +- A target account that cannot read the tables it writes, so the copy could not count what each key + refuses. - PostgreSQL, because its switch also stops the target's own triggers and needs a superuser. - **Stop and keep what ran** and **Skip and continue**, since what they keep after a failure can leave a child's parent missing.