v2-0002-Avoid-TRUNCATE-when-restoring-load-via-partition-.patch
text/x-diff
Filename: v2-0002-Avoid-TRUNCATE-when-restoring-load-via-partition-.patch
Type: text/x-diff
Part: 1
Message:
Re: pg_dump versus hash partitioning
Patch
Format: format-patch
Series: patch v2-0002
Subject: Avoid TRUNCATE when restoring load-via-partition-root data.
| File | + | − |
|---|---|---|
| src/bin/pg_dump/pg_backup_archiver.c | 54 | 7 |
From 685b4a1c0aaa59b2b6047051d082838b88528587 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 13 Mar 2023 19:05:05 -0400
Subject: [PATCH v2 2/6] Avoid TRUNCATE when restoring load-via-partition-root
data.
This fixes the deadlock problem, allowing 004_pg_dump_parallel.pl to
succeed in the test where there's a manual --load-via-partition-root
switch. It relies on the dump having used COPY commands, though.
---
src/bin/pg_dump/pg_backup_archiver.c | 61 ++++++++++++++++++++++++----
1 file changed, 54 insertions(+), 7 deletions(-)
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 61ebb8fe85..cb3a2c21f5 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -86,6 +86,7 @@ static RestorePass _tocEntryRestorePass(TocEntry *te);
static bool _tocEntryIsACL(TocEntry *te);
static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te);
static void _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te);
+static bool is_load_via_partition_root(TocEntry *te);
static void buildTocEntryArrays(ArchiveHandle *AH);
static void _moveBefore(TocEntry *pos, TocEntry *te);
static int _discoverArchiveFormat(ArchiveHandle *AH);
@@ -887,6 +888,8 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, bool is_parallel)
}
else
{
+ bool use_truncate;
+
_disableTriggersIfNecessary(AH, te);
/* Select owner and schema as necessary */
@@ -898,13 +901,23 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, bool is_parallel)
/*
* In parallel restore, if we created the table earlier in
- * the run then we wrap the COPY in a transaction and
- * precede it with a TRUNCATE. If archiving is not on
- * this prevents WAL-logging the COPY. This obtains a
- * speedup similar to that from using single_txn mode in
- * non-parallel restores.
+ * the run and we are not restoring a
+ * load-via-partition-root data item then we wrap the COPY
+ * in a transaction and precede it with a TRUNCATE. If
+ * archiving is not on this prevents WAL-logging the COPY.
+ * This obtains a speedup similar to that from using
+ * single_txn mode in non-parallel restores.
+ *
+ * We mustn't do this for load-via-partition-root cases
+ * because some data might get moved across partition
+ * boundaries, risking deadlock and/or loss of previously
+ * loaded data. (We assume that all partitions of a
+ * partitioned table will be treated the same way.)
*/
- if (is_parallel && te->created)
+ use_truncate = is_parallel && te->created &&
+ !is_load_via_partition_root(te);
+
+ if (use_truncate)
{
/*
* Parallel restore is always talking directly to a
@@ -942,7 +955,7 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, bool is_parallel)
AH->outputKind = OUTPUT_SQLCMDS;
/* close out the transaction started above */
- if (is_parallel && te->created)
+ if (use_truncate)
CommitTransaction(&AH->public);
_enableTriggersIfNecessary(AH, te);
@@ -1036,6 +1049,40 @@ _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te)
fmtQualifiedId(te->namespace, te->tag));
}
+/*
+ * Detect whether a TABLE DATA TOC item is performing "load via partition
+ * root", that is the target table is an ancestor partition rather than the
+ * table the TOC item is nominally for.
+ *
+ * In newer archive files this can be detected by checking for a special
+ * comment placed in te->defn. In older files we have to fall back to seeing
+ * if the COPY statement targets the named table or some other one. This
+ * will not work for data dumped as INSERT commands, so we could give a false
+ * negative in that case; fortunately, that's a rarely-used option.
+ */
+static bool
+is_load_via_partition_root(TocEntry *te)
+{
+ if (te->copyStmt && *te->copyStmt)
+ {
+ PQExpBuffer copyStmt = createPQExpBuffer();
+ bool result;
+
+ /*
+ * Build the initial part of the COPY as it would appear if the
+ * nominal target table is the actual target. If we see anything
+ * else, it must be a load-via-partition-root case.
+ */
+ appendPQExpBuffer(copyStmt, "COPY %s ",
+ fmtQualifiedId(te->namespace, te->tag));
+ result = strncmp(te->copyStmt, copyStmt->data, copyStmt->len) != 0;
+ destroyPQExpBuffer(copyStmt);
+ return result;
+ }
+ /* Assume it's not load-via-partition-root */
+ return false;
+}
+
/*
* This is a routine that is part of the dumper interface, hence the 'Archive*' parameter.
*/
--
2.31.1