From aa78d861024b409bf63121dedf9dd955ad9dc734 Mon Sep 17 00:00:00 2001 From: jian he Date: Mon, 20 Jan 2025 16:12:40 +0800 Subject: [PATCH v38 1/1] make statistics dumped at SECTION_POST_DATA based on v38-0001-Enable-dumping-of-table-index-stats-in-pg_dump.patch refactoring make statistics dump in the SECTION_POST_DATA. --- doc/src/sgml/ref/pg_dump.sgml | 58 +++- doc/src/sgml/ref/pg_dumpall.sgml | 38 +++ doc/src/sgml/ref/pg_restore.sgml | 51 +++- doc/src/sgml/ref/pgupgrade.sgml | 18 ++ src/bin/pg_dump/common.c | 3 + src/bin/pg_dump/pg_backup.h | 2 + src/bin/pg_dump/pg_backup_archiver.c | 22 +- src/bin/pg_dump/pg_dump.c | 383 ++++++++++++++++++++++++++- src/bin/pg_dump/pg_dump.h | 8 + src/bin/pg_dump/pg_dump_sort.c | 11 +- src/bin/pg_dump/pg_dumpall.c | 5 + src/bin/pg_dump/pg_restore.c | 38 ++- src/bin/pg_dump/t/001_basic.pl | 18 ++ src/bin/pg_dump/t/002_pg_dump.pl | 62 ++++- src/bin/pg_upgrade/dump.c | 6 +- src/bin/pg_upgrade/option.c | 12 + src/bin/pg_upgrade/pg_upgrade.h | 1 + src/tools/pgindent/typedefs.list | 1 + 18 files changed, 705 insertions(+), 32 deletions(-) diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index d66e901f51b..3f663915422 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -123,7 +123,7 @@ PostgreSQL documentation - Dump only the data, not the schema (data definitions). + Dump only the data, not the schema (data definitions) or statistics. Table data, large objects, and sequence values are dumped. @@ -141,13 +141,11 @@ PostgreSQL documentation Include large objects in the dump. This is the default behavior - except when , , or - is specified. The - switch is therefore only useful to add large objects to dumps - where a specific schema or table has been requested. Note that - large objects are considered data and therefore will be included when - is used, but not - when is. + except when , , + , or , or + is specified. The + switch is therefore only useful to add large objects to dumps where a + specific schema or table has been requested. @@ -516,10 +514,11 @@ PostgreSQL documentation - Dump only the object definitions (schema), not data. + Dump only the object definitions (schema), not data or statistics. - This option is the inverse of . + This option is mutually exclusive to + and . It is similar to, but for historical reasons not identical to, specifying . @@ -652,6 +651,18 @@ PostgreSQL documentation + + + + + + Dump only the statistics, not the schema (data definitions) or data. + Statistics for tables, materialized views, and indexes are dumped. + + + + + [:detail] @@ -1080,6 +1091,15 @@ PostgreSQL documentation + + + + + Do not dump data. + + + + @@ -1098,6 +1118,24 @@ PostgreSQL documentation + + + + + Do not dump schema (data definitions). + + + + + + + + + Do not dump statistics. + + + + diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml index 014f2792589..d423153a93a 100644 --- a/doc/src/sgml/ref/pg_dumpall.sgml +++ b/doc/src/sgml/ref/pg_dumpall.sgml @@ -265,6 +265,17 @@ exclude database PATTERN + + + + + + Dump only the statistics, not the schema (data definitions) or data. + Statistics for tables, materialized views, and indexes are dumped. + + + + @@ -422,6 +433,15 @@ exclude database PATTERN + + + + + Do not dump data. + + + + @@ -447,6 +467,15 @@ exclude database PATTERN + + + + + Do not dump schema (data definitions). + + + + @@ -456,6 +485,15 @@ exclude database PATTERN + + + + + Do not dump statistics. + + + + diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml index b8b27e1719e..22c3c118add 100644 --- a/doc/src/sgml/ref/pg_restore.sgml +++ b/doc/src/sgml/ref/pg_restore.sgml @@ -94,7 +94,7 @@ PostgreSQL documentation - Restore only the data, not the schema (data definitions). + Restore only the data, not the schema (data definitions) or statistics. Table data, large objects, and sequence values are restored, if present in the archive. @@ -483,10 +483,11 @@ PostgreSQL documentation to the extent that schema entries are present in the archive. - This option is the inverse of . + This option is mutually exclusive of + and . It is similar to, but for historical reasons not identical to, specifying - . + . (Do not confuse this with the option, which @@ -599,6 +600,20 @@ PostgreSQL documentation + + + + + + Restore only the statistics, not schema (data definitions) or data. + + + (Do not confuse this with the option, which + uses the word schema in a different meaning.) + + + + @@ -681,6 +696,16 @@ PostgreSQL documentation + + + + + Do not output commands to restore data, even if the archive + contains them. + + + + @@ -713,6 +738,16 @@ PostgreSQL documentation + + + + + Do not output commands to restore schema (data definitions), even if + the archive contains them. + + + + @@ -723,6 +758,16 @@ PostgreSQL documentation + + + + + Do not output commands to restore statistics, even if the archive + contains them. + + + + diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml index 4777381dac2..64a1ebd613b 100644 --- a/doc/src/sgml/ref/pgupgrade.sgml +++ b/doc/src/sgml/ref/pgupgrade.sgml @@ -145,6 +145,24 @@ PostgreSQL documentation + + + + + Restore statistics from the old cluster into the new cluster. + + + + + + + + + Do not restore statistics from the old cluster into the new cluster. + + + + options options diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 56b6c368acf..bc856492cc8 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -216,6 +216,9 @@ getSchemaData(Archive *fout, int *numTablesPtr) pg_log_info("flagging indexes in partitioned tables"); flagInhIndexes(fout, tblinfo, numTables); + pg_log_info("reading table statistics"); + getRelationStatistics(fout, tblinfo, numTables); + pg_log_info("reading extended statistics"); getExtendedStatistics(fout); diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index f0f19bb0b29..350cf659c41 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -160,6 +160,7 @@ typedef struct _restoreOptions /* flags derived from the user-settable flags */ bool dumpSchema; bool dumpData; + bool dumpStatistics; } RestoreOptions; typedef struct _dumpOptions @@ -208,6 +209,7 @@ typedef struct _dumpOptions /* flags derived from the user-settable flags */ bool dumpSchema; bool dumpData; + bool dumpStatistics; } DumpOptions; /* diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index 707a3fc844c..b190d9731f6 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -46,7 +46,6 @@ #define TEXT_DUMP_HEADER "--\n-- PostgreSQL database dump\n--\n\n" #define TEXT_DUMPALL_HEADER "--\n-- PostgreSQL database cluster dump\n--\n\n" - static ArchiveHandle *_allocAH(const char *FileSpec, const ArchiveFormat fmt, const pg_compress_specification compression_spec, bool dosync, ArchiveMode mode, @@ -149,6 +148,7 @@ InitDumpOptions(DumpOptions *opts) opts->dumpSections = DUMP_UNSECTIONED; opts->dumpSchema = true; opts->dumpData = true; + opts->dumpStatistics = true; } /* @@ -169,9 +169,10 @@ dumpOptionsFromRestoreOptions(RestoreOptions *ropt) dopt->outputClean = ropt->dropSchema; dopt->dumpData = ropt->dumpData; dopt->dumpSchema = ropt->dumpSchema; + dopt->dumpSections = ropt->dumpSections; + dopt->dumpStatistics = ropt->dumpStatistics; dopt->if_exists = ropt->if_exists; dopt->column_inserts = ropt->column_inserts; - dopt->dumpSections = ropt->dumpSections; dopt->aclsSkip = ropt->aclsSkip; dopt->outputSuperuser = ropt->superuser; dopt->outputCreateDB = ropt->createDB; @@ -1084,6 +1085,7 @@ NewRestoreOptions(void) opts->compression_spec.level = 0; opts->dumpSchema = true; opts->dumpData = true; + opts->dumpStatistics = true; return opts; } @@ -1093,6 +1095,7 @@ _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te) { RestoreOptions *ropt = AH->public.ropt; + ahprintf(AH, "-- ALTER TABLE DISABLE TRIGGER ALL %d %d; \n\n", ropt->dumpSchema, ropt->disable_triggers); /* This hack is only needed in a data-only restore */ if (ropt->dumpSchema || !ropt->disable_triggers) return; @@ -2962,6 +2965,18 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH) if (ropt->no_subscriptions && strcmp(te->desc, "SUBSCRIPTION") == 0) return 0; + /* + * statistics is in the REQ_DATA section. we can choose to ignore it, but + * not when we request dump statistics + */ + if (strcmp(te->desc, "STATISTICS DATA") == 0) + { + if (!ropt->dumpStatistics) + return 0; + else + return REQ_DATA; + } + /* Ignore it if section is not to be dumped/restored */ switch (curSection) { @@ -3196,7 +3211,8 @@ _tocEntryRestorePass(TocEntry *te) strcmp(te->desc, "DEFAULT ACL") == 0) return RESTORE_PASS_ACL; if (strcmp(te->desc, "EVENT TRIGGER") == 0 || - strcmp(te->desc, "MATERIALIZED VIEW DATA") == 0) + strcmp(te->desc, "MATERIALIZED VIEW DATA") == 0 || + strcmp(te->desc, "STATISTICS DATA") == 0 ) return RESTORE_PASS_POST_ACL; /* diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 8f73a5df956..19a08f8d08d 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -430,7 +430,11 @@ main(int argc, char **argv) DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC; bool data_only = false; bool schema_only = false; + bool statistics_only = false; + static int no_data = 0; + static int no_schema = 0; + static int no_statistics = 0; static DumpOptions dopt; static struct option long_options[] = { @@ -466,6 +470,7 @@ main(int argc, char **argv) {"encoding", required_argument, NULL, 'E'}, {"help", no_argument, NULL, '?'}, {"version", no_argument, NULL, 'V'}, + {"statistics-only", no_argument, NULL, 'X'}, /* * the following options don't have an equivalent short option letter @@ -492,8 +497,11 @@ main(int argc, char **argv) {"strict-names", no_argument, &strict_names, 1}, {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1}, {"no-comments", no_argument, &dopt.no_comments, 1}, + {"no-data", no_argument, &no_data, 1}, {"no-publications", no_argument, &dopt.no_publications, 1}, + {"no-schema", no_argument, &no_schema, 1}, {"no-security-labels", no_argument, &dopt.no_security_labels, 1}, + {"no-statistics", no_argument, &no_statistics, 1}, {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1}, {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1}, {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1}, @@ -539,7 +547,7 @@ main(int argc, char **argv) InitDumpOptions(&dopt); - while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:", + while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:PRsS:t:T:U:vwWxXZ:", long_options, &optindex)) != -1) { switch (c) @@ -613,6 +621,10 @@ main(int argc, char **argv) dopt.cparams.pgport = pg_strdup(optarg); break; + case 'X': /* Dump statistics only */ + statistics_only = true; + break; + case 'R': /* no-op, still accepted for backwards compatibility */ break; @@ -784,6 +796,17 @@ main(int argc, char **argv) if (data_only && schema_only) pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together"); + if (schema_only && statistics_only) + pg_fatal("options -s/--schema-only and -X/--statistics-only cannot be used together"); + if (data_only && statistics_only) + pg_fatal("options -a/--data-only and -X/--statistics-only cannot be used together"); + + if (data_only && no_data) + pg_fatal("options -a/--data-only and --no-data cannot be used together"); + if (schema_only && no_schema) + pg_fatal("options -s/--schema-only and --no-schema cannot be used together"); + if (statistics_only && no_statistics) + pg_fatal("options -X/--statistics-only and --no-statistics cannot be used together"); if (schema_only && foreign_servers_include_patterns.head != NULL) pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together"); @@ -798,8 +821,17 @@ main(int argc, char **argv) pg_fatal("option --if-exists requires option -c/--clean"); /* set derivative flags */ - dopt.dumpSchema = (!data_only); - dopt.dumpData = (!schema_only); + dopt.dumpData = data_only || (!schema_only && !statistics_only); + dopt.dumpSchema = schema_only || (!data_only && !statistics_only); + dopt.dumpStatistics = statistics_only || (!data_only && !schema_only); + + if (no_data) + dopt.dumpData = false; + if (no_schema) + dopt.dumpSchema = false; + if (no_statistics || + (dopt.dumpSections && (!(dopt.dumpSections & DUMP_POST_DATA)))) + dopt.dumpStatistics = false; /* * --inserts are already implied above if --column-inserts or @@ -1099,6 +1131,7 @@ main(int argc, char **argv) ropt->dropSchema = dopt.outputClean; ropt->dumpData = dopt.dumpData; ropt->dumpSchema = dopt.dumpSchema; + ropt->dumpStatistics = dopt.dumpStatistics; ropt->if_exists = dopt.if_exists; ropt->column_inserts = dopt.column_inserts; ropt->dumpSections = dopt.dumpSections; @@ -1177,7 +1210,7 @@ help(const char *progname) printf(_(" -?, --help show this help, then exit\n")); printf(_("\nOptions controlling the output content:\n")); - printf(_(" -a, --data-only dump only the data, not the schema\n")); + printf(_(" -a, --data-only dump only the data, not the schema or statistics\n")); printf(_(" -b, --large-objects include large objects in dump\n")); printf(_(" --blobs (same as --large-objects, deprecated)\n")); printf(_(" -B, --no-large-objects exclude large objects in dump\n")); @@ -1190,11 +1223,12 @@ help(const char *progname) printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n")); printf(_(" -O, --no-owner skip restoration of object ownership in\n" " plain-text format\n")); - printf(_(" -s, --schema-only dump only the schema, no data\n")); + printf(_(" -s, --schema-only dump only the schema, no data or statistics\n")); printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n")); printf(_(" -t, --table=PATTERN dump only the specified table(s)\n")); printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n")); printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n")); + printf(_(" -X, --statistics-only dump only the statistics, not schema or data\n")); printf(_(" --binary-upgrade for use by upgrade utilities only\n")); printf(_(" --column-inserts dump data as INSERT commands with column names\n")); printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n")); @@ -1219,8 +1253,11 @@ help(const char *progname) printf(_(" --inserts dump data as INSERT commands, rather than COPY\n")); printf(_(" --load-via-partition-root load partitions via the root table\n")); printf(_(" --no-comments do not dump comment commands\n")); + printf(_(" --no-data do not dump data\n")); printf(_(" --no-publications do not dump publications\n")); + printf(_(" --no-schema do not dump schema\n")); printf(_(" --no-security-labels do not dump security label assignments\n")); + printf(_(" --no-statistics do not dump statistics\n")); printf(_(" --no-subscriptions do not dump subscriptions\n")); printf(_(" --no-table-access-method do not dump table access methods\n")); printf(_(" --no-tablespaces do not dump tablespace assignments\n")); @@ -6777,6 +6814,85 @@ getFuncs(Archive *fout) destroyPQExpBuffer(query); } +/* + * getRelationStatistics + * get information about all statistics on dumpable relations. + */ +void +getRelationStatistics(Archive *fout, TableInfo tblinfo[], int numTables) +{ + int i = 0; + int j = 0; + int tbl_cnt = 0; + int index_cnt = 0; + RelStatsInfo *rel_stats_info = NULL; + TableInfo *tbinfo = NULL; + IndxInfo *indxinfo = NULL; + Oid *tabldids = NULL; + + if (!fout->dopt->dumpStatistics) + return; + + tabldids = pg_malloc0(numTables * sizeof(Oid)); + for (i = 0; i < numTables; i++) + { + tbinfo = &tblinfo[i]; + /* + * we only dump statistics related to regular relations, partitioned + * tables, materialized views + */ + if (!(tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_MATVIEW || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE)) + continue; + + if (!tbinfo->interesting) + continue; + + tabldids[tbl_cnt] = tbinfo->dobj.catId.oid; + index_cnt = index_cnt + tbinfo->numIndexes; + tbl_cnt++; + } + + if(tbl_cnt == 0) + { + free(tabldids); + return; + } + + /* + * since we dump indexes statistics, we need iterate over TableInfo->indexes + */ + rel_stats_info = pg_malloc((tbl_cnt + index_cnt) * sizeof(RelStatsInfo)); + for (i = 0; i < tbl_cnt; i++) + { + tbinfo = findTableByOid(tabldids[i]); + rel_stats_info[j].dobj.objType = DO_REL_STATS; + rel_stats_info[j].dobj.catId.tableoid = 0; + rel_stats_info[j].dobj.catId.oid = tbinfo->dobj.catId.oid; + AssignDumpId(&rel_stats_info[j].dobj); + rel_stats_info[j].dobj.namespace = tbinfo->dobj.namespace; + rel_stats_info[j].dobj.name = pg_strdup(tbinfo->dobj.name); + rel_stats_info[j].stat_relation = tbinfo; + + for (int k = 0; k < tbinfo->numIndexes; k++) + { + j++; + indxinfo = (&(tbinfo->indexes)[k]); + rel_stats_info[j].dobj.objType = DO_REL_STATS; + rel_stats_info[j].dobj.catId.tableoid = 0; + rel_stats_info[j].dobj.catId.oid = indxinfo->dobj.catId.oid; + AssignDumpId(&rel_stats_info[j].dobj); + rel_stats_info[j].dobj.namespace = indxinfo->dobj.namespace; + rel_stats_info[j].dobj.name = pg_strdup(indxinfo->dobj.name); + rel_stats_info[j].stat_relation = tbinfo; + } + j++; + } + Assert(j == tbl_cnt + index_cnt); + free(tabldids); +} + /* * getTables * read all the tables (no indexes) in the system catalogs, @@ -10296,6 +10412,259 @@ dumpComment(Archive *fout, const char *type, catalogId, subid, dumpId, NULL); } +/* + * Tabular description of the parameters to pg_restore_relation_stats() + * param_name, param_type + */ +static const char *rel_stats_arginfo[][2] = { + {"relation", "regclass"}, + {"version", "integer"}, + {"relpages", "integer"}, + {"reltuples", "real"}, + {"relallvisible", "integer"}, +}; + +/* + * Tabular description of the parameters to pg_restore_attribute_stats() + * param_name, param_type + */ +static const char *att_stats_arginfo[][2] = { + {"relation", "regclass"}, + {"attname", "name"}, + {"inherited", "boolean"}, + {"version", "integer"}, + {"null_frac", "float4"}, + {"avg_width", "integer"}, + {"n_distinct", "float4"}, + {"most_common_vals", "text"}, + {"most_common_freqs", "float4[]"}, + {"histogram_bounds", "text"}, + {"correlation", "float4"}, + {"most_common_elems", "text"}, + {"most_common_elem_freqs", "float4[]"}, + {"elem_count_histogram", "float4[]"}, + {"range_length_histogram", "text"}, + {"range_empty_frac", "float4"}, + {"range_bounds_histogram", "text"}, +}; + +/* + * getRelStatsExportQuery -- + * + * Generate a query that will fetch all relation (e.g. pg_class) + * stats for a given relation. + */ +static void +getRelStatsExportQuery(PQExpBuffer query, Archive *fout, + const char *schemaname, const char *relname) +{ + resetPQExpBuffer(query); + appendPQExpBufferStr(query, + "SELECT c.oid::regclass AS relation, " + "current_setting('server_version_num') AS version, " + "c.relpages, c.reltuples, c.relallvisible " + "FROM pg_class c " + "JOIN pg_namespace n " + "ON n.oid = c.relnamespace " + "WHERE n.nspname = "); + appendStringLiteralAH(query, schemaname, fout); + appendPQExpBufferStr(query, " AND c.relname = "); + appendStringLiteralAH(query, relname, fout); +} + +/* + * getAttStatsExportQuery -- + * + * Generate a query that will fetch all attribute (e.g. pg_statistic) + * stats for a given relation. + */ +static void +getAttStatsExportQuery(PQExpBuffer query, Archive *fout, + const char *schemaname, const char *relname) +{ + resetPQExpBuffer(query); + appendPQExpBufferStr(query, + "SELECT c.oid::regclass AS relation, " + "s.attname," + "s.inherited," + "current_setting('server_version_num') AS version, " + "s.null_frac," + "s.avg_width," + "s.n_distinct," + "s.most_common_vals," + "s.most_common_freqs," + "s.histogram_bounds," + "s.correlation," + "s.most_common_elems," + "s.most_common_elem_freqs," + "s.elem_count_histogram,"); + + if (fout->remoteVersion >= 170000) + appendPQExpBufferStr(query, + "s.range_length_histogram," + "s.range_empty_frac," + "s.range_bounds_histogram "); + else + appendPQExpBufferStr(query, + "NULL AS range_length_histogram," + "NULL AS range_empty_frac," + "NULL AS range_bounds_histogram "); + + appendPQExpBufferStr(query, + "FROM pg_stats s " + "JOIN pg_namespace n " + "ON n.nspname = s.schemaname " + "JOIN pg_class c " + "ON c.relname = s.tablename " + "AND c.relnamespace = n.oid " + "WHERE s.schemaname = "); + appendStringLiteralAH(query, schemaname, fout); + appendPQExpBufferStr(query, " AND s.tablename = "); + appendStringLiteralAH(query, relname, fout); + appendPQExpBufferStr(query, " ORDER BY s.attname, s.inherited"); +} + + +/* + * appendNamedArgument -- + * + * Convenience routine for constructing parameters of the form: + * 'paraname', 'value'::type + */ +static void +appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname, + const char *argval, const char *argtype) +{ + appendPQExpBufferStr(out, "\t"); + + appendStringLiteralAH(out, argname, fout); + appendPQExpBufferStr(out, ", "); + + appendStringLiteralAH(out, argval, fout); + appendPQExpBuffer(out, "::%s", argtype); +} + +/* + * appendRelStatsImport -- + * + * Append a formatted pg_restore_relation_stats statement. + */ +static void +appendRelStatsImport(PQExpBuffer out, Archive *fout, PGresult *res) +{ + const char *sep = ""; + + if (PQntuples(res) == 0) + return; + + appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n"); + + for (int argno = 0; argno < lengthof(rel_stats_arginfo); argno++) + { + const char *argname = rel_stats_arginfo[argno][0]; + const char *argtype = rel_stats_arginfo[argno][1]; + int fieldno = PQfnumber(res, argname); + + if (fieldno < 0) + pg_fatal("relation stats export query missing field '%s'", + argname); + + if (PQgetisnull(res, 0, fieldno)) + continue; + + appendPQExpBufferStr(out, sep); + appendNamedArgument(out, fout, argname, PQgetvalue(res, 0, fieldno), argtype); + + sep = ",\n"; + } + appendPQExpBufferStr(out, "\n);\n"); +} + +/* + * appendAttStatsImport -- + * + * Append a series of formatted pg_restore_attribute_stats statements. + */ +static void +appendAttStatsImport(PQExpBuffer out, Archive *fout, PGresult *res) +{ + for (int rownum = 0; rownum < PQntuples(res); rownum++) + { + const char *sep = ""; + + appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n"); + for (int argno = 0; argno < lengthof(att_stats_arginfo); argno++) + { + const char *argname = att_stats_arginfo[argno][0]; + const char *argtype = att_stats_arginfo[argno][1]; + int fieldno = PQfnumber(res, argname); + + if (fieldno < 0) + pg_fatal("attribute stats export query missing field '%s'", + argname); + + if (PQgetisnull(res, rownum, fieldno)) + continue; + + appendPQExpBufferStr(out, sep); + appendNamedArgument(out, fout, argname, PQgetvalue(res, rownum, fieldno), argtype); + sep = ",\n"; + } + appendPQExpBufferStr(out, "\n);\n"); + } +} + +/* + * dumpRelationStats -- + * + * Dump command to import stats into the relation on the new database. + */ +static void +dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo) +{ + PGresult *res; + PQExpBuffer query; + PQExpBuffer out; + PQExpBuffer tag; + DumpableObject *dobj = (DumpableObject *) &rsinfo->dobj; + + /* nothing to do if we are not dumping statistics */ + if (!fout->dopt->dumpStatistics) + return; + + tag = createPQExpBuffer(); + appendPQExpBuffer(tag, "%s %s", "STATISTICS DATA", fmtId(dobj->name)); + + query = createPQExpBuffer(); + out = createPQExpBuffer(); + + getRelStatsExportQuery(query, fout, dobj->namespace->dobj.name, + dobj->name); + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + appendRelStatsImport(out, fout, res); + PQclear(res); + + getAttStatsExportQuery(query, fout, dobj->namespace->dobj.name, + dobj->name); + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + appendAttStatsImport(out, fout, res); + PQclear(res); + + ArchiveEntry(fout, nilCatalogId, createDumpId(), + ARCHIVE_OPTS(.tag = tag->data, + .namespace = dobj->namespace->dobj.name, + .description = "STATISTICS DATA", + .section = SECTION_POST_DATA, + .owner = rsinfo->stat_relation->rolname, + .createStmt = out->data, + .deps = dobj->dependencies, + .nDeps = dobj->nDeps)); + + destroyPQExpBuffer(query); + destroyPQExpBuffer(out); + destroyPQExpBuffer(tag); +} + /* * dumpTableComment -- * @@ -10744,6 +11113,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) case DO_SUBSCRIPTION_REL: dumpSubscriptionTable(fout, (const SubRelInfo *) dobj); break; + case DO_REL_STATS: + dumpRelationStats(fout, (const RelStatsInfo *) dobj); + break; case DO_PRE_DATA_BOUNDARY: case DO_POST_DATA_BOUNDARY: /* never dumped, nothing to do */ @@ -18949,6 +19321,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs, case DO_PUBLICATION_TABLE_IN_SCHEMA: case DO_SUBSCRIPTION: case DO_SUBSCRIPTION_REL: + case DO_REL_STATS: /* Post-data objects: must come after the post-data boundary */ addObjectDependency(dobj, postDataBound->dumpId); break; diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index f62b564ed1b..09d4970ecd2 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -84,6 +84,7 @@ typedef enum DO_PUBLICATION_TABLE_IN_SCHEMA, DO_SUBSCRIPTION, DO_SUBSCRIPTION_REL, /* see note for SubRelInfo */ + DO_REL_STATS, } DumpableObjectType; /* @@ -429,6 +430,12 @@ typedef struct _indexAttachInfo IndxInfo *partitionIdx; /* link to index on partition */ } IndexAttachInfo; +typedef struct _relStatsInfo +{ + DumpableObject dobj; + TableInfo *stat_relation; /* link to relation the stat is for */ +} RelStatsInfo; + typedef struct _statsExtInfo { DumpableObject dobj; @@ -763,6 +770,7 @@ extern InhInfo *getInherits(Archive *fout, int *numInherits); extern void getPartitioningInfo(Archive *fout); extern void getIndexes(Archive *fout, TableInfo tblinfo[], int numTables); extern void getExtendedStatistics(Archive *fout); +extern void getRelationStatistics(Archive *fout, TableInfo tblinfo[], int numTables); extern void getConstraints(Archive *fout, TableInfo tblinfo[], int numTables); extern void getRules(Archive *fout); extern void getTriggers(Archive *fout, TableInfo tblinfo[], int numTables); diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c index dc9a28924bd..76f02db5e04 100644 --- a/src/bin/pg_dump/pg_dump_sort.c +++ b/src/bin/pg_dump/pg_dump_sort.c @@ -97,7 +97,8 @@ enum dbObjectTypePriorities PRIO_SUBSCRIPTION_REL, PRIO_DEFAULT_ACL, /* done in ACL pass */ PRIO_EVENT_TRIGGER, /* must be next to last! */ - PRIO_REFRESH_MATVIEW /* must be last! */ + PRIO_REFRESH_MATVIEW, /* must be last! */ + PRIO_RELSTAT, /* must really be last! */ }; /* This table is indexed by enum DumpableObjectType */ @@ -144,6 +145,7 @@ static const int dbObjectTypePriority[] = [DO_POST_DATA_BOUNDARY] = PRIO_POST_DATA_BOUNDARY, [DO_EVENT_TRIGGER] = PRIO_EVENT_TRIGGER, [DO_REFRESH_MATVIEW] = PRIO_REFRESH_MATVIEW, + [DO_REL_STATS] = PRIO_RELSTAT, [DO_POLICY] = PRIO_POLICY, [DO_PUBLICATION] = PRIO_PUBLICATION, [DO_PUBLICATION_REL] = PRIO_PUBLICATION_REL, @@ -152,7 +154,7 @@ static const int dbObjectTypePriority[] = [DO_SUBSCRIPTION_REL] = PRIO_SUBSCRIPTION_REL, }; -StaticAssertDecl(lengthof(dbObjectTypePriority) == (DO_SUBSCRIPTION_REL + 1), +StaticAssertDecl(lengthof(dbObjectTypePriority) == (DO_REL_STATS + 1), "array length mismatch"); static DumpId preDataBoundId; @@ -1500,6 +1502,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize) "POST-DATA BOUNDARY (ID %d)", obj->dumpId); return; + case DO_REL_STATS: + snprintf(buf, bufsize, + "RELATION STATISTICS FOR %s (ID %d OID %u)", + obj->name, obj->dumpId, obj->catId.oid); + return; } /* shouldn't get here */ snprintf(buf, bufsize, diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 396f79781c5..7effb704905 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -103,6 +103,7 @@ static int use_setsessauth = 0; static int no_comments = 0; static int no_publications = 0; static int no_security_labels = 0; +static int no_statistics = 0; static int no_subscriptions = 0; static int no_toast_compression = 0; static int no_unlogged_table_data = 0; @@ -172,6 +173,7 @@ main(int argc, char *argv[]) {"no-role-passwords", no_argument, &no_role_passwords, 1}, {"no-security-labels", no_argument, &no_security_labels, 1}, {"no-subscriptions", no_argument, &no_subscriptions, 1}, + {"no-statistics", no_argument, &no_statistics, 1}, {"no-sync", no_argument, NULL, 4}, {"no-toast-compression", no_argument, &no_toast_compression, 1}, {"no-unlogged-table-data", no_argument, &no_unlogged_table_data, 1}, @@ -451,6 +453,8 @@ main(int argc, char *argv[]) appendPQExpBufferStr(pgdumpopts, " --no-publications"); if (no_security_labels) appendPQExpBufferStr(pgdumpopts, " --no-security-labels"); + if (no_statistics) + appendPQExpBufferStr(pgdumpopts, " --no-statistics"); if (no_subscriptions) appendPQExpBufferStr(pgdumpopts, " --no-subscriptions"); if (no_toast_compression) @@ -666,6 +670,7 @@ help(void) printf(_(" --no-publications do not dump publications\n")); printf(_(" --no-role-passwords do not dump passwords for roles\n")); printf(_(" --no-security-labels do not dump security label assignments\n")); + printf(_(" --no-statistics do not dump statistics\n")); printf(_(" --no-subscriptions do not dump subscriptions\n")); printf(_(" --no-sync do not wait for changes to be written safely to disk\n")); printf(_(" --no-table-access-method do not dump table access methods\n")); diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c index 88ae39d938a..2cc7d3eeb08 100644 --- a/src/bin/pg_dump/pg_restore.c +++ b/src/bin/pg_dump/pg_restore.c @@ -63,6 +63,9 @@ main(int argc, char **argv) int numWorkers = 1; Archive *AH; char *inputFileSpec; + bool data_only = false; + bool schema_only = false; + bool statistics_only = false; static int disable_triggers = 0; static int enable_row_security = 0; static int if_exists = 0; @@ -71,12 +74,13 @@ main(int argc, char **argv) static int outputNoTablespaces = 0; static int use_setsessauth = 0; static int no_comments = 0; + static int no_data = 0; static int no_publications = 0; + static int no_schema = 0; static int no_security_labels = 0; + static int no_statistics = 0; static int no_subscriptions = 0; static int strict_names = 0; - bool data_only = false; - bool schema_only = false; struct option cmdopts[] = { {"clean", 0, NULL, 'c'}, @@ -108,6 +112,7 @@ main(int argc, char **argv) {"username", 1, NULL, 'U'}, {"verbose", 0, NULL, 'v'}, {"single-transaction", 0, NULL, '1'}, + {"statistics-only", no_argument, NULL, 'X'}, /* * the following options don't have an equivalent short option letter @@ -124,9 +129,12 @@ main(int argc, char **argv) {"transaction-size", required_argument, NULL, 5}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, {"no-comments", no_argument, &no_comments, 1}, + {"no-data", no_argument, &no_data, 1}, {"no-publications", no_argument, &no_publications, 1}, + {"no-schema", no_argument, &no_schema, 1}, {"no-security-labels", no_argument, &no_security_labels, 1}, {"no-subscriptions", no_argument, &no_subscriptions, 1}, + {"no-statistics", no_argument, &no_statistics, 1}, {"filter", required_argument, NULL, 4}, {NULL, 0, NULL, 0} @@ -271,6 +279,10 @@ main(int argc, char **argv) opts->aclsSkip = 1; break; + case 'X': /* Restore statistics only */ + statistics_only = true; + break; + case '1': /* Restore data in a single transaction */ opts->single_txn = true; opts->exit_on_error = true; @@ -343,6 +355,10 @@ main(int argc, char **argv) if (data_only && schema_only) pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together"); + if (data_only && statistics_only) + pg_fatal("options -a/--data-only and -X/--statistics-only cannot be used together"); + if (schema_only && statistics_only) + pg_fatal("options -s/--schema-only and -X/--statistics-only cannot be used together"); if (data_only && opts->dropSchema) pg_fatal("options -c/--clean and -a/--data-only cannot be used together"); @@ -362,8 +378,17 @@ main(int argc, char **argv) pg_fatal("cannot specify both --single-transaction and multiple jobs"); /* set derivative flags */ - opts->dumpSchema = (!data_only); - opts->dumpData = (!schema_only); + opts->dumpData = data_only || (!schema_only && !statistics_only); + opts->dumpSchema = schema_only || (!data_only && !statistics_only); + opts->dumpStatistics = statistics_only || (!data_only && !schema_only); + + if (no_data) + opts->dumpData = false; + if (no_schema) + opts->dumpSchema = false; + if (no_statistics || + (opts->dumpSections && (!(opts->dumpSections & DUMP_POST_DATA)))) + opts->dumpStatistics = false; opts->disable_triggers = disable_triggers; opts->enable_row_security = enable_row_security; @@ -484,6 +509,7 @@ usage(const char *progname) printf(_(" -t, --table=NAME restore named relation (table, view, etc.)\n")); printf(_(" -T, --trigger=NAME restore named trigger\n")); printf(_(" -x, --no-privileges skip restoration of access privileges (grant/revoke)\n")); + printf(_(" -X, --statistics-only restore only the statistics, not schema or data\n")); printf(_(" -1, --single-transaction restore as a single transaction\n")); printf(_(" --disable-triggers disable triggers during data-only restore\n")); printf(_(" --enable-row-security enable row security\n")); @@ -491,10 +517,14 @@ usage(const char *progname) " in FILENAME\n")); printf(_(" --if-exists use IF EXISTS when dropping objects\n")); printf(_(" --no-comments do not restore comment commands\n")); + printf(_(" --no-data do not restore data\n")); printf(_(" --no-data-for-failed-tables do not restore data of tables that could not be\n" " created\n")); printf(_(" --no-publications do not restore publications\n")); + printf(_(" --no-schema do not restore schema\n")); printf(_(" --no-security-labels do not restore security labels\n")); + printf(_(" --no-statistics do not restore statistics\n")); + /* This hack is only needed in a data-only restore */ printf(_(" --no-subscriptions do not restore subscriptions\n")); printf(_(" --no-table-access-method do not restore table access methods\n")); printf(_(" --no-tablespaces do not restore tablespace assignments\n")); diff --git a/src/bin/pg_dump/t/001_basic.pl b/src/bin/pg_dump/t/001_basic.pl index 214240f1ae5..f29da06ed28 100644 --- a/src/bin/pg_dump/t/001_basic.pl +++ b/src/bin/pg_dump/t/001_basic.pl @@ -50,12 +50,30 @@ command_fails_like( 'pg_dump: options -s/--schema-only and -a/--data-only cannot be used together' ); +command_fails_like( + [ 'pg_dump', '-s', '-X' ], + qr/\Qpg_dump: error: options -s\/--schema-only and -X\/--statistics-only cannot be used together\E/, + 'pg_dump: error: options -s/--schema-only and -X/--statistics-only cannot be used together' +); + +command_fails_like( + [ 'pg_dump', '-a', '-X' ], + qr/\Qpg_dump: error: options -a\/--data-only and -X\/--statistics-only cannot be used together\E/, + 'pg_dump: error: options -a/--data-only and -X/--statistics-only cannot be used together' +); + command_fails_like( [ 'pg_dump', '-s', '--include-foreign-data=xxx' ], qr/\Qpg_dump: error: options -s\/--schema-only and --include-foreign-data cannot be used together\E/, 'pg_dump: options -s/--schema-only and --include-foreign-data cannot be used together' ); +command_fails_like( + [ 'pg_dump', '--statistics-only', '--no-statistics' ], + qr/\Qpg_dump: error: options -X\/--statistics-only and --no-statistics cannot be used together\E/, + 'pg_dump: options -X\/--statistics-only and --no-statistics cannot be used together' +); + command_fails_like( [ 'pg_dump', '-j2', '--include-foreign-data=xxx' ], qr/\Qpg_dump: error: option --include-foreign-data is not supported with parallel backup\E/, diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index bf65d44b942..da8b6e85f3e 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -66,7 +66,7 @@ my %pgdump_runs = ( '--format=custom', "--file=$tempdir/binary_upgrade.dump", '-w', - '--schema-only', + '--no-data', '--binary-upgrade', '-d', 'postgres', # alternative way to specify database ], @@ -645,7 +645,19 @@ my %pgdump_runs = ( '--schema=dump_test', '-b', '-B', '--no-sync', 'postgres', ], - },); + }, + no_statistics => { + dump_cmd => [ + 'pg_dump', "--file=$tempdir/no_statistics.sql", + '--no-sync', '--no-statistics', 'postgres', + ], + }, + no_schema => { + dump_cmd => [ + 'pg_dump', "--file=$tempdir/no_schema.sql", + '--no-sync', '--no-schema', 'postgres', + ], + }); ############################################################### # Definition of the tests to run. @@ -711,6 +723,7 @@ my %full_runs = ( no_large_objects => 1, no_owner => 1, no_privs => 1, + no_statistics => 1, no_table_access_method => 1, pg_dumpall_dbprivs => 1, pg_dumpall_exclude => 1, @@ -912,6 +925,7 @@ my %tests = ( column_inserts => 1, data_only => 1, inserts => 1, + no_schema => 1, section_data => 1, test_schema_plus_large_objects => 1, }, @@ -1325,6 +1339,7 @@ my %tests = ( column_inserts => 1, data_only => 1, inserts => 1, + no_schema => 1, section_data => 1, test_schema_plus_large_objects => 1, }, @@ -1346,6 +1361,7 @@ my %tests = ( column_inserts => 1, data_only => 1, inserts => 1, + no_schema => 1, section_data => 1, test_schema_plus_large_objects => 1, }, @@ -1367,6 +1383,7 @@ my %tests = ( column_inserts => 1, data_only => 1, inserts => 1, + no_schema => 1, section_data => 1, test_schema_plus_large_objects => 1, }, @@ -1533,6 +1550,7 @@ my %tests = ( column_inserts => 1, data_only => 1, inserts => 1, + no_schema => 1, section_data => 1, test_schema_plus_large_objects => 1, }, @@ -1686,6 +1704,7 @@ my %tests = ( %full_runs, %dump_test_schema_runs, data_only => 1, + no_schema => 1, only_dump_test_table => 1, section_data => 1, }, @@ -1713,6 +1732,7 @@ my %tests = ( data_only => 1, exclude_test_table => 1, exclude_test_table_data => 1, + no_schema => 1, section_data => 1, }, unlike => { @@ -1733,7 +1753,10 @@ my %tests = ( \QCOPY dump_test.fk_reference_test_table (col1) FROM stdin;\E \n(?:\d\n){5}\\\.\n /xms, - like => { data_only => 1, }, + like => { + data_only => 1, + no_schema => 1, + }, }, 'COPY test_second_table' => { @@ -1749,6 +1772,7 @@ my %tests = ( %full_runs, %dump_test_schema_runs, data_only => 1, + no_schema => 1, section_data => 1, }, unlike => { @@ -1771,6 +1795,7 @@ my %tests = ( %full_runs, %dump_test_schema_runs, data_only => 1, + no_schema => 1, section_data => 1, }, unlike => { @@ -1794,6 +1819,7 @@ my %tests = ( %full_runs, %dump_test_schema_runs, data_only => 1, + no_schema => 1, section_data => 1, }, unlike => { @@ -1816,6 +1842,7 @@ my %tests = ( %full_runs, %dump_test_schema_runs, data_only => 1, + no_schema => 1, section_data => 1, }, unlike => { @@ -1838,6 +1865,7 @@ my %tests = ( %full_runs, %dump_test_schema_runs, data_only => 1, + no_schema => 1, section_data => 1, }, unlike => { @@ -3234,6 +3262,7 @@ my %tests = ( like => { %full_runs, data_only => 1, + no_schema => 1, section_data => 1, only_dump_test_schema => 1, test_schema_plus_large_objects => 1, @@ -3404,6 +3433,7 @@ my %tests = ( %full_runs, %dump_test_schema_runs, data_only => 1, + no_schema => 1, only_dump_measurement => 1, section_data => 1, only_dump_test_schema => 1, @@ -4286,6 +4316,7 @@ my %tests = ( column_inserts => 1, data_only => 1, inserts => 1, + no_schema => 1, section_data => 1, test_schema_plus_large_objects => 1, binary_upgrade => 1, @@ -4586,6 +4617,31 @@ my %tests = ( }, }, + # + # Table statistics should go in section=data. + # Materialized view statistics should go in section=post-data. + # + 'statistics_import' => { + create_sql => ' + CREATE TABLE dump_test.has_stats + AS SELECT g.g AS x, g.g / 2 AS y FROM generate_series(1,100) AS g(g); + CREATE TABLE dump_test.has_stats_mv AS SELECT * FROM dump_test.has_stats; + ANALYZE dump_test.has_stats, dump_test.has_stats_mv;', + regexp => qr/pg_catalog.pg_restore_attribute_stats/, + like => { + %full_runs, + %dump_test_schema_runs, + section_post_data => 1, + no_schema => 1, + }, + unlike => { + exclude_dump_test_schema => 1, + no_statistics => 1, + only_dump_measurement => 1, + schema_only => 1, + }, + }, + # CREATE TABLE with partitioned table and various AMs. One # partition uses the same default as the parent, and a second # uses its own AM. diff --git a/src/bin/pg_upgrade/dump.c b/src/bin/pg_upgrade/dump.c index 8ce0fa3020e..a29cd2cca98 100644 --- a/src/bin/pg_upgrade/dump.c +++ b/src/bin/pg_upgrade/dump.c @@ -21,10 +21,11 @@ generate_old_dump(void) /* run new pg_dumpall binary for globals */ exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/pg_dumpall\" %s --globals-only --quote-all-identifiers " + "\"%s/pg_dumpall\" %s %s --globals-only --quote-all-identifiers " "--binary-upgrade %s --no-sync -f \"%s/%s\"", new_cluster.bindir, cluster_conn_opts(&old_cluster), log_opts.verbose ? "--verbose" : "", + user_opts.do_statistics ? "" : "--no-statistics", log_opts.dumpdir, GLOBALS_DUMP_FILE); check_ok(); @@ -52,10 +53,11 @@ generate_old_dump(void) snprintf(log_file_name, sizeof(log_file_name), DB_DUMP_LOG_FILE_MASK, old_db->db_oid); parallel_exec_prog(log_file_name, NULL, - "\"%s/pg_dump\" %s --schema-only --quote-all-identifiers " + "\"%s/pg_dump\" %s --no-data %s --quote-all-identifiers " "--binary-upgrade --format=custom %s --no-sync --file=\"%s/%s\" %s", new_cluster.bindir, cluster_conn_opts(&old_cluster), log_opts.verbose ? "--verbose" : "", + user_opts.do_statistics ? "" : "--no-statistics", log_opts.dumpdir, sql_file_name, escaped_connstr.data); diff --git a/src/bin/pg_upgrade/option.c b/src/bin/pg_upgrade/option.c index 108eb7a1ba4..3b6c7ec994e 100644 --- a/src/bin/pg_upgrade/option.c +++ b/src/bin/pg_upgrade/option.c @@ -60,6 +60,8 @@ parseCommandLine(int argc, char *argv[]) {"copy", no_argument, NULL, 2}, {"copy-file-range", no_argument, NULL, 3}, {"sync-method", required_argument, NULL, 4}, + {"with-statistics", no_argument, NULL, 5}, + {"no-statistics", no_argument, NULL, 6}, {NULL, 0, NULL, 0} }; @@ -70,6 +72,7 @@ parseCommandLine(int argc, char *argv[]) user_opts.do_sync = true; user_opts.transfer_mode = TRANSFER_MODE_COPY; + user_opts.do_statistics = true; os_info.progname = get_progname(argv[0]); @@ -212,6 +215,13 @@ parseCommandLine(int argc, char *argv[]) user_opts.sync_method = pg_strdup(optarg); break; + case 5: + user_opts.do_statistics = true; + break; + case 6: + user_opts.do_statistics = false; + break; + default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), os_info.progname); @@ -306,7 +316,9 @@ usage(void) printf(_(" --clone clone instead of copying files to new cluster\n")); printf(_(" --copy copy files to new cluster (default)\n")); printf(_(" --copy-file-range copy files to new cluster with copy_file_range\n")); + printf(_(" --no-statistics do not import statistics from old cluster\n")); printf(_(" --sync-method=METHOD set method for syncing files to disk\n")); + printf(_(" --with-statistics import statistics from old cluster (default)\n")); printf(_(" -?, --help show this help, then exit\n")); printf(_("\n" "Before running pg_upgrade you must:\n" diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h index 0cdd675e4f1..3fe111fbde5 100644 --- a/src/bin/pg_upgrade/pg_upgrade.h +++ b/src/bin/pg_upgrade/pg_upgrade.h @@ -327,6 +327,7 @@ typedef struct int jobs; /* number of processes/threads to use */ char *socketdir; /* directory to use for Unix sockets */ char *sync_method; + bool do_statistics; /* carry over statistics from old cluster */ } UserOpts; typedef struct diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 668bddbfcd7..8a0968aaa7b 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2398,6 +2398,7 @@ RelMapFile RelMapping RelOptInfo RelOptKind +RelStatsInfo RelToCheck RelToCluster RelabelType -- 2.34.1