v10-0001-Introduce-CreateStmtPtr.patch
text/x-patch
Filename: v10-0001-Introduce-CreateStmtPtr.patch
Type: text/x-patch
Part: 2
Message:
Re: Statistics Import and Export
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v10-0001
Subject: Introduce CreateStmtPtr.
| File | + | − |
|---|---|---|
| src/bin/pg_dump/pg_backup_archiver.c | 21 | 1 |
| src/bin/pg_dump/pg_backup_archiver.h | 7 | 0 |
| src/bin/pg_dump/pg_backup.h | 2 | 0 |
| src/bin/pg_dump/pg_dump.c | 126 | 104 |
From 8611beb5a7906d0f7e93fb68aa41dd58bc7ab80f Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huinker@gmail.com>
Date: Fri, 14 Mar 2025 01:06:19 -0400
Subject: [PATCH v10 1/4] Introduce CreateStmtPtr.
CreateStmtPtr is a function pointer that can replace the createStmt/defn
parameter. This is useful in situations where the amount of text
generated for a definition is so large that it is undesirable to hold
many such objects in memory at the same time.
Using functions of this type, the text created is then immediately
written out to the appropriate file for the given dump format.
---
src/bin/pg_dump/pg_backup.h | 2 +
src/bin/pg_dump/pg_backup_archiver.c | 22 ++-
src/bin/pg_dump/pg_backup_archiver.h | 7 +
src/bin/pg_dump/pg_dump.c | 230 +++++++++++++++------------
4 files changed, 156 insertions(+), 105 deletions(-)
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 658986de6f8..fdcccd64a70 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -289,6 +289,8 @@ typedef int (*DataDumperPtr) (Archive *AH, const void *userArg);
typedef void (*SetupWorkerPtrType) (Archive *AH);
+typedef char *(*CreateStmtPtr) (Archive *AH, const void *userArg);
+
/*
* Main archiver interface.
*/
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 82d51c89ac6..e512201ed58 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -1264,6 +1264,9 @@ ArchiveEntry(Archive *AHX, CatalogId catalogId, DumpId dumpId,
newToc->dataDumper = opts->dumpFn;
newToc->dataDumperArg = opts->dumpArg;
newToc->hadDumper = opts->dumpFn ? true : false;
+ newToc->createDumper = opts->createFn;
+ newToc->createDumperArg = opts->createArg;
+ newToc->hadCreateDumper = opts->createFn ? true : false;
newToc->formatData = NULL;
newToc->dataLength = 0;
@@ -2620,7 +2623,17 @@ WriteToc(ArchiveHandle *AH)
WriteStr(AH, te->tag);
WriteStr(AH, te->desc);
WriteInt(AH, te->section);
- WriteStr(AH, te->defn);
+
+ if (te->hadCreateDumper)
+ {
+ char *defn = te->createDumper((Archive *) AH, te->createDumperArg);
+
+ WriteStr(AH, defn);
+ pg_free(defn);
+ }
+ else
+ WriteStr(AH, te->defn);
+
WriteStr(AH, te->dropStmt);
WriteStr(AH, te->copyStmt);
WriteStr(AH, te->namespace);
@@ -3856,6 +3869,13 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, const char *pfx)
{
IssueACLPerBlob(AH, te);
}
+ else if (te->hadCreateDumper)
+ {
+ char *ptr = te->createDumper((Archive *) AH, te->createDumperArg);
+
+ ahwrite(ptr, 1, strlen(ptr), AH);
+ pg_free(ptr);
+ }
else if (te->defn && strlen(te->defn) > 0)
{
ahprintf(AH, "%s\n\n", te->defn);
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index a2064f471ed..e68db633995 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -368,6 +368,11 @@ struct _tocEntry
const void *dataDumperArg; /* Arg for above routine */
void *formatData; /* TOC Entry data specific to file format */
+ CreateStmtPtr createDumper; /* Routine for create statement creation */
+ const void *createDumperArg; /* arg for the above routine */
+ bool hadCreateDumper; /* Archiver was passed a create statement
+ * routine */
+
/* working state while dumping/restoring */
pgoff_t dataLength; /* item's data size; 0 if none or unknown */
int reqs; /* do we need schema and/or data of object
@@ -407,6 +412,8 @@ typedef struct _archiveOpts
int nDeps;
DataDumperPtr dumpFn;
const void *dumpArg;
+ CreateStmtPtr createFn;
+ const void *createArg;
} ArchiveOpts;
#define ARCHIVE_OPTS(...) &(ArchiveOpts){__VA_ARGS__}
/* Called to add a TOC entry */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e41e645f649..224dc8c9330 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -10520,51 +10520,44 @@ statisticsDumpSection(const RelStatsInfo *rsinfo)
}
/*
- * dumpRelationStats --
+ * printDumpRelationStats --
*
- * Dump command to import stats into the relation on the new database.
+ * Generate the SQL statements needed to restore a relation's statistics.
*/
-static void
-dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
+static char *
+printRelationStats(Archive *fout, const void *userArg)
{
+ const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
const DumpableObject *dobj = &rsinfo->dobj;
+
+ PQExpBufferData query;
+ PQExpBufferData out;
+
PGresult *res;
- PQExpBuffer query;
- PQExpBuffer out;
- DumpId *deps = NULL;
- int ndeps = 0;
- int i_attname;
- int i_inherited;
- int i_null_frac;
- int i_avg_width;
- int i_n_distinct;
- int i_most_common_vals;
- int i_most_common_freqs;
- int i_histogram_bounds;
- int i_correlation;
- int i_most_common_elems;
- int i_most_common_elem_freqs;
- int i_elem_count_histogram;
- int i_range_length_histogram;
- int i_range_empty_frac;
- int i_range_bounds_histogram;
- /* nothing to do if we are not dumping statistics */
- if (!fout->dopt->dumpStatistics)
- return;
+ static bool first_query = true;
+ static int i_attname;
+ static int i_inherited;
+ static int i_null_frac;
+ static int i_avg_width;
+ static int i_n_distinct;
+ static int i_most_common_vals;
+ static int i_most_common_freqs;
+ static int i_histogram_bounds;
+ static int i_correlation;
+ static int i_most_common_elems;
+ static int i_most_common_elem_freqs;
+ static int i_elem_count_histogram;
+ static int i_range_length_histogram;
+ static int i_range_empty_frac;
+ static int i_range_bounds_histogram;
- /* dependent on the relation definition, if doing schema */
- if (fout->dopt->dumpSchema)
+ initPQExpBuffer(&query);
+
+ if (first_query)
{
- deps = dobj->dependencies;
- ndeps = dobj->nDeps;
- }
-
- query = createPQExpBuffer();
- if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
- {
- appendPQExpBufferStr(query,
- "PREPARE getAttributeStats(pg_catalog.name, pg_catalog.name) AS\n"
+ appendPQExpBufferStr(&query,
+ "PREPARE getAttributeStats(pg_catalog.text, pg_catalog.text) AS\n"
"SELECT s.attname, s.inherited, "
"s.null_frac, s.avg_width, s.n_distinct, "
"s.most_common_vals, s.most_common_freqs, "
@@ -10573,82 +10566,85 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
"s.elem_count_histogram, ");
if (fout->remoteVersion >= 170000)
- appendPQExpBufferStr(query,
+ appendPQExpBufferStr(&query,
"s.range_length_histogram, "
"s.range_empty_frac, "
"s.range_bounds_histogram ");
else
- appendPQExpBufferStr(query,
+ appendPQExpBufferStr(&query,
"NULL AS range_length_histogram,"
"NULL AS range_empty_frac,"
"NULL AS range_bounds_histogram ");
- appendPQExpBufferStr(query,
+ appendPQExpBufferStr(&query,
"FROM pg_catalog.pg_stats s "
"WHERE s.schemaname = $1 "
"AND s.tablename = $2 "
"ORDER BY s.attname, s.inherited");
- ExecuteSqlStatement(fout, query->data);
+ ExecuteSqlStatement(fout, query.data);
- fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
- resetPQExpBuffer(query);
+ resetPQExpBuffer(&query);
}
- out = createPQExpBuffer();
+ initPQExpBuffer(&out);
/* restore relation stats */
- appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
- appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+ appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
+ appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
fout->remoteVersion);
- appendPQExpBufferStr(out, "\t'schemaname', ");
- appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
- appendPQExpBufferStr(out, ",\n");
- appendPQExpBufferStr(out, "\t'relname', ");
- appendStringLiteralAH(out, rsinfo->dobj.name, fout);
- appendPQExpBufferStr(out, ",\n");
- appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
- appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
- appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer\n);\n",
+ appendPQExpBufferStr(&out, "\t'schemaname', ");
+ appendStringLiteralAH(&out, rsinfo->dobj.namespace->dobj.name, fout);
+ appendPQExpBufferStr(&out, ",\n");
+ appendPQExpBufferStr(&out, "\t'relname', ");
+ appendStringLiteralAH(&out, rsinfo->dobj.name, fout);
+ appendPQExpBufferStr(&out, ",\n");
+ appendPQExpBuffer(&out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
+ appendPQExpBuffer(&out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
+ appendPQExpBuffer(&out, "\t'relallvisible', '%d'::integer\n);\n",
rsinfo->relallvisible);
/* fetch attribute stats */
- appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
- appendStringLiteralAH(query, dobj->namespace->dobj.name, fout);
- appendPQExpBufferStr(query, ", ");
- appendStringLiteralAH(query, dobj->name, fout);
- appendPQExpBufferStr(query, ");");
+ appendPQExpBufferStr(&query, "EXECUTE getAttributeStats(");
+ appendStringLiteralAH(&query, dobj->namespace->dobj.name, fout);
+ appendPQExpBufferStr(&query, ", ");
+ appendStringLiteralAH(&query, dobj->name, fout);
+ appendPQExpBufferStr(&query, ")");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ res = ExecuteSqlQuery(fout, query.data, PGRES_TUPLES_OK);
- i_attname = PQfnumber(res, "attname");
- i_inherited = PQfnumber(res, "inherited");
- i_null_frac = PQfnumber(res, "null_frac");
- i_avg_width = PQfnumber(res, "avg_width");
- i_n_distinct = PQfnumber(res, "n_distinct");
- i_most_common_vals = PQfnumber(res, "most_common_vals");
- i_most_common_freqs = PQfnumber(res, "most_common_freqs");
- i_histogram_bounds = PQfnumber(res, "histogram_bounds");
- i_correlation = PQfnumber(res, "correlation");
- i_most_common_elems = PQfnumber(res, "most_common_elems");
- i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
- i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
- i_range_length_histogram = PQfnumber(res, "range_length_histogram");
- i_range_empty_frac = PQfnumber(res, "range_empty_frac");
- i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+ if (first_query)
+ {
+ i_attname = PQfnumber(res, "attname");
+ i_inherited = PQfnumber(res, "inherited");
+ i_null_frac = PQfnumber(res, "null_frac");
+ i_avg_width = PQfnumber(res, "avg_width");
+ i_n_distinct = PQfnumber(res, "n_distinct");
+ i_most_common_vals = PQfnumber(res, "most_common_vals");
+ i_most_common_freqs = PQfnumber(res, "most_common_freqs");
+ i_histogram_bounds = PQfnumber(res, "histogram_bounds");
+ i_correlation = PQfnumber(res, "correlation");
+ i_most_common_elems = PQfnumber(res, "most_common_elems");
+ i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
+ i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
+ i_range_length_histogram = PQfnumber(res, "range_length_histogram");
+ i_range_empty_frac = PQfnumber(res, "range_empty_frac");
+ i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
+ first_query = false;
+ }
/* restore attribute stats */
for (int rownum = 0; rownum < PQntuples(res); rownum++)
{
const char *attname;
- appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
- appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+ appendPQExpBufferStr(&out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
+ appendPQExpBuffer(&out, "\t'version', '%u'::integer,\n",
fout->remoteVersion);
- appendPQExpBufferStr(out, "\t'schemaname', ");
- appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
- appendPQExpBufferStr(out, ",\n\t'relname', ");
- appendStringLiteralAH(out, rsinfo->dobj.name, fout);
+ appendPQExpBufferStr(&out, "\t'schemaname', ");
+ appendStringLiteralAH(&out, rsinfo->dobj.namespace->dobj.name, fout);
+ appendPQExpBufferStr(&out, ",\n\t'relname', ");
+ appendStringLiteralAH(&out, rsinfo->dobj.name, fout);
if (PQgetisnull(res, rownum, i_attname))
pg_fatal("attname cannot be NULL");
@@ -10661,8 +10657,8 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
*/
if (rsinfo->nindAttNames == 0)
{
- appendPQExpBuffer(out, ",\n\t'attname', ");
- appendStringLiteralAH(out, attname, fout);
+ appendPQExpBuffer(&out, ",\n\t'attname', ");
+ appendStringLiteralAH(&out, attname, fout);
}
else
{
@@ -10672,7 +10668,7 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
{
if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
{
- appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
+ appendPQExpBuffer(&out, ",\n\t'attnum', '%d'::smallint",
i + 1);
found = true;
break;
@@ -10684,67 +10680,93 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
}
if (!PQgetisnull(res, rownum, i_inherited))
- appendNamedArgument(out, fout, "inherited", "boolean",
+ appendNamedArgument(&out, fout, "inherited", "boolean",
PQgetvalue(res, rownum, i_inherited));
if (!PQgetisnull(res, rownum, i_null_frac))
- appendNamedArgument(out, fout, "null_frac", "real",
+ appendNamedArgument(&out, fout, "null_frac", "real",
PQgetvalue(res, rownum, i_null_frac));
if (!PQgetisnull(res, rownum, i_avg_width))
- appendNamedArgument(out, fout, "avg_width", "integer",
+ appendNamedArgument(&out, fout, "avg_width", "integer",
PQgetvalue(res, rownum, i_avg_width));
if (!PQgetisnull(res, rownum, i_n_distinct))
- appendNamedArgument(out, fout, "n_distinct", "real",
+ appendNamedArgument(&out, fout, "n_distinct", "real",
PQgetvalue(res, rownum, i_n_distinct));
if (!PQgetisnull(res, rownum, i_most_common_vals))
- appendNamedArgument(out, fout, "most_common_vals", "text",
+ appendNamedArgument(&out, fout, "most_common_vals", "text",
PQgetvalue(res, rownum, i_most_common_vals));
if (!PQgetisnull(res, rownum, i_most_common_freqs))
- appendNamedArgument(out, fout, "most_common_freqs", "real[]",
+ appendNamedArgument(&out, fout, "most_common_freqs", "real[]",
PQgetvalue(res, rownum, i_most_common_freqs));
if (!PQgetisnull(res, rownum, i_histogram_bounds))
- appendNamedArgument(out, fout, "histogram_bounds", "text",
+ appendNamedArgument(&out, fout, "histogram_bounds", "text",
PQgetvalue(res, rownum, i_histogram_bounds));
if (!PQgetisnull(res, rownum, i_correlation))
- appendNamedArgument(out, fout, "correlation", "real",
+ appendNamedArgument(&out, fout, "correlation", "real",
PQgetvalue(res, rownum, i_correlation));
if (!PQgetisnull(res, rownum, i_most_common_elems))
- appendNamedArgument(out, fout, "most_common_elems", "text",
+ appendNamedArgument(&out, fout, "most_common_elems", "text",
PQgetvalue(res, rownum, i_most_common_elems));
if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
- appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
+ appendNamedArgument(&out, fout, "most_common_elem_freqs", "real[]",
PQgetvalue(res, rownum, i_most_common_elem_freqs));
if (!PQgetisnull(res, rownum, i_elem_count_histogram))
- appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
+ appendNamedArgument(&out, fout, "elem_count_histogram", "real[]",
PQgetvalue(res, rownum, i_elem_count_histogram));
if (fout->remoteVersion >= 170000)
{
if (!PQgetisnull(res, rownum, i_range_length_histogram))
- appendNamedArgument(out, fout, "range_length_histogram", "text",
+ appendNamedArgument(&out, fout, "range_length_histogram", "text",
PQgetvalue(res, rownum, i_range_length_histogram));
if (!PQgetisnull(res, rownum, i_range_empty_frac))
- appendNamedArgument(out, fout, "range_empty_frac", "real",
+ appendNamedArgument(&out, fout, "range_empty_frac", "real",
PQgetvalue(res, rownum, i_range_empty_frac));
if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
- appendNamedArgument(out, fout, "range_bounds_histogram", "text",
+ appendNamedArgument(&out, fout, "range_bounds_histogram", "text",
PQgetvalue(res, rownum, i_range_bounds_histogram));
}
- appendPQExpBufferStr(out, "\n);\n");
+ appendPQExpBufferStr(&out, "\n);\n");
}
PQclear(res);
+ termPQExpBuffer(&query);
+ return out.data;
+}
+
+/*
+ * dumpRelationStats --
+ *
+ * Dump command to import stats into the relation on the new database.
+ */
+static void
+dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
+{
+ const DumpableObject *dobj = &rsinfo->dobj;
+
+ DumpId *deps = NULL;
+ int ndeps = 0;
+
+ /* nothing to do if we are not dumping statistics */
+ if (!fout->dopt->dumpStatistics)
+ return;
+
+ /* dependent on the relation definition, if doing schema */
+ if (fout->dopt->dumpSchema)
+ {
+ deps = dobj->dependencies;
+ ndeps = dobj->nDeps;
+ }
+
ArchiveEntry(fout, nilCatalogId, createDumpId(),
ARCHIVE_OPTS(.tag = dobj->name,
.namespace = dobj->namespace->dobj.name,
.description = "STATISTICS DATA",
.section = rsinfo->postponed_def ?
SECTION_POST_DATA : statisticsDumpSection(rsinfo),
- .createStmt = out->data,
+ .createFn = printRelationStats,
+ .createArg = rsinfo,
.deps = deps,
.nDeps = ndeps));
-
- destroyPQExpBuffer(out);
- destroyPQExpBuffer(query);
}
/*
base-commit: bde2fb797aaebcbe06bf60f330ba5a068f17dda7
--
2.49.0