From efdcdf9b0f9ee85db6de6146b55884db4128945f Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Fri, 1 Dec 2023 12:56:26 +0900 Subject: [PATCH v4 6/8] Sequence access methods - dump/restore support --- src/bin/pg_dump/pg_backup.h | 2 + src/bin/pg_dump/pg_backup_archiver.c | 66 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_backup_archiver.h | 6 ++- src/bin/pg_dump/pg_dump.c | 39 ++++++++++++++-- src/bin/pg_dump/pg_dumpall.c | 5 +++ src/bin/pg_dump/pg_restore.c | 4 ++ src/bin/pg_dump/t/002_pg_dump.pl | 63 ++++++++++++++++++++++---- doc/src/sgml/ref/pg_dump.sgml | 17 +++++++ doc/src/sgml/ref/pg_dumpall.sgml | 11 +++++ doc/src/sgml/ref/pg_restore.sgml | 11 +++++ 10 files changed, 210 insertions(+), 14 deletions(-) diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index fbf5f1c515..4eae73c16e 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -95,6 +95,7 @@ typedef struct _restoreOptions { int createDB; /* Issue commands to create the database */ int noOwner; /* Don't try to match original object owner */ + int noSequenceAm; /* Don't issue sequence-AM-related commands */ int noTableAm; /* Don't issue table-AM-related commands */ int noTablespace; /* Don't issue tablespace-related commands */ int disable_triggers; /* disable triggers during data-only @@ -185,6 +186,7 @@ typedef struct _dumpOptions int no_unlogged_table_data; int serializable_deferrable; int disable_triggers; + int outputNoSequenceAm; int outputNoTableAm; int outputNoTablespaces; int use_setsessauth; diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index c7a6c918a6..fbc7e49f36 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -171,6 +171,7 @@ dumpOptionsFromRestoreOptions(RestoreOptions *ropt) dopt->outputSuperuser = ropt->superuser; dopt->outputCreateDB = ropt->createDB; dopt->outputNoOwner = ropt->noOwner; + dopt->outputNoSequenceAm = ropt->noSequenceAm; dopt->outputNoTableAm = ropt->noTableAm; dopt->outputNoTablespaces = ropt->noTablespace; dopt->disable_triggers = ropt->disable_triggers; @@ -1221,6 +1222,7 @@ ArchiveEntry(Archive *AHX, CatalogId catalogId, DumpId dumpId, newToc->tag = pg_strdup(opts->tag); newToc->namespace = opts->namespace ? pg_strdup(opts->namespace) : NULL; newToc->tablespace = opts->tablespace ? pg_strdup(opts->tablespace) : NULL; + newToc->sequenceam = opts->sequenceam ? pg_strdup(opts->sequenceam) : NULL; newToc->tableam = opts->tableam ? pg_strdup(opts->tableam) : NULL; newToc->owner = opts->owner ? pg_strdup(opts->owner) : NULL; newToc->desc = pg_strdup(opts->description); @@ -2372,6 +2374,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt, AH->currUser = NULL; /* unknown */ AH->currSchema = NULL; /* ditto */ + AH->currSequenceAm = NULL; /* ditto */ AH->currTablespace = NULL; /* ditto */ AH->currTableAm = NULL; /* ditto */ @@ -2601,6 +2604,7 @@ WriteToc(ArchiveHandle *AH) WriteStr(AH, te->copyStmt); WriteStr(AH, te->namespace); WriteStr(AH, te->tablespace); + WriteStr(AH, te->sequenceam); WriteStr(AH, te->tableam); WriteStr(AH, te->owner); WriteStr(AH, "false"); @@ -2704,6 +2708,9 @@ ReadToc(ArchiveHandle *AH) if (AH->version >= K_VERS_1_10) te->tablespace = ReadStr(AH); + if (AH->version >= K_VERS_1_17) + te->sequenceam = ReadStr(AH); + if (AH->version >= K_VERS_1_14) te->tableam = ReadStr(AH); @@ -3356,6 +3363,9 @@ _reconnectToDB(ArchiveHandle *AH, const char *dbname) free(AH->currSchema); AH->currSchema = NULL; + free(AH->currSequenceAm); + AH->currSequenceAm = NULL; + free(AH->currTableAm); AH->currTableAm = NULL; @@ -3518,6 +3528,57 @@ _selectTablespace(ArchiveHandle *AH, const char *tablespace) destroyPQExpBuffer(qry); } +/* + * Set the proper default_sequence_access_method value for the sequence. + */ +static void +_selectSequenceAccessMethod(ArchiveHandle *AH, const char *sequenceam) +{ + RestoreOptions *ropt = AH->public.ropt; + PQExpBuffer cmd; + const char *want, + *have; + + /* do nothing in --no-sequence-access-method mode */ + if (ropt->noSequenceAm) + return; + + have = AH->currSequenceAm; + want = sequenceam; + + if (!want) + return; + + if (have && strcmp(want, have) == 0) + return; + + cmd = createPQExpBuffer(); + appendPQExpBuffer(cmd, + "SET default_sequence_access_method = %s;", + fmtId(want)); + + if (RestoringToDB(AH)) + { + PGresult *res; + + res = PQexec(AH->connection, cmd->data); + + if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) + warn_or_exit_horribly(AH, + "could not set default_sequence_access_method: %s", + PQerrorMessage(AH->connection)); + + PQclear(res); + } + else + ahprintf(AH, "%s\n\n", cmd->data); + + destroyPQExpBuffer(cmd); + + free(AH->currSequenceAm); + AH->currSequenceAm = pg_strdup(want); +} + /* * Set the proper default_table_access_method value for the table. */ @@ -3677,6 +3738,7 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData) _becomeOwner(AH, te); _selectOutputSchema(AH, te->namespace); _selectTablespace(AH, te->tablespace); + _selectSequenceAccessMethod(AH, te->sequenceam); _selectTableAccessMethod(AH, te->tableam); /* Emit header comment for item */ @@ -4171,6 +4233,8 @@ restore_toc_entries_prefork(ArchiveHandle *AH, TocEntry *pending_list) AH->currUser = NULL; free(AH->currSchema); AH->currSchema = NULL; + free(AH->currSequenceAm); + AH->currSequenceAm = NULL; free(AH->currTablespace); AH->currTablespace = NULL; free(AH->currTableAm); @@ -4910,6 +4974,7 @@ CloneArchive(ArchiveHandle *AH) clone->connCancel = NULL; clone->currUser = NULL; clone->currSchema = NULL; + clone->currSequenceAm = NULL; clone->currTableAm = NULL; clone->currTablespace = NULL; @@ -4969,6 +5034,7 @@ DeCloneArchive(ArchiveHandle *AH) /* Clear any connection-local state */ free(AH->currUser); free(AH->currSchema); + free(AH->currSequenceAm); free(AH->currTablespace); free(AH->currTableAm); free(AH->savedPassword); diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h index d6104a7196..9eb6f45389 100644 --- a/src/bin/pg_dump/pg_backup_archiver.h +++ b/src/bin/pg_dump/pg_backup_archiver.h @@ -70,10 +70,11 @@ * in header */ #define K_VERS_1_16 MAKE_ARCHIVE_VERSION(1, 16, 0) /* BLOB METADATA entries * and multiple BLOBS */ +#define K_VERS_1_17 MAKE_ARCHIVE_VERSION(1, 17, 0) /* add sequenceam */ /* Current archive version number (the format we can output) */ #define K_VERS_MAJOR 1 -#define K_VERS_MINOR 16 +#define K_VERS_MINOR 17 #define K_VERS_REV 0 #define K_VERS_SELF MAKE_ARCHIVE_VERSION(K_VERS_MAJOR, K_VERS_MINOR, K_VERS_REV) @@ -321,6 +322,7 @@ struct _archiveHandle /* these vars track state to avoid sending redundant SET commands */ char *currUser; /* current username, or NULL if unknown */ char *currSchema; /* current schema, or NULL */ + char *currSequenceAm; /* current sequence access method, or NULL */ char *currTablespace; /* current tablespace, or NULL */ char *currTableAm; /* current table access method, or NULL */ @@ -352,6 +354,7 @@ struct _tocEntry char *namespace; /* null or empty string if not in a schema */ char *tablespace; /* null if not in a tablespace; empty string * means use database default */ + char *sequenceam; /* table access method, only for SEQUENCE tags */ char *tableam; /* table access method, only for TABLE tags */ char *owner; char *desc; @@ -392,6 +395,7 @@ typedef struct _archiveOpts const char *tag; const char *namespace; const char *tablespace; + const char *sequenceam; const char *tableam; const char *owner; const char *description; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 851d228f79..83d0cb2165 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -422,6 +422,7 @@ main(int argc, char **argv) {"if-exists", no_argument, &dopt.if_exists, 1}, {"inserts", no_argument, NULL, 9}, {"lock-wait-timeout", required_argument, NULL, 2}, + {"no-sequence-access-method", no_argument, &dopt.outputNoSequenceAm, 1}, {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1}, {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1}, {"quote-all-identifiers", no_argument, "e_all_identifiers, 1}, @@ -1036,6 +1037,7 @@ main(int argc, char **argv) ropt->superuser = dopt.outputSuperuser; ropt->createDB = dopt.outputCreateDB; ropt->noOwner = dopt.outputNoOwner; + ropt->noSequenceAm = dopt.outputNoSequenceAm; ropt->noTableAm = dopt.outputNoTableAm; ropt->noTablespace = dopt.outputNoTablespaces; ropt->disable_triggers = dopt.disable_triggers; @@ -1152,6 +1154,7 @@ help(const char *progname) printf(_(" --no-publications do not dump publications\n")); printf(_(" --no-security-labels do not dump security label assignments\n")); printf(_(" --no-subscriptions do not dump subscriptions\n")); + printf(_(" --no-sequence-access-method do not sequence table access methods\n")); printf(_(" --no-table-access-method do not dump table access methods\n")); printf(_(" --no-tablespaces do not dump tablespace assignments\n")); printf(_(" --no-toast-compression do not dump TOAST compression methods\n")); @@ -13306,6 +13309,9 @@ dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo) case AMTYPE_INDEX: appendPQExpBufferStr(q, "TYPE INDEX "); break; + case AMTYPE_SEQUENCE: + appendPQExpBufferStr(q, "TYPE SEQUENCE "); + break; case AMTYPE_TABLE: appendPQExpBufferStr(q, "TYPE TABLE "); break; @@ -17547,7 +17553,8 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo) *maxv, *minv, *cache, - *seqtype; + *seqtype, + *seqam; bool cycled; bool is_ascending; int64 default_minv, @@ -17561,13 +17568,35 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo) qseqname = pg_strdup(fmtId(tbinfo->dobj.name)); - if (fout->remoteVersion >= 100000) + if (fout->remoteVersion >= 170000) { + /* + * PostgreSQL 17 has added support for sequence access methods. + */ + appendPQExpBuffer(query, + "SELECT format_type(s.seqtypid, NULL), " + "s.seqstart, s.seqincrement, " + "s.seqmax, s.seqmin, " + "s.seqcache, s.seqcycle, " + "a.amname AS seqam " + "FROM pg_catalog.pg_sequence s " + "JOIN pg_class c ON (c.oid = s.seqrelid) " + "JOIN pg_am a ON (a.oid = c.relam) " + "WHERE s.seqrelid = '%u'::oid", + tbinfo->dobj.catId.oid); + } + else if (fout->remoteVersion >= 100000) + { + /* + * PostgreSQL 10 has moved sequence metadata to the catalog + * pg_sequence. + */ appendPQExpBuffer(query, "SELECT format_type(seqtypid, NULL), " "seqstart, seqincrement, " "seqmax, seqmin, " - "seqcache, seqcycle " + "seqcache, seqcycle, " + "'local' AS seqam " "FROM pg_catalog.pg_sequence " "WHERE seqrelid = '%u'::oid", tbinfo->dobj.catId.oid); @@ -17583,7 +17612,7 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo) appendPQExpBuffer(query, "SELECT 'bigint' AS sequence_type, " "start_value, increment_by, max_value, min_value, " - "cache_value, is_cycled FROM %s", + "cache_value, is_cycled, 'local' as seqam FROM %s", fmtQualifiedDumpable(tbinfo)); } @@ -17602,6 +17631,7 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo) minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqam = PQgetvalue(res, 0, 7); /* Calculate default limits for a sequence of this type */ is_ascending = (incby[0] != '-'); @@ -17733,6 +17763,7 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo) ARCHIVE_OPTS(.tag = tbinfo->dobj.name, .namespace = tbinfo->dobj.namespace->dobj.name, .owner = tbinfo->rolname, + .sequenceam = seqam, .description = "SEQUENCE", .section = SECTION_PRE_DATA, .createStmt = query->data, diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 73337f3392..756a63c24d 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -97,6 +97,7 @@ static int disable_dollar_quoting = 0; static int disable_triggers = 0; static int if_exists = 0; static int inserts = 0; +static int no_sequence_access_method = 0; static int no_table_access_method = 0; static int no_tablespaces = 0; static int use_setsessauth = 0; @@ -161,6 +162,7 @@ main(int argc, char *argv[]) {"if-exists", no_argument, &if_exists, 1}, {"inserts", no_argument, &inserts, 1}, {"lock-wait-timeout", required_argument, NULL, 2}, + {"no-sequence-access-method", no_argument, &no_sequence_access_method, 1}, {"no-table-access-method", no_argument, &no_table_access_method, 1}, {"no-tablespaces", no_argument, &no_tablespaces, 1}, {"quote-all-identifiers", no_argument, "e_all_identifiers, 1}, @@ -435,6 +437,8 @@ main(int argc, char *argv[]) appendPQExpBufferStr(pgdumpopts, " --disable-triggers"); if (inserts) appendPQExpBufferStr(pgdumpopts, " --inserts"); + if (no_sequence_access_method) + appendPQExpBufferStr(pgdumpopts, " --no-sequence-access-method"); if (no_table_access_method) appendPQExpBufferStr(pgdumpopts, " --no-table-access-method"); if (no_tablespaces) @@ -668,6 +672,7 @@ help(void) printf(_(" --no-security-labels do not dump security label assignments\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-sequence-access-method do not dump sequence access methods\n")); printf(_(" --no-table-access-method do not dump table access methods\n")); printf(_(" --no-tablespaces do not dump tablespace assignments\n")); printf(_(" --no-toast-compression do not dump TOAST compression methods\n")); diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c index 5ea78cf7cc..077489f4b1 100644 --- a/src/bin/pg_dump/pg_restore.c +++ b/src/bin/pg_dump/pg_restore.c @@ -68,6 +68,7 @@ main(int argc, char **argv) static int enable_row_security = 0; static int if_exists = 0; static int no_data_for_failed_tables = 0; + static int outputNoSequenceAm = 0; static int outputNoTableAm = 0; static int outputNoTablespaces = 0; static int use_setsessauth = 0; @@ -115,6 +116,7 @@ main(int argc, char **argv) {"enable-row-security", no_argument, &enable_row_security, 1}, {"if-exists", no_argument, &if_exists, 1}, {"no-data-for-failed-tables", no_argument, &no_data_for_failed_tables, 1}, + {"no-sequence-access-method", no_argument, &outputNoSequenceAm, 1}, {"no-table-access-method", no_argument, &outputNoTableAm, 1}, {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, {"role", required_argument, NULL, 2}, @@ -363,6 +365,7 @@ main(int argc, char **argv) opts->disable_triggers = disable_triggers; opts->enable_row_security = enable_row_security; opts->noDataForFailedTables = no_data_for_failed_tables; + opts->noSequenceAm = outputNoSequenceAm; opts->noTableAm = outputNoTableAm; opts->noTablespace = outputNoTablespaces; opts->use_setsessauth = use_setsessauth; @@ -491,6 +494,7 @@ usage(const char *progname) printf(_(" --no-publications do not restore publications\n")); printf(_(" --no-security-labels do not restore security labels\n")); printf(_(" --no-subscriptions do not restore subscriptions\n")); + printf(_(" --no-sequence-access-method do not restore sequence access methods\n")); printf(_(" --no-table-access-method do not restore table access methods\n")); printf(_(" --no-tablespaces do not restore tablespace assignments\n")); printf(_(" --section=SECTION restore named section (pre-data, data, or post-data)\n")); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 0c057fef94..690501a189 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -537,6 +537,13 @@ my %pgdump_runs = ( 'postgres', ], }, + no_sequence_access_method => { + dump_cmd => [ + 'pg_dump', '--no-sync', + "--file=$tempdir/no_sequence_access_method.sql", + '--no-sequence-access-method', 'postgres', + ], + }, no_table_access_method => { dump_cmd => [ 'pg_dump', '--no-sync', @@ -711,6 +718,7 @@ my %full_runs = ( no_large_objects => 1, no_owner => 1, no_privs => 1, + no_sequence_access_method => 1, no_table_access_method => 1, pg_dumpall_dbprivs => 1, pg_dumpall_exclude => 1, @@ -3855,9 +3863,7 @@ my %tests = ( \QCREATE INDEX measurement_city_id_logdate_idx ON ONLY dump_test.measurement USING\E /xm, like => { - %full_runs, - %dump_test_schema_runs, - section_post_data => 1, + %full_runs, %dump_test_schema_runs, section_post_data => 1, }, unlike => { exclude_dump_test_schema => 1, @@ -4526,6 +4532,18 @@ my %tests = ( }, }, + 'CREATE ACCESS METHOD regress_test_sequence_am' => { + create_order => 11, + create_sql => + 'CREATE ACCESS METHOD regress_sequence_am TYPE SEQUENCE HANDLER local_sequenceam_handler;', + regexp => qr/^ + \QCREATE ACCESS METHOD regress_sequence_am TYPE SEQUENCE HANDLER local_sequenceam_handler;\E + \n/xm, + like => { + %full_runs, section_pre_data => 1, + }, + }, + # It's a bit tricky to ensure that the proper SET of default table # AM occurs. To achieve that we create a table with the standard # AM, test AM, standard AM. That guarantees that there needs to be @@ -4554,6 +4572,35 @@ my %tests = ( }, }, + + # This uses the same trick as for materialized views and tables, + # but this time with a sequence access method, checking that a + # correct set of SET queries are created. + 'CREATE SEQUENCE regress_pg_dump_seq_am' => { + create_order => 12, + create_sql => ' + CREATE SEQUENCE dump_test.regress_pg_dump_seq_am_0 USING local; + CREATE SEQUENCE dump_test.regress_pg_dump_seq_am_1 USING regress_sequence_am; + CREATE SEQUENCE dump_test.regress_pg_dump_seq_am_2 USING local;', + regexp => qr/^ + \QSET default_sequence_access_method = regress_sequence_am;\E + (\n(?!SET[^;]+;)[^\n]*)* + \n\QCREATE SEQUENCE dump_test.regress_pg_dump_seq_am_1\E + \n\s+\QSTART WITH 1\E + \n\s+\QINCREMENT BY 1\E + \n\s+\QNO MINVALUE\E + \n\s+\QNO MAXVALUE\E + \n\s+\QCACHE 1;\E\n/xm, + like => { + %full_runs, %dump_test_schema_runs, section_pre_data => 1, + }, + unlike => { + exclude_dump_test_schema => 1, + no_sequence_access_method => 1, + only_dump_measurement => 1, + }, + }, + 'CREATE MATERIALIZED VIEW regress_pg_dump_matview_am' => { create_order => 13, create_sql => ' @@ -4783,10 +4830,8 @@ $node->command_fails_like( ############################################################## # Test dumping pg_catalog (for research -- cannot be reloaded) -$node->command_ok( - [ 'pg_dump', '-p', "$port", '-n', 'pg_catalog' ], - 'pg_dump: option -n pg_catalog' -); +$node->command_ok([ 'pg_dump', '-p', "$port", '-n', 'pg_catalog' ], + 'pg_dump: option -n pg_catalog'); ######################################### # Test valid database exclusion patterns @@ -4953,8 +4998,8 @@ foreach my $run (sort keys %pgdump_runs) } # Check for useless entries in "unlike" list. Runs that are # not listed in "like" don't need to be excluded in "unlike". - if ($tests{$test}->{unlike}->{$test_key} && - !defined($tests{$test}->{like}->{$test_key})) + if ($tests{$test}->{unlike}->{$test_key} + && !defined($tests{$test}->{like}->{$test_key})) { die "useless \"unlike\" entry \"$test_key\" in test \"$test\""; } diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index b99793e414..bb70d3a40f 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -1104,6 +1104,23 @@ PostgreSQL documentation + + + + + Do not output commands to select sequence access methods. + With this option, all objects will be created with whichever + sequence access method is the default during restore. + + + + This option is ignored when emitting an archive (non-text) output + file. For the archive formats, you can specify the option when you + call pg_restore. + + + + diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml index 4d7c046468..34643175fb 100644 --- a/doc/src/sgml/ref/pg_dumpall.sgml +++ b/doc/src/sgml/ref/pg_dumpall.sgml @@ -479,6 +479,17 @@ exclude database PATTERN + + + + + Do not output commands to select sequence access methods. + With this option, all objects will be created with whichever + sequence access method is the default during restore. + + + + diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml index 2e3ba80258..c552a1e746 100644 --- a/doc/src/sgml/ref/pg_restore.sgml +++ b/doc/src/sgml/ref/pg_restore.sgml @@ -733,6 +733,17 @@ PostgreSQL documentation + + + + + Do not output commands to select sequence access methods. + With this option, all objects will be created with whichever + sequence access method is the default during restore. + + + + -- 2.43.0