X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbin%2Fpg_dump%2Fpg_dump.c;h=c2bb6161b214811d38af43f480e5bb517d4db0c1;hb=f0fedfe82c8adea78354652d67c027a1a8fbce88;hp=c3c861d85787ff9b89bbb9074403a55d6b14ee5f;hpb=1cc1b91d1b09a5cdd9fc51c9eee31effd2227b4f;p=postgresql diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index c3c861d857..c2bb6161b2 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -4,7 +4,7 @@ * pg_dump is a utility for dumping out a postgres database * into a script file. * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * pg_dump will read the system catalogs in a database and dump out a @@ -14,13 +14,13 @@ * Note that pg_dump runs in a transaction-snapshot mode transaction, * so it sees a consistent snapshot of the database including system * catalogs. However, it relies in part on various specialized backend - * functions like pg_get_indexdef(), and those things tend to run on - * SnapshotNow time, ie they look at the currently committed state. So - * it is possible to get 'cache lookup failed' error if someone - * performs DDL changes while a dump is happening. The window for this - * sort of thing is from the acquisition of the transaction snapshot to - * getSchemaData() (when pg_dump acquires AccessShareLock on every - * table it intends to dump). It isn't very large, but it can happen. + * functions like pg_get_indexdef(), and those things tend to look at + * the currently committed state. So it is possible to get 'cache + * lookup failed' error if someone performs DDL changes while a dump is + * happening. The window for this sort of thing is from the acquisition + * of the transaction snapshot to getSchemaData() (when pg_dump acquires + * AccessShareLock on every table it intends to dump). It isn't very large, + * but it can happen. * * http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php * @@ -49,6 +49,7 @@ #include "catalog/pg_cast.h" #include "catalog/pg_class.h" #include "catalog/pg_default_acl.h" +#include "catalog/pg_event_trigger.h" #include "catalog/pg_largeobject.h" #include "catalog/pg_largeobject_metadata.h" #include "catalog/pg_proc.h" @@ -58,12 +59,9 @@ #include "pg_backup_archiver.h" #include "pg_backup_db.h" -#include "dumpmem.h" +#include "pg_backup_utils.h" #include "dumputils.h" - -extern char *optarg; -extern int optind, - opterr; +#include "parallel.h" typedef struct @@ -88,11 +86,11 @@ bool g_verbose; /* User wants verbose narration of our * activities. */ /* various user-settable parameters */ -bool schemaOnly; -bool dataOnly; -int dumpSections; /* bitmask of chosen sections */ -bool aclsSkip; -const char *lockWaitTimeout; +static bool schemaOnly; +static bool dataOnly; +static int dumpSections; /* bitmask of chosen sections */ +static bool aclsSkip; +static const char *lockWaitTimeout; /* subquery used to convert user ID (eg, datdba) to user name */ static const char *username_subquery; @@ -129,22 +127,19 @@ char g_comment_end[10]; static const CatalogId nilCatalogId = {0, 0}; -/* these are to avoid passing around info for findNamespace() */ -static NamespaceInfo *g_namespaces; -static int g_numNamespaces; - /* flags for various command-line long options */ static int binary_upgrade = 0; static int disable_dollar_quoting = 0; static int dump_inserts = 0; static int column_inserts = 0; +static int if_exists = 0; static int no_security_labels = 0; +static int no_synchronized_snapshots = 0; static int no_unlogged_table_data = 0; static int serializable_deferrable = 0; static void help(const char *progname); -static void pgdump_cleanup_at_exit(int code, void *arg); static void setup_connection(Archive *AH, const char *dumpencoding, char *use_role); static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode); @@ -156,6 +151,7 @@ static void expand_table_name_patterns(Archive *fout, SimpleOidList *oids); static NamespaceInfo *findNamespace(Archive *fout, Oid nsoid, Oid objoid); static void dumpTableData(Archive *fout, TableDataInfo *tdinfo); +static void refreshMatViewData(Archive *fout, TableDataInfo *tdinfo); static void guessConstraintInheritance(TableInfo *tblinfo, int numTables); static void dumpComment(Archive *fout, const char *target, const char *namespace, const char *owner, @@ -191,10 +187,12 @@ static void dumpConversion(Archive *fout, ConvInfo *convinfo); static void dumpRule(Archive *fout, RuleInfo *rinfo); static void dumpAgg(Archive *fout, AggInfo *agginfo); static void dumpTrigger(Archive *fout, TriggerInfo *tginfo); +static void dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo); static void dumpTable(Archive *fout, TableInfo *tbinfo); static void dumpTableSchema(Archive *fout, TableInfo *tbinfo); static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo); static void dumpSequence(Archive *fout, TableInfo *tbinfo); +static void dumpSequenceData(Archive *fout, TableDataInfo *tdinfo); static void dumpIndex(Archive *fout, IndxInfo *indxinfo); static void dumpConstraint(Archive *fout, ConstraintInfo *coninfo); static void dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo); @@ -215,29 +213,37 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, const char *acls); static void getDependencies(Archive *fout); +static void BuildArchiveDependencies(Archive *fout); +static void findDumpableDependencies(ArchiveHandle *AH, DumpableObject *dobj, + DumpId **dependencies, int *nDeps, int *allocDeps); + +static DumpableObject *createBoundaryObjects(void); +static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs, + DumpableObject *boundaryObjs); + static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo); static void getTableData(TableInfo *tblinfo, int numTables, bool oids); static void makeTableDataInfo(TableInfo *tbinfo, bool oids); +static void buildMatViewRefreshDependencies(Archive *fout); static void getTableDataFKConstraints(void); -static char *format_function_arguments(FuncInfo *finfo, char *funcargs); +static char *format_function_arguments(FuncInfo *finfo, char *funcargs, + bool is_agg); static char *format_function_arguments_old(Archive *fout, FuncInfo *finfo, int nallargs, char **allargtypes, char **argmodes, char **argnames); static char *format_function_signature(Archive *fout, - FuncInfo *finfo, bool honor_quotes); -static const char *convertRegProcReference(Archive *fout, - const char *proc); -static const char *convertOperatorReference(Archive *fout, const char *opr); + FuncInfo *finfo, bool honor_quotes); +static char *convertRegProcReference(Archive *fout, + const char *proc); +static char *convertOperatorReference(Archive *fout, const char *opr); static const char *convertTSFunction(Archive *fout, Oid funcOid); static Oid findLastBuiltinOid_V71(Archive *fout, const char *); static Oid findLastBuiltinOid_V70(Archive *fout); static void selectSourceSchema(Archive *fout, const char *schemaName); static char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts); static char *myFormatType(const char *typname, int32 typmod); -static const char *fmtQualifiedId(Archive *fout, - const char *schema, const char *id); static void getBlobs(Archive *fout); static void dumpBlob(Archive *fout, BlobInfo *binfo); static int dumpBlobs(Archive *fout, void *arg); @@ -255,8 +261,11 @@ static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer, DumpableObject *dobj, const char *objlabel); static const char *getAttrName(int attrnum, TableInfo *tblInfo); -static const char *fmtCopyColumnList(const TableInfo *ti); +static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer); +static char *get_synchronized_snapshot(Archive *fout); static PGresult *ExecuteSqlQueryForSingleRow(Archive *fout, char *query); +static void setupDumpWorker(Archive *AHX, RestoreOptions *ropt); + int main(int argc, char **argv) @@ -274,7 +283,9 @@ main(int argc, char **argv) int numTables; DumpableObject **dobjs; int numObjs; + DumpableObject *boundaryObjs; int i; + int numWorkers = 1; enum trivalue prompt_password = TRI_DEFAULT; int compressLevel = -1; int plainText = 0; @@ -284,12 +295,11 @@ main(int argc, char **argv) int outputNoOwner = 0; char *outputSuperuser = NULL; char *use_role = NULL; - int my_version; int optindex; RestoreOptions *ropt; ArchiveFormat archiveFormat = archUnknown; ArchiveMode archiveMode; - Archive *fout; /* the script file */ + Archive *fout; /* the script file */ static int disable_triggers = 0; static int outputNoTablespaces = 0; @@ -300,10 +310,12 @@ main(int argc, char **argv) {"blobs", no_argument, NULL, 'b'}, {"clean", no_argument, NULL, 'c'}, {"create", no_argument, NULL, 'C'}, + {"dbname", required_argument, NULL, 'd'}, {"file", required_argument, NULL, 'f'}, {"format", required_argument, NULL, 'F'}, {"host", required_argument, NULL, 'h'}, {"ignore-version", no_argument, NULL, 'i'}, + {"jobs", 1, NULL, 'j'}, {"no-reconnect", no_argument, NULL, 'R'}, {"oids", no_argument, NULL, 'o'}, {"no-owner", no_argument, NULL, 'O'}, @@ -334,6 +346,7 @@ main(int argc, char **argv) {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1}, {"disable-triggers", no_argument, &disable_triggers, 1}, {"exclude-table-data", required_argument, NULL, 4}, + {"if-exists", no_argument, &if_exists, 1}, {"inserts", no_argument, &dump_inserts, 1}, {"lock-wait-timeout", required_argument, NULL, 2}, {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, @@ -343,6 +356,7 @@ main(int argc, char **argv) {"serializable-deferrable", no_argument, &serializable_deferrable, 1}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, {"no-security-labels", no_argument, &no_security_labels, 1}, + {"no-synchronized-snapshots", no_argument, &no_synchronized_snapshots, 1}, {"no-unlogged-table-data", no_argument, &no_unlogged_table_data, 1}, {NULL, 0, NULL, 0} @@ -350,6 +364,12 @@ main(int argc, char **argv) set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump")); + /* + * Initialize what we need for parallel execution, especially for thread + * support on Windows. + */ + init_parallel_dump_utils(); + g_verbose = false; strcpy(g_comment_start, "-- "); @@ -380,7 +400,7 @@ main(int argc, char **argv) } } - while ((c = getopt_long(argc, argv, "abcCE:f:F:h:in:N:oOp:RsS:t:T:U:vwWxZ:", + while ((c = getopt_long(argc, argv, "abcCd:E:f:F:h:ij:n:N:oOp:RsS:t:T:U:vwWxZ:", long_options, &optindex)) != -1) { switch (c) @@ -401,26 +421,34 @@ main(int argc, char **argv) outputCreateDB = 1; break; + case 'd': /* database name */ + dbname = pg_strdup(optarg); + break; + case 'E': /* Dump encoding */ - dumpencoding = optarg; + dumpencoding = pg_strdup(optarg); break; case 'f': - filename = optarg; + filename = pg_strdup(optarg); break; case 'F': - format = optarg; + format = pg_strdup(optarg); break; case 'h': /* server host */ - pghost = optarg; + pghost = pg_strdup(optarg); break; case 'i': /* ignored, deprecated option */ break; + case 'j': /* number of dump jobs */ + numWorkers = atoi(optarg); + break; + case 'n': /* include schema(s) */ simple_string_list_append(&schema_include_patterns, optarg); include_everything = false; @@ -439,7 +467,7 @@ main(int argc, char **argv) break; case 'p': /* server port */ - pgport = optarg; + pgport = pg_strdup(optarg); break; case 'R': @@ -464,7 +492,7 @@ main(int argc, char **argv) break; case 'U': - username = optarg; + username = pg_strdup(optarg); break; case 'v': /* verbose */ @@ -492,19 +520,19 @@ main(int argc, char **argv) break; case 2: /* lock-wait-timeout */ - lockWaitTimeout = optarg; + lockWaitTimeout = pg_strdup(optarg); break; case 3: /* SET ROLE */ - use_role = optarg; + use_role = pg_strdup(optarg); break; - case 4: /* exclude table(s) data */ + case 4: /* exclude table(s) data */ simple_string_list_append(&tabledata_exclude_patterns, optarg); break; case 5: /* section */ - set_section(optarg, &dumpSections); + set_dump_section(optarg, &dumpSections); break; default: @@ -513,8 +541,11 @@ main(int argc, char **argv) } } - /* Get database name from command line */ - if (optind < argc) + /* + * Non-option argument specifies database name as long as it wasn't + * already specified with -d / --dbname + */ + if (optind < argc && dbname == NULL) dbname = argv[optind++]; /* Complain if any arguments remain */ @@ -532,23 +563,16 @@ main(int argc, char **argv) dump_inserts = 1; if (dataOnly && schemaOnly) - exit_horribly(NULL, "options -s/--schema-only and -a/--data-only cannot be used together\n"); - - if ((dataOnly || schemaOnly) && dumpSections != DUMP_UNSECTIONED) - exit_horribly(NULL, "options -s/--schema-only and -a/--data-only cannot be used with --section\n"); - - if (dataOnly) - dumpSections = DUMP_DATA; - else if (schemaOnly) - dumpSections = DUMP_PRE_DATA | DUMP_POST_DATA; - else if ( dumpSections != DUMP_UNSECTIONED) { - dataOnly = dumpSections == DUMP_DATA; - schemaOnly = !(dumpSections & DUMP_DATA); + write_msg(NULL, "options -s/--schema-only and -a/--data-only cannot be used together\n"); + exit_nicely(1); } if (dataOnly && outputClean) - exit_horribly(NULL, "options -c/--clean and -a/--data-only cannot be used together\n"); + { + write_msg(NULL, "options -c/--clean and -a/--data-only cannot be used together\n"); + exit_nicely(1); + } if (dump_inserts && oids) { @@ -557,6 +581,9 @@ main(int argc, char **argv) exit_nicely(1); } + if (if_exists && !outputClean) + exit_horribly(NULL, "option --if-exists requires -c/--clean option\n"); + /* Identify archive format to emit */ archiveFormat = parseArchiveFormat(format, &archiveMode); @@ -573,9 +600,28 @@ main(int argc, char **argv) compressLevel = 0; } + /* + * On Windows we can only have at most MAXIMUM_WAIT_OBJECTS (= 64 usually) + * parallel jobs because that's the maximum limit for the + * WaitForMultipleObjects() call. + */ + if (numWorkers <= 0 +#ifdef WIN32 + || numWorkers > MAXIMUM_WAIT_OBJECTS +#endif + ) + exit_horribly(NULL, "%s: invalid number of parallel jobs\n", progname); + + /* Parallel backup only in the directory archive format so far */ + if (archiveFormat != archDirectory && numWorkers > 1) + exit_horribly(NULL, "parallel backup only supported by the directory format\n"); + /* Open the output file */ - fout = CreateArchive(filename, archiveFormat, compressLevel, archiveMode); - on_exit_nicely(pgdump_cleanup_at_exit, fout); + fout = CreateArchive(filename, archiveFormat, compressLevel, archiveMode, + setupDumpWorker); + + /* Register the cleanup hook */ + on_exit_close_archive(fout); if (fout == NULL) exit_horribly(NULL, "could not open output file \"%s\" for writing\n", filename); @@ -583,16 +629,14 @@ main(int argc, char **argv) /* Let the archiver know how noisy to be */ fout->verbose = g_verbose; - my_version = parse_version(PG_VERSION); - if (my_version < 0) - exit_horribly(NULL, "could not parse version string \"%s\"\n", PG_VERSION); - /* * We allow the server to be back to 7.0, and up to any minor release of * our own major version. (See also version check in pg_dumpall.c.) */ fout->minRemoteVersion = 70000; - fout->maxRemoteVersion = (my_version / 100) * 100 + 99; + fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99; + + fout->numWorkers = numWorkers; /* * Open the database using the Archiver, so it knows about it. Errors mean @@ -609,23 +653,23 @@ main(int argc, char **argv) no_security_labels = 1; /* - * Start transaction-snapshot mode transaction to dump consistent data. + * When running against 9.0 or later, check if we are in recovery mode, + * which means we are on a hot standby. */ - ExecuteSqlStatement(fout, "BEGIN"); - if (fout->remoteVersion >= 90100) + if (fout->remoteVersion >= 90000) { - if (serializable_deferrable) - ExecuteSqlStatement(fout, - "SET TRANSACTION ISOLATION LEVEL " - "SERIALIZABLE, READ ONLY, DEFERRABLE"); - else - ExecuteSqlStatement(fout, - "SET TRANSACTION ISOLATION LEVEL " - "REPEATABLE READ"); + PGresult *res = ExecuteSqlQueryForSingleRow(fout, "SELECT pg_catalog.pg_is_in_recovery()"); + + if (strcmp(PQgetvalue(res, 0, 0), "t") == 0) + { + /* + * On hot standby slaves, never try to dump unlogged table data, + * since it will just throw an error. + */ + no_unlogged_table_data = true; + } + PQclear(res); } - else - ExecuteSqlStatement(fout, - "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"); /* Select the appropriate subquery to convert user IDs to names */ if (fout->remoteVersion >= 80100) @@ -635,12 +679,20 @@ main(int argc, char **argv) else username_subquery = "SELECT usename FROM pg_user WHERE usesysid ="; + /* check the version for the synchronized snapshots feature */ + if (numWorkers > 1 && fout->remoteVersion < 90200 + && !no_synchronized_snapshots) + exit_horribly(NULL, + "Synchronized snapshots are not supported by this server version.\n" + "Run with --no-synchronized-snapshots instead if you do not need\n" + "synchronized snapshots.\n"); + /* Find the last built-in OID, if needed */ if (fout->remoteVersion < 70300) { if (fout->remoteVersion >= 70100) g_last_builtin_oid = findLastBuiltinOid_V71(fout, - PQdb(GetConnection(fout))); + PQdb(GetConnection(fout))); else g_last_builtin_oid = findLastBuiltinOid_V70(fout); if (g_verbose) @@ -694,6 +746,7 @@ main(int argc, char **argv) if (!schemaOnly) { getTableData(tblinfo, numTables, oids); + buildMatViewRefreshDependencies(fout); if (dataOnly) getTableDataFKConstraints(); } @@ -706,6 +759,17 @@ main(int argc, char **argv) */ getDependencies(fout); + /* Lastly, create dummy objects to represent the section boundaries */ + boundaryObjs = createBoundaryObjects(); + + /* Get pointers to all the known DumpableObjects */ + getDumpableObjects(&dobjs, &numObjs); + + /* + * Add dummy dependencies to enforce the dump section ordering. + */ + addBoundaryDependencies(dobjs, numObjs, boundaryObjs); + /* * Sort the objects into a safe dump order (no forward references). * @@ -715,14 +779,17 @@ main(int argc, char **argv) * will dump identically. Before 7.3 we don't have dependencies and we * use OID ordering as an (unreliable) guide to creation order. */ - getDumpableObjects(&dobjs, &numObjs); - if (fout->remoteVersion >= 70300) sortDumpableObjectsByTypeName(dobjs, numObjs); else sortDumpableObjectsByTypeOid(dobjs, numObjs); - sortDumpableObjects(dobjs, numObjs); + /* If we do a parallel dump, we want the largest tables to go first */ + if (archiveFormat == archDirectory && numWorkers > 1) + sortDataAndIndexObjectsBySize(dobjs, numObjs); + + sortDumpableObjects(dobjs, numObjs, + boundaryObjs[0].dumpId, boundaryObjs[1].dumpId); /* * Create archive TOC entries for all the objects to be dumped, in a safe @@ -742,31 +809,49 @@ main(int argc, char **argv) dumpDumpableObject(fout, dobjs[i]); /* - * And finally we can do the actual output. + * Set up options info to ensure we dump what we want. */ - if (plainText) - { - ropt = NewRestoreOptions(); - ropt->filename = filename; - ropt->dropSchema = outputClean; - ropt->aclsSkip = aclsSkip; - ropt->superuser = outputSuperuser; - ropt->createDB = outputCreateDB; - ropt->noOwner = outputNoOwner; - ropt->noTablespace = outputNoTablespaces; - ropt->disable_triggers = disable_triggers; - ropt->use_setsessauth = use_setsessauth; - ropt->dataOnly = dataOnly; - - if (compressLevel == -1) - ropt->compression = 0; - else - ropt->compression = compressLevel; + ropt = NewRestoreOptions(); + ropt->filename = filename; + ropt->dropSchema = outputClean; + ropt->dataOnly = dataOnly; + ropt->schemaOnly = schemaOnly; + ropt->if_exists = if_exists; + ropt->dumpSections = dumpSections; + ropt->aclsSkip = aclsSkip; + ropt->superuser = outputSuperuser; + ropt->createDB = outputCreateDB; + ropt->noOwner = outputNoOwner; + ropt->noTablespace = outputNoTablespaces; + ropt->disable_triggers = disable_triggers; + ropt->use_setsessauth = use_setsessauth; - ropt->suppressDumpWarnings = true; /* We've already shown them */ + if (compressLevel == -1) + ropt->compression = 0; + else + ropt->compression = compressLevel; - RestoreArchive(fout, ropt); - } + ropt->suppressDumpWarnings = true; /* We've already shown them */ + + SetArchiveRestoreOptions(fout, ropt); + + /* + * The archive's TOC entries are now marked as to which ones will actually + * be output, so we can set up their dependency lists properly. This isn't + * necessary for plain-text output, though. + */ + if (!plainText) + BuildArchiveDependencies(fout); + + /* + * And finally we can do the actual output. + * + * Note: for non-plain-text output formats, the output file is written + * inside CloseArchive(). This is, um, bizarre; but not worth changing + * right now. + */ + if (plainText) + RestoreArchive(fout); CloseArchive(fout); @@ -782,48 +867,52 @@ help(const char *progname) printf(_(" %s [OPTION]... [DBNAME]\n"), progname); printf(_("\nGeneral options:\n")); - printf(_(" -f, --file=FILENAME output file or directory name\n")); - printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n" - " plain text (default))\n")); - printf(_(" -v, --verbose verbose mode\n")); - printf(_(" -Z, --compress=0-9 compression level for compressed formats\n")); - printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n")); - printf(_(" --help show this help, then exit\n")); - printf(_(" --version output version information, then exit\n")); + printf(_(" -f, --file=FILENAME output file or directory name\n")); + printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n" + " plain text (default))\n")); + printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n")); + printf(_(" -v, --verbose verbose mode\n")); + printf(_(" -V, --version output version information, then exit\n")); + printf(_(" -Z, --compress=0-9 compression level for compressed formats\n")); + printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n")); + 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(_(" -b, --blobs include large objects in dump\n")); - printf(_(" -c, --clean clean (drop) database objects before recreating\n")); - printf(_(" -C, --create include commands to create database in dump\n")); - printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n")); - printf(_(" -n, --schema=SCHEMA dump the named schema(s) only\n")); - printf(_(" -N, --exclude-schema=SCHEMA do NOT dump the named schema(s)\n")); - printf(_(" -o, --oids include OIDs in dump\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, --superuser=NAME superuser user name to use in plain-text format\n")); - printf(_(" -t, --table=TABLE dump the named table(s) only\n")); - printf(_(" -T, --exclude-table=TABLE do NOT dump the named table(s)\n")); - printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\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")); - printf(_(" --disable-triggers disable triggers during data-only restore\n")); - printf(_(" --exclude-table-data=TABLE do NOT dump data for the named table(s)\n")); - printf(_(" --inserts dump data as INSERT commands, rather than COPY\n")); - printf(_(" --no-security-labels do not dump security label assignments\n")); - printf(_(" --no-tablespaces do not dump tablespace assignments\n")); - printf(_(" --no-unlogged-table-data do not dump unlogged table data\n")); - printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n")); - printf(_(" --section=SECTION dump named section (pre-data, data or post-data)\n")); - printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n")); + printf(_(" -a, --data-only dump only the data, not the schema\n")); + printf(_(" -b, --blobs include large objects in dump\n")); + printf(_(" -c, --clean clean (drop) database objects before recreating\n")); + printf(_(" -C, --create include commands to create database in dump\n")); + printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n")); + printf(_(" -n, --schema=SCHEMA dump the named schema(s) only\n")); + printf(_(" -N, --exclude-schema=SCHEMA do NOT dump the named schema(s)\n")); + printf(_(" -o, --oids include OIDs in dump\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, --superuser=NAME superuser user name to use in plain-text format\n")); + printf(_(" -t, --table=TABLE dump the named table(s) only\n")); + printf(_(" -T, --exclude-table=TABLE do NOT dump the named table(s)\n")); + printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\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")); + printf(_(" --disable-triggers disable triggers during data-only restore\n")); + printf(_(" --exclude-table-data=TABLE do NOT dump data for the named table(s)\n")); + printf(_(" --if-exists use IF EXISTS when dropping objects\n")); + printf(_(" --inserts dump data as INSERT commands, rather than COPY\n")); + printf(_(" --no-security-labels do not dump security label assignments\n")); + printf(_(" --no-synchronized-snapshots do not use synchronized snapshots in parallel jobs\n")); + printf(_(" --no-tablespaces do not dump tablespace assignments\n")); + printf(_(" --no-unlogged-table-data do not dump unlogged table data\n")); + printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n")); + printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n")); + printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n")); printf(_(" --use-set-session-authorization\n" - " use SET SESSION AUTHORIZATION commands instead of\n" - " ALTER OWNER commands to set ownership\n")); + " use SET SESSION AUTHORIZATION commands instead of\n" + " ALTER OWNER commands to set ownership\n")); printf(_("\nConnection options:\n")); + printf(_(" -d, --dbname=DBNAME database to dump\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); printf(_(" -p, --port=PORT database server port number\n")); printf(_(" -U, --username=NAME connect as specified database user\n")); @@ -836,21 +925,18 @@ help(const char *progname) printf(_("Report bugs to .\n")); } -static void -pgdump_cleanup_at_exit(int code, void *arg) -{ - Archive *AH = (Archive *) arg; - - DisconnectDatabase(AH); -} - static void setup_connection(Archive *AH, const char *dumpencoding, char *use_role) { PGconn *conn = GetConnection(AH); const char *std_strings; - /* Set the client encoding if requested */ + /* + * Set the client encoding if requested. If dumpencoding == NULL then + * either it hasn't been requested or we're a cloned connection and then + * this has already been set in CloneArchive according to the original + * connection encoding. + */ if (dumpencoding) { if (PQsetClientEncoding(conn, dumpencoding) < 0) @@ -867,6 +953,10 @@ setup_connection(Archive *AH, const char *dumpencoding, char *use_role) std_strings = PQparameterStatus(conn, "standard_conforming_strings"); AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0); + /* Set the role if requested */ + if (!use_role && AH->use_role) + use_role = AH->use_role; + /* Set the role if requested */ if (use_role && AH->remoteVersion >= 80100) { @@ -875,6 +965,10 @@ setup_connection(Archive *AH, const char *dumpencoding, char *use_role) appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role)); ExecuteSqlStatement(AH, query->data); destroyPQExpBuffer(query); + + /* save this for later use on parallel connections */ + if (!AH->use_role) + AH->use_role = strdup(use_role); } /* Set the datestyle to ISO to ensure the dump's portability */ @@ -905,12 +999,77 @@ setup_connection(Archive *AH, const char *dumpencoding, char *use_role) */ if (AH->remoteVersion >= 70300) ExecuteSqlStatement(AH, "SET statement_timeout = 0"); + if (AH->remoteVersion >= 90300) + ExecuteSqlStatement(AH, "SET lock_timeout = 0"); /* * Quote all identifiers, if requested. */ if (quote_all_identifiers && AH->remoteVersion >= 90100) ExecuteSqlStatement(AH, "SET quote_all_identifiers = true"); + + /* + * Start transaction-snapshot mode transaction to dump consistent data. + */ + ExecuteSqlStatement(AH, "BEGIN"); + if (AH->remoteVersion >= 90100) + { + if (serializable_deferrable) + ExecuteSqlStatement(AH, + "SET TRANSACTION ISOLATION LEVEL " + "SERIALIZABLE, READ ONLY, DEFERRABLE"); + else + ExecuteSqlStatement(AH, + "SET TRANSACTION ISOLATION LEVEL " + "REPEATABLE READ, READ ONLY"); + } + else if (AH->remoteVersion >= 70400) + { + /* note: comma was not accepted in SET TRANSACTION before 8.0 */ + ExecuteSqlStatement(AH, + "SET TRANSACTION ISOLATION LEVEL " + "SERIALIZABLE READ ONLY"); + } + else + ExecuteSqlStatement(AH, + "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"); + + + + if (AH->numWorkers > 1 && AH->remoteVersion >= 90200 && !no_synchronized_snapshots) + { + if (AH->sync_snapshot_id) + { + PQExpBuffer query = createPQExpBuffer(); + + appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT "); + appendStringLiteralConn(query, AH->sync_snapshot_id, conn); + ExecuteSqlStatement(AH, query->data); + destroyPQExpBuffer(query); + } + else + AH->sync_snapshot_id = get_synchronized_snapshot(AH); + } +} + +static void +setupDumpWorker(Archive *AHX, RestoreOptions *ropt) +{ + setup_connection(AHX, NULL, NULL); +} + +static char * +get_synchronized_snapshot(Archive *fout) +{ + char *query = "SELECT pg_export_snapshot()"; + char *result; + PGresult *res; + + res = ExecuteSqlQueryForSingleRow(fout, query); + result = strdup(PQgetvalue(res, 0, 0)); + PQclear(res); + + return result; } static ArchiveFormat @@ -934,13 +1093,6 @@ parseArchiveFormat(const char *format, ArchiveMode *mode) archiveFormat = archDirectory; else if (pg_strcasecmp(format, "directory") == 0) archiveFormat = archDirectory; - else if (pg_strcasecmp(format, "f") == 0 || pg_strcasecmp(format, "file") == 0) - - /* - * Dump files into the current directory; for demonstration only, not - * documented. - */ - archiveFormat = archFiles; else if (pg_strcasecmp(format, "p") == 0) archiveFormat = archNull; else if (pg_strcasecmp(format, "plain") == 0) @@ -984,7 +1136,7 @@ expand_schema_name_patterns(Archive *fout, for (cell = patterns->head; cell; cell = cell->next) { if (cell != patterns->head) - appendPQExpBuffer(query, "UNION ALL\n"); + appendPQExpBufferStr(query, "UNION ALL\n"); appendPQExpBuffer(query, "SELECT oid FROM pg_catalog.pg_namespace n\n"); processSQLNamePattern(GetConnection(fout), query, cell->val, false, @@ -1028,14 +1180,14 @@ expand_table_name_patterns(Archive *fout, for (cell = patterns->head; cell; cell = cell->next) { if (cell != patterns->head) - appendPQExpBuffer(query, "UNION ALL\n"); + appendPQExpBufferStr(query, "UNION ALL\n"); appendPQExpBuffer(query, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -1144,6 +1296,7 @@ selectDumpableType(TypeInfo *tyinfo) if (tyinfo->isArray) { tyinfo->dobj.objType = DO_DUMMY_TYPE; + /* * Fall through to set the dump flag; we assume that the subsequent * rules will do the same thing as they would for the array's base @@ -1234,6 +1387,12 @@ dumpTableData_copy(Archive *fout, void *dcontext) const bool hasoids = tbinfo->hasoids; const bool oids = tdinfo->oids; PQExpBuffer q = createPQExpBuffer(); + + /* + * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId + * which uses it already. + */ + PQExpBuffer clistBuf = createPQExpBuffer(); PGconn *conn = GetConnection(fout); PGresult *res; int ret; @@ -1258,14 +1417,14 @@ dumpTableData_copy(Archive *fout, void *dcontext) * cases involving ADD COLUMN and inheritance.) */ if (fout->remoteVersion >= 70300) - column_list = fmtCopyColumnList(tbinfo); + column_list = fmtCopyColumnList(tbinfo, clistBuf); else column_list = ""; /* can't select columns in COPY */ if (oids && hasoids) { appendPQExpBuffer(q, "COPY %s %s WITH OIDS TO stdout;", - fmtQualifiedId(fout, + fmtQualifiedId(fout->remoteVersion, tbinfo->dobj.namespace->dobj.name, classname), column_list); @@ -1283,7 +1442,7 @@ dumpTableData_copy(Archive *fout, void *dcontext) else appendPQExpBufferStr(q, "* "); appendPQExpBuffer(q, "FROM %s %s) TO stdout;", - fmtQualifiedId(fout, + fmtQualifiedId(fout->remoteVersion, tbinfo->dobj.namespace->dobj.name, classname), tdinfo->filtercond); @@ -1291,13 +1450,14 @@ dumpTableData_copy(Archive *fout, void *dcontext) else { appendPQExpBuffer(q, "COPY %s %s TO stdout;", - fmtQualifiedId(fout, + fmtQualifiedId(fout->remoteVersion, tbinfo->dobj.namespace->dobj.name, classname), column_list); } res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT); PQclear(res); + destroyPQExpBuffer(clistBuf); for (;;) { @@ -1399,6 +1559,7 @@ dumpTableData_insert(Archive *fout, void *dcontext) TableInfo *tbinfo = tdinfo->tdtable; const char *classname = tbinfo->dobj.name; PQExpBuffer q = createPQExpBuffer(); + PQExpBuffer insertStmt = NULL; PGresult *res; int tuple; int nfields; @@ -1416,7 +1577,7 @@ dumpTableData_insert(Archive *fout, void *dcontext) { appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " "SELECT * FROM ONLY %s", - fmtQualifiedId(fout, + fmtQualifiedId(fout->remoteVersion, tbinfo->dobj.namespace->dobj.name, classname)); } @@ -1424,7 +1585,7 @@ dumpTableData_insert(Archive *fout, void *dcontext) { appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " "SELECT * FROM %s", - fmtQualifiedId(fout, + fmtQualifiedId(fout->remoteVersion, tbinfo->dobj.namespace->dobj.name, classname)); } @@ -1440,34 +1601,57 @@ dumpTableData_insert(Archive *fout, void *dcontext) nfields = PQnfields(res); for (tuple = 0; tuple < PQntuples(res); tuple++) { - archprintf(fout, "INSERT INTO %s ", fmtId(classname)); - if (nfields == 0) + /* + * First time through, we build as much of the INSERT statement as + * possible in "insertStmt", which we can then just print for each + * line. If the table happens to have zero columns then this will + * be a complete statement, otherwise it will end in "VALUES(" and + * be ready to have the row's column values appended. + */ + if (insertStmt == NULL) { + insertStmt = createPQExpBuffer(); + appendPQExpBuffer(insertStmt, "INSERT INTO %s ", + fmtId(classname)); + /* corner case for zero-column table */ - archprintf(fout, "DEFAULT VALUES;\n"); - continue; - } - if (column_inserts) - { - resetPQExpBuffer(q); - appendPQExpBuffer(q, "("); - for (field = 0; field < nfields; field++) + if (nfields == 0) { - if (field > 0) - appendPQExpBuffer(q, ", "); - appendPQExpBufferStr(q, fmtId(PQfname(res, field))); + appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n"); + } + else + { + /* append the list of column names if required */ + if (column_inserts) + { + appendPQExpBufferStr(insertStmt, "("); + for (field = 0; field < nfields; field++) + { + if (field > 0) + appendPQExpBufferStr(insertStmt, ", "); + appendPQExpBufferStr(insertStmt, + fmtId(PQfname(res, field))); + } + appendPQExpBufferStr(insertStmt, ") "); + } + + appendPQExpBufferStr(insertStmt, "VALUES ("); } - appendPQExpBuffer(q, ") "); - archputs(q->data, fout); } - archprintf(fout, "VALUES ("); + + archputs(insertStmt->data, fout); + + /* if it is zero-column table then we're done */ + if (nfields == 0) + continue; + for (field = 0; field < nfields; field++) { if (field > 0) - archprintf(fout, ", "); + archputs(", ", fout); if (PQgetisnull(res, tuple, field)) { - archprintf(fout, "NULL"); + archputs("NULL", fout); continue; } @@ -1496,7 +1680,7 @@ dumpTableData_insert(Archive *fout, void *dcontext) const char *s = PQgetvalue(res, tuple, field); if (strspn(s, "0123456789 +-eE.") == strlen(s)) - archprintf(fout, "%s", s); + archputs(s, fout); else archprintf(fout, "'%s'", s); } @@ -1510,9 +1694,9 @@ dumpTableData_insert(Archive *fout, void *dcontext) case BOOLOID: if (strcmp(PQgetvalue(res, tuple, field), "t") == 0) - archprintf(fout, "true"); + archputs("true", fout); else - archprintf(fout, "false"); + archputs("false", fout); break; default: @@ -1525,7 +1709,7 @@ dumpTableData_insert(Archive *fout, void *dcontext) break; } } - archprintf(fout, ");\n"); + archputs(");\n", fout); } if (PQntuples(res) <= 0) @@ -1536,11 +1720,14 @@ dumpTableData_insert(Archive *fout, void *dcontext) PQclear(res); } - archprintf(fout, "\n\n"); + archputs("\n\n", fout); ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor"); destroyPQExpBuffer(q); + if (insertStmt != NULL) + destroyPQExpBuffer(insertStmt); + return 1; } @@ -1556,6 +1743,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) { TableInfo *tbinfo = tdinfo->tdtable; PQExpBuffer copyBuf = createPQExpBuffer(); + PQExpBuffer clistBuf = createPQExpBuffer(); DataDumperPtr dumpFn; char *copyStmt; @@ -1567,7 +1755,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) appendPQExpBuffer(copyBuf, "COPY %s ", fmtId(tbinfo->dobj.name)); appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n", - fmtCopyColumnList(tbinfo), + fmtCopyColumnList(tbinfo, clistBuf), (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : ""); copyStmt = copyBuf->data; } @@ -1578,15 +1766,64 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) copyStmt = NULL; } + /* + * Note: although the TableDataInfo is a full DumpableObject, we treat its + * dependency on its table as "special" and pass it to ArchiveEntry now. + * See comments for BuildArchiveDependencies. + */ ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId, tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, false, "TABLE DATA", SECTION_DATA, "", "", copyStmt, - tdinfo->dobj.dependencies, tdinfo->dobj.nDeps, + &(tbinfo->dobj.dumpId), 1, dumpFn, tdinfo); destroyPQExpBuffer(copyBuf); + destroyPQExpBuffer(clistBuf); +} + +/* + * refreshMatViewData - + * load or refresh the contents of a single materialized view + * + * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW + * statement. + */ +static void +refreshMatViewData(Archive *fout, TableDataInfo *tdinfo) +{ + TableInfo *tbinfo = tdinfo->tdtable; + PQExpBuffer q; + + /* If the materialized view is not flagged as populated, skip this. */ + if (!tbinfo->relispopulated) + return; + + q = createPQExpBuffer(); + + appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n", + fmtId(tbinfo->dobj.name)); + + ArchiveEntry(fout, + tdinfo->dobj.catId, /* catalog ID */ + tdinfo->dobj.dumpId, /* dump ID */ + tbinfo->dobj.name, /* Name */ + tbinfo->dobj.namespace->dobj.name, /* Namespace */ + NULL, /* Tablespace */ + tbinfo->rolname, /* Owner */ + false, /* with oids */ + "MATERIALIZED VIEW DATA", /* Desc */ + SECTION_POST_DATA, /* Section */ + q->data, /* Create */ + "", /* Del */ + NULL, /* Copy */ + tdinfo->dobj.dependencies, /* Deps */ + tdinfo->dobj.nDeps, /* # Deps */ + NULL, /* Dumper */ + NULL); /* Dumper Arg */ + + destroyPQExpBuffer(q); } /* @@ -1626,9 +1863,6 @@ makeTableDataInfo(TableInfo *tbinfo, bool oids) /* Skip VIEWs (no data to dump) */ if (tbinfo->relkind == RELKIND_VIEW) return; - /* Skip SEQUENCEs (handled elsewhere) */ - if (tbinfo->relkind == RELKIND_SEQUENCE) - return; /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; @@ -1646,7 +1880,10 @@ makeTableDataInfo(TableInfo *tbinfo, bool oids) /* OK, let's dump it */ tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo)); - tdinfo->dobj.objType = DO_TABLE_DATA; + if (tbinfo->relkind == RELKIND_MATVIEW) + tdinfo->dobj.objType = DO_REFRESH_MATVIEW; + else + tdinfo->dobj.objType = DO_TABLE_DATA; /* * Note: use tableoid 0 so that this object won't be mistaken for @@ -1665,6 +1902,117 @@ makeTableDataInfo(TableInfo *tbinfo, bool oids) tbinfo->dataObj = tdinfo; } +/* + * The refresh for a materialized view must be dependent on the refresh for + * any materialized view that this one is dependent on. + * + * This must be called after all the objects are created, but before they are + * sorted. + */ +static void +buildMatViewRefreshDependencies(Archive *fout) +{ + PQExpBuffer query; + PGresult *res; + int ntups, + i; + int i_classid, + i_objid, + i_refobjid; + + /* No Mat Views before 9.3. */ + if (fout->remoteVersion < 90300) + return; + + /* Make sure we are in proper schema */ + selectSourceSchema(fout, "pg_catalog"); + + query = createPQExpBuffer(); + + appendPQExpBufferStr(query, "WITH RECURSIVE w AS " + "( " + "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind " + "FROM pg_depend d1 " + "JOIN pg_class c1 ON c1.oid = d1.objid " + "AND c1.relkind = 'm' " + "JOIN pg_rewrite r1 ON r1.ev_class = d1.objid " + "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass " + "AND d2.objid = r1.oid " + "AND d2.refobjid <> d1.objid " + "JOIN pg_class c2 ON c2.oid = d2.refobjid " + "AND c2.relkind IN ('m','v') " + "WHERE d1.classid = 'pg_class'::regclass " + "UNION " + "SELECT w.objid, d3.refobjid, c3.relkind " + "FROM w " + "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid " + "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass " + "AND d3.objid = r3.oid " + "AND d3.refobjid <> w.refobjid " + "JOIN pg_class c3 ON c3.oid = d3.refobjid " + "AND c3.relkind IN ('m','v') " + ") " + "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid " + "FROM w " + "WHERE refrelkind = 'm'"); + + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + + i_classid = PQfnumber(res, "classid"); + i_objid = PQfnumber(res, "objid"); + i_refobjid = PQfnumber(res, "refobjid"); + + for (i = 0; i < ntups; i++) + { + CatalogId objId; + CatalogId refobjId; + DumpableObject *dobj; + DumpableObject *refdobj; + TableInfo *tbinfo; + TableInfo *reftbinfo; + + objId.tableoid = atooid(PQgetvalue(res, i, i_classid)); + objId.oid = atooid(PQgetvalue(res, i, i_objid)); + refobjId.tableoid = objId.tableoid; + refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid)); + + dobj = findObjectByCatalogId(objId); + if (dobj == NULL) + continue; + + Assert(dobj->objType == DO_TABLE); + tbinfo = (TableInfo *) dobj; + Assert(tbinfo->relkind == RELKIND_MATVIEW); + dobj = (DumpableObject *) tbinfo->dataObj; + if (dobj == NULL) + continue; + Assert(dobj->objType == DO_REFRESH_MATVIEW); + + refdobj = findObjectByCatalogId(refobjId); + if (refdobj == NULL) + continue; + + Assert(refdobj->objType == DO_TABLE); + reftbinfo = (TableInfo *) refdobj; + Assert(reftbinfo->relkind == RELKIND_MATVIEW); + refdobj = (DumpableObject *) reftbinfo->dataObj; + if (refdobj == NULL) + continue; + Assert(refdobj->objType == DO_REFRESH_MATVIEW); + + addObjectDependency(dobj, refdobj->dumpId); + + if (!reftbinfo->relispopulated) + tbinfo->relispopulated = false; + } + + PQclear(res); + + destroyPQExpBuffer(query); +} + /* * getTableDataFKConstraints - * add dump-order dependencies reflecting foreign key constraints @@ -1921,33 +2269,33 @@ dumpDatabase(Archive *fout) fmtId(datname)); if (strlen(encoding) > 0) { - appendPQExpBuffer(creaQry, " ENCODING = "); + appendPQExpBufferStr(creaQry, " ENCODING = "); appendStringLiteralAH(creaQry, encoding, fout); } if (strlen(collate) > 0) { - appendPQExpBuffer(creaQry, " LC_COLLATE = "); + appendPQExpBufferStr(creaQry, " LC_COLLATE = "); appendStringLiteralAH(creaQry, collate, fout); } if (strlen(ctype) > 0) { - appendPQExpBuffer(creaQry, " LC_CTYPE = "); + appendPQExpBufferStr(creaQry, " LC_CTYPE = "); appendStringLiteralAH(creaQry, ctype, fout); } if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0) appendPQExpBuffer(creaQry, " TABLESPACE = %s", fmtId(tablespace)); - appendPQExpBuffer(creaQry, ";\n"); + appendPQExpBufferStr(creaQry, ";\n"); if (binary_upgrade) { - appendPQExpBuffer(creaQry, "\n-- For binary upgrade, set datfrozenxid.\n"); + appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid.\n"); appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n" "SET datfrozenxid = '%u'\n" "WHERE datname = ", frozenxid); appendStringLiteralAH(creaQry, datname, fout); - appendPQExpBuffer(creaQry, ";\n"); + appendPQExpBufferStr(creaQry, ";\n"); } @@ -1997,7 +2345,7 @@ dumpDatabase(Archive *fout) i_relfrozenxid = PQfnumber(lo_res, "relfrozenxid"); - appendPQExpBuffer(loOutQry, "\n-- For binary upgrade, set pg_largeobject.relfrozenxid\n"); + appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, set pg_largeobject.relfrozenxid\n"); appendPQExpBuffer(loOutQry, "UPDATE pg_catalog.pg_class\n" "SET relfrozenxid = '%u'\n" "WHERE oid = %u;\n", @@ -2029,7 +2377,7 @@ dumpDatabase(Archive *fout) i_relfrozenxid = PQfnumber(lo_res, "relfrozenxid"); - appendPQExpBuffer(loOutQry, "\n-- For binary upgrade, set pg_largeobject_metadata.relfrozenxid\n"); + appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, set pg_largeobject_metadata.relfrozenxid\n"); appendPQExpBuffer(loOutQry, "UPDATE pg_catalog.pg_class\n" "SET relfrozenxid = '%u'\n" "WHERE oid = %u;\n", @@ -2068,7 +2416,7 @@ dumpDatabase(Archive *fout) */ appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", fmtId(datname)); appendStringLiteralAH(dbQry, comment, fout); - appendPQExpBuffer(dbQry, ";\n"); + appendPQExpBufferStr(dbQry, ";\n"); ArchiveEntry(fout, dbCatId, createDumpId(), datname, NULL, NULL, dba, false, "COMMENT", SECTION_NONE, @@ -2121,9 +2469,9 @@ dumpEncoding(Archive *AH) if (g_verbose) write_msg(NULL, "saving encoding = %s\n", encname); - appendPQExpBuffer(qry, "SET client_encoding = "); + appendPQExpBufferStr(qry, "SET client_encoding = "); appendStringLiteralAH(qry, encname, AH); - appendPQExpBuffer(qry, ";\n"); + appendPQExpBufferStr(qry, ";\n"); ArchiveEntry(AH, nilCatalogId, createDumpId(), "ENCODING", NULL, NULL, "", @@ -2191,13 +2539,13 @@ getBlobs(Archive *fout) " FROM pg_largeobject_metadata", username_subquery); else if (fout->remoteVersion >= 70100) - appendPQExpBuffer(blobQry, - "SELECT DISTINCT loid, NULL::oid, NULL::oid" - " FROM pg_largeobject"); + appendPQExpBufferStr(blobQry, + "SELECT DISTINCT loid, NULL::oid, NULL::oid" + " FROM pg_largeobject"); else - appendPQExpBuffer(blobQry, - "SELECT oid, NULL::oid, NULL::oid" - " FROM pg_class WHERE relkind = 'l'"); + appendPQExpBufferStr(blobQry, + "SELECT oid, NULL::oid, NULL::oid" + " FROM pg_class WHERE relkind = 'l'"); res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK); @@ -2267,7 +2615,7 @@ dumpBlob(Archive *fout, BlobInfo *binfo) binfo->rolname, false, "BLOB", SECTION_PRE_DATA, cquery->data, dquery->data, NULL, - binfo->dobj.dependencies, binfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* set up tag for comment and/or ACL */ @@ -2372,8 +2720,6 @@ dumpBlobs(Archive *fout, void *arg) PQclear(res); } while (ntups > 0); - PQclear(res); - return 1; } @@ -2386,7 +2732,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout, PGresult *upgrade_res; Oid pg_type_array_oid; - appendPQExpBuffer(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n"); + appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n"); appendPQExpBuffer(upgrade_buffer, "SELECT binary_upgrade.set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n", pg_type_oid); @@ -2404,7 +2750,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout, if (OidIsValid(pg_type_array_oid)) { - appendPQExpBuffer(upgrade_buffer, + appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type array oid\n"); appendPQExpBuffer(upgrade_buffer, "SELECT binary_upgrade.set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n", @@ -2447,7 +2793,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout, Oid pg_type_toast_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "trel"))); - appendPQExpBuffer(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type toast oid\n"); + appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type toast oid\n"); appendPQExpBuffer(upgrade_buffer, "SELECT binary_upgrade.set_next_toast_pg_type_oid('%u'::pg_catalog.oid);\n\n", pg_type_toast_oid); @@ -2469,22 +2815,22 @@ binary_upgrade_set_pg_class_oids(Archive *fout, PQExpBuffer upgrade_query = createPQExpBuffer(); PGresult *upgrade_res; Oid pg_class_reltoastrelid; - Oid pg_class_reltoastidxid; + Oid pg_index_indexrelid; appendPQExpBuffer(upgrade_query, - "SELECT c.reltoastrelid, t.reltoastidxid " + "SELECT c.reltoastrelid, i.indexrelid " "FROM pg_catalog.pg_class c LEFT JOIN " - "pg_catalog.pg_class t ON (c.reltoastrelid = t.oid) " + "pg_catalog.pg_index i ON (c.reltoastrelid = i.indrelid AND i.indisvalid) " "WHERE c.oid = '%u'::pg_catalog.oid;", pg_class_oid); upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data); pg_class_reltoastrelid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "reltoastrelid"))); - pg_class_reltoastidxid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "reltoastidxid"))); + pg_index_indexrelid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "indexrelid"))); - appendPQExpBuffer(upgrade_buffer, - "\n-- For binary upgrade, must preserve pg_class oids\n"); + appendPQExpBufferStr(upgrade_buffer, + "\n-- For binary upgrade, must preserve pg_class oids\n"); if (!is_index) { @@ -2510,7 +2856,7 @@ binary_upgrade_set_pg_class_oids(Archive *fout, /* every toast table has an index */ appendPQExpBuffer(upgrade_buffer, "SELECT binary_upgrade.set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n", - pg_class_reltoastidxid); + pg_index_indexrelid); } } else @@ -2518,7 +2864,7 @@ binary_upgrade_set_pg_class_oids(Archive *fout, "SELECT binary_upgrade.set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n", pg_class_oid); - appendPQExpBuffer(upgrade_buffer, "\n"); + appendPQExpBufferChar(upgrade_buffer, '\n'); PQclear(upgrade_res); destroyPQExpBuffer(upgrade_query); @@ -2553,9 +2899,9 @@ binary_upgrade_extension_member(PQExpBuffer upgrade_buffer, extobj = NULL; } if (extobj == NULL) - exit_horribly(NULL, "could not find parent extension for %s", objlabel); + exit_horribly(NULL, "could not find parent extension for %s\n", objlabel); - appendPQExpBuffer(upgrade_buffer, + appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, handle extension membership the hard way\n"); appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s;\n", fmtId(extobj->name), @@ -2611,8 +2957,7 @@ getNamespaces(Archive *fout, int *numNamespaces) selectDumpableNamespace(&nsinfo[1]); - g_namespaces = nsinfo; - g_numNamespaces = *numNamespaces = 2; + *numNamespaces = 2; return nsinfo; } @@ -2664,8 +3009,7 @@ getNamespaces(Archive *fout, int *numNamespaces) PQclear(res); destroyPQExpBuffer(query); - g_namespaces = nsinfo; - g_numNamespaces = *numNamespaces = ntups; + *numNamespaces = ntups; return nsinfo; } @@ -2676,35 +3020,34 @@ getNamespaces(Archive *fout, int *numNamespaces) * getNamespaces * * NB: for pre-7.3 source database, we use object OID to guess whether it's - * a system object or not. In 7.3 and later there is no guessing. + * a system object or not. In 7.3 and later there is no guessing, and we + * don't use objoid at all. */ static NamespaceInfo * findNamespace(Archive *fout, Oid nsoid, Oid objoid) { - int i; + NamespaceInfo *nsinfo; if (fout->remoteVersion >= 70300) { - for (i = 0; i < g_numNamespaces; i++) - { - NamespaceInfo *nsinfo = &g_namespaces[i]; - - if (nsoid == nsinfo->dobj.catId.oid) - return nsinfo; - } - exit_horribly(NULL, "schema with OID %u does not exist\n", nsoid); + nsinfo = findNamespaceByOid(nsoid); } else { - /* This code depends on the layout set up by getNamespaces. */ + /* This code depends on the dummy objects set up by getNamespaces. */ + Oid i; + if (objoid > g_last_builtin_oid) i = 0; /* user object */ else i = 1; /* system object */ - return &g_namespaces[i]; + nsinfo = findNamespaceByOid(i); } - return NULL; /* keep compiler quiet */ + if (nsinfo == NULL) + exit_horribly(NULL, "schema with OID %u does not exist\n", nsoid); + + return nsinfo; } /* @@ -2745,10 +3088,10 @@ getExtensions(Archive *fout, int *numExtensions) /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); - appendPQExpBuffer(query, "SELECT x.tableoid, x.oid, " - "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition " - "FROM pg_extension x " - "JOIN pg_namespace n ON n.oid = x.extnamespace"); + appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, " + "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition " + "FROM pg_extension x " + "JOIN pg_namespace n ON n.oid = x.extnamespace"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -2813,6 +3156,7 @@ getTypes(Archive *fout, int *numTypes) int i_oid; int i_typname; int i_typnamespace; + int i_typacl; int i_rolname; int i_typinput; int i_typoutput; @@ -2842,10 +3186,25 @@ getTypes(Archive *fout, int *numTypes) /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); - if (fout->remoteVersion >= 80300) + if (fout->remoteVersion >= 90200) + { + appendPQExpBuffer(query, "SELECT tableoid, oid, typname, " + "typnamespace, typacl, " + "(%s typowner) AS rolname, " + "typinput::oid AS typinput, " + "typoutput::oid AS typoutput, typelem, typrelid, " + "CASE WHEN typrelid = 0 THEN ' '::\"char\" " + "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " + "typtype, typisdefined, " + "typname[0] = '_' AND typelem != 0 AND " + "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray " + "FROM pg_type", + username_subquery); + } + else if (fout->remoteVersion >= 80300) { appendPQExpBuffer(query, "SELECT tableoid, oid, typname, " - "typnamespace, " + "typnamespace, '{=U}' AS typacl, " "(%s typowner) AS rolname, " "typinput::oid AS typinput, " "typoutput::oid AS typoutput, typelem, typrelid, " @@ -2860,7 +3219,7 @@ getTypes(Archive *fout, int *numTypes) else if (fout->remoteVersion >= 70300) { appendPQExpBuffer(query, "SELECT tableoid, oid, typname, " - "typnamespace, " + "typnamespace, '{=U}' AS typacl, " "(%s typowner) AS rolname, " "typinput::oid AS typinput, " "typoutput::oid AS typoutput, typelem, typrelid, " @@ -2874,7 +3233,7 @@ getTypes(Archive *fout, int *numTypes) else if (fout->remoteVersion >= 70100) { appendPQExpBuffer(query, "SELECT tableoid, oid, typname, " - "0::oid AS typnamespace, " + "0::oid AS typnamespace, '{=U}' AS typacl, " "(%s typowner) AS rolname, " "typinput::oid AS typinput, " "typoutput::oid AS typoutput, typelem, typrelid, " @@ -2890,7 +3249,7 @@ getTypes(Archive *fout, int *numTypes) appendPQExpBuffer(query, "SELECT " "(SELECT oid FROM pg_class WHERE relname = 'pg_type') AS tableoid, " "oid, typname, " - "0::oid AS typnamespace, " + "0::oid AS typnamespace, '{=U}' AS typacl, " "(%s typowner) AS rolname, " "typinput::oid AS typinput, " "typoutput::oid AS typoutput, typelem, typrelid, " @@ -2912,6 +3271,7 @@ getTypes(Archive *fout, int *numTypes) i_oid = PQfnumber(res, "oid"); i_typname = PQfnumber(res, "typname"); i_typnamespace = PQfnumber(res, "typnamespace"); + i_typacl = PQfnumber(res, "typacl"); i_rolname = PQfnumber(res, "rolname"); i_typinput = PQfnumber(res, "typinput"); i_typoutput = PQfnumber(res, "typoutput"); @@ -2934,6 +3294,7 @@ getTypes(Archive *fout, int *numTypes) atooid(PQgetvalue(res, i, i_typnamespace)), tyinfo[i].dobj.catId.oid); tyinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); + tyinfo[i].typacl = pg_strdup(PQgetvalue(res, i, i_typacl)); tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem)); tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid)); tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind); @@ -2964,7 +3325,7 @@ getTypes(Archive *fout, int *numTypes) /* * If it's a base type, make a DumpableObject representing a shell * definition of the type. We will need to dump that ahead of the I/O - * functions for the type. Similarly, range types need a shell + * functions for the type. Similarly, range types need a shell * definition in case they have a canonicalize function. * * Note: the shell type doesn't have a catId. You might think it @@ -3169,7 +3530,7 @@ getCollations(Archive *fout, int *numCollations) PGresult *res; int ntups; int i; - PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer query; CollInfo *collinfo; int i_tableoid; int i_oid; @@ -3184,6 +3545,8 @@ getCollations(Archive *fout, int *numCollations) return NULL; } + query = createPQExpBuffer(); + /* * find all collations, including builtin collations; we filter out * system-defined collations at dump-out time. @@ -3248,7 +3611,7 @@ getConversions(Archive *fout, int *numConversions) PGresult *res; int ntups; int i; - PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer query; ConvInfo *convinfo; int i_tableoid; int i_oid; @@ -3263,6 +3626,8 @@ getConversions(Archive *fout, int *numConversions) return NULL; } + query = createPQExpBuffer(); + /* * find all conversions, including builtin conversions; we filter out * system-defined conversions at dump-out time. @@ -3353,19 +3718,19 @@ getOpclasses(Archive *fout, int *numOpclasses) } else if (fout->remoteVersion >= 70100) { - appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, " - "0::oid AS opcnamespace, " - "''::name AS rolname " - "FROM pg_opclass"); + appendPQExpBufferStr(query, "SELECT tableoid, oid, opcname, " + "0::oid AS opcnamespace, " + "''::name AS rolname " + "FROM pg_opclass"); } else { - appendPQExpBuffer(query, "SELECT " - "(SELECT oid FROM pg_class WHERE relname = 'pg_opclass') AS tableoid, " - "oid, opcname, " - "0::oid AS opcnamespace, " - "''::name AS rolname " - "FROM pg_opclass"); + appendPQExpBufferStr(query, "SELECT " + "(SELECT oid FROM pg_class WHERE relname = 'pg_opclass') AS tableoid, " + "oid, opcname, " + "0::oid AS opcnamespace, " + "''::name AS rolname " + "FROM pg_opclass"); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -3523,6 +3888,7 @@ getAggregates(Archive *fout, int *numAggs) int i_proargtypes; int i_rolname; int i_aggacl; + int i_proiargs; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -3532,11 +3898,12 @@ getAggregates(Archive *fout, int *numAggs) * rationale behind the filtering logic. */ - if (fout->remoteVersion >= 80200) + if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, " "pronamespace AS aggnamespace, " "pronargs, proargtypes, " + "pg_catalog.pg_get_function_identity_arguments(oid) AS proiargs," "(%s proowner) AS rolname, " "proacl AS aggacl " "FROM pg_proc p " @@ -3546,13 +3913,28 @@ getAggregates(Archive *fout, int *numAggs) "WHERE nspname = 'pg_catalog')", username_subquery); if (binary_upgrade && fout->remoteVersion >= 90100) - appendPQExpBuffer(query, - " OR EXISTS(SELECT 1 FROM pg_depend WHERE " - "classid = 'pg_proc'::regclass AND " - "objid = p.oid AND " - "refclassid = 'pg_extension'::regclass AND " - "deptype = 'e')"); - appendPQExpBuffer(query, ")"); + appendPQExpBufferStr(query, + " OR EXISTS(SELECT 1 FROM pg_depend WHERE " + "classid = 'pg_proc'::regclass AND " + "objid = p.oid AND " + "refclassid = 'pg_extension'::regclass AND " + "deptype = 'e')"); + appendPQExpBufferChar(query, ')'); + } + else if (fout->remoteVersion >= 80200) + { + appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, " + "pronamespace AS aggnamespace, " + "pronargs, proargtypes, " + "NULL::text AS proiargs," + "(%s proowner) AS rolname, " + "proacl AS aggacl " + "FROM pg_proc p " + "WHERE proisagg AND (" + "pronamespace != " + "(SELECT oid FROM pg_namespace " + "WHERE nspname = 'pg_catalog'))", + username_subquery); } else if (fout->remoteVersion >= 70300) { @@ -3560,6 +3942,7 @@ getAggregates(Archive *fout, int *numAggs) "pronamespace AS aggnamespace, " "CASE WHEN proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype THEN 0 ELSE 1 END AS pronargs, " "proargtypes, " + "NULL::text AS proiargs, " "(%s proowner) AS rolname, " "proacl AS aggacl " "FROM pg_proc " @@ -3574,6 +3957,7 @@ getAggregates(Archive *fout, int *numAggs) "0::oid AS aggnamespace, " "CASE WHEN aggbasetype = 0 THEN 0 ELSE 1 END AS pronargs, " "aggbasetype AS proargtypes, " + "NULL::text AS proiargs, " "(%s aggowner) AS rolname, " "'{=X}' AS aggacl " "FROM pg_aggregate " @@ -3589,6 +3973,7 @@ getAggregates(Archive *fout, int *numAggs) "0::oid AS aggnamespace, " "CASE WHEN aggbasetype = 0 THEN 0 ELSE 1 END AS pronargs, " "aggbasetype AS proargtypes, " + "NULL::text AS proiargs, " "(%s aggowner) AS rolname, " "'{=X}' AS aggacl " "FROM pg_aggregate " @@ -3612,6 +3997,7 @@ getAggregates(Archive *fout, int *numAggs) i_proargtypes = PQfnumber(res, "proargtypes"); i_rolname = PQfnumber(res, "rolname"); i_aggacl = PQfnumber(res, "aggacl"); + i_proiargs = PQfnumber(res, "proiargs"); for (i = 0; i < ntups; i++) { @@ -3631,6 +4017,7 @@ getAggregates(Archive *fout, int *numAggs) agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */ agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */ agginfo[i].aggfn.proacl = pg_strdup(PQgetvalue(res, i, i_aggacl)); + agginfo[i].aggfn.proiargs = pg_strdup(PQgetvalue(res, i, i_proiargs)); agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs)); if (agginfo[i].aggfn.nargs == 0) agginfo[i].aggfn.argtypes = NULL; @@ -3682,6 +4069,7 @@ getFuncs(Archive *fout, int *numFuncs) int i_proargtypes; int i_prorettype; int i_proacl; + int i_proiargs; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -3702,12 +4090,13 @@ getFuncs(Archive *fout, int *numFuncs) * doesn't have; otherwise we might not get creation ordering correct. */ - if (fout->remoteVersion >= 70300) + if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT tableoid, oid, proname, prolang, " "pronargs, proargtypes, prorettype, proacl, " "pronamespace, " + "pg_catalog.pg_get_function_identity_arguments(oid) AS proiargs," "(%s proowner) AS rolname " "FROM pg_proc p " "WHERE NOT proisagg AND (" @@ -3716,18 +4105,33 @@ getFuncs(Archive *fout, int *numFuncs) "WHERE nspname = 'pg_catalog')", username_subquery); if (fout->remoteVersion >= 90200) - appendPQExpBuffer(query, - "\n AND NOT EXISTS (SELECT 1 FROM pg_depend " - "WHERE classid = 'pg_proc'::regclass AND " - "objid = p.oid AND deptype = 'i')"); + appendPQExpBufferStr(query, + "\n AND NOT EXISTS (SELECT 1 FROM pg_depend " + "WHERE classid = 'pg_proc'::regclass AND " + "objid = p.oid AND deptype = 'i')"); if (binary_upgrade && fout->remoteVersion >= 90100) - appendPQExpBuffer(query, - "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE " - "classid = 'pg_proc'::regclass AND " - "objid = p.oid AND " - "refclassid = 'pg_extension'::regclass AND " - "deptype = 'e')"); - appendPQExpBuffer(query, ")"); + appendPQExpBufferStr(query, + "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE " + "classid = 'pg_proc'::regclass AND " + "objid = p.oid AND " + "refclassid = 'pg_extension'::regclass AND " + "deptype = 'e')"); + appendPQExpBufferChar(query, ')'); + } + else if (fout->remoteVersion >= 70300) + { + appendPQExpBuffer(query, + "SELECT tableoid, oid, proname, prolang, " + "pronargs, proargtypes, prorettype, proacl, " + "pronamespace, " + "NULL::text AS proiargs," + "(%s proowner) AS rolname " + "FROM pg_proc p " + "WHERE NOT proisagg AND (" + "pronamespace != " + "(SELECT oid FROM pg_namespace " + "WHERE nspname = 'pg_catalog'))", + username_subquery); } else if (fout->remoteVersion >= 70100) { @@ -3736,6 +4140,7 @@ getFuncs(Archive *fout, int *numFuncs) "pronargs, proargtypes, prorettype, " "'{=X}' AS proacl, " "0::oid AS pronamespace, " + "NULL::text AS proiargs," "(%s proowner) AS rolname " "FROM pg_proc " "WHERE pg_proc.oid > '%u'::oid", @@ -3752,6 +4157,7 @@ getFuncs(Archive *fout, int *numFuncs) "pronargs, proargtypes, prorettype, " "'{=X}' AS proacl, " "0::oid AS pronamespace, " + "NULL::text AS proiargs," "(%s proowner) AS rolname " "FROM pg_proc " "where pg_proc.oid > '%u'::oid", @@ -3765,7 +4171,7 @@ getFuncs(Archive *fout, int *numFuncs) *numFuncs = ntups; - finfo = (FuncInfo *) pg_calloc(ntups, sizeof(FuncInfo)); + finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo)); i_tableoid = PQfnumber(res, "tableoid"); i_oid = PQfnumber(res, "oid"); @@ -3777,6 +4183,7 @@ getFuncs(Archive *fout, int *numFuncs) i_proargtypes = PQfnumber(res, "proargtypes"); i_prorettype = PQfnumber(res, "prorettype"); i_proacl = PQfnumber(res, "proacl"); + i_proiargs = PQfnumber(res, "proiargs"); for (i = 0; i < ntups; i++) { @@ -3792,6 +4199,7 @@ getFuncs(Archive *fout, int *numFuncs) finfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname)); finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang)); finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype)); + finfo[i].proiargs = pg_strdup(PQgetvalue(res, i, i_proiargs)); finfo[i].proacl = pg_strdup(PQgetvalue(res, i, i_proacl)); finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs)); if (finfo[i].nargs == 0) @@ -3850,24 +4258,28 @@ getTables(Archive *fout, int *numTables) int i_toastoid; int i_toastfrozenxid; int i_relpersistence; + int i_relispopulated; + int i_relreplident; int i_owning_tab; int i_owning_col; int i_reltablespace; int i_reloptions; + int i_checkoption; int i_toastreloptions; int i_reloftype; + int i_relpages; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); /* - * Find all the tables (including views and sequences). + * Find all the tables and table-like objects. * * We include system catalogs, so that we can work if a user table is * defined to inherit from a system catalog (pretty weird, but...) * - * We ignore tables that are not type 'r' (ordinary relation), 'S' - * (sequence), 'v' (view), or 'c' (composite type). + * We ignore relations that are not ordinary tables, sequences, views, + * materialized views, composite types, or foreign tables. * * Composite-type table entries won't be dumped as such, but we have to * make a DumpableObject for them so that we can track dependencies of the @@ -3880,7 +4292,85 @@ getTables(Archive *fout, int *numTables) * we cannot correctly identify inherited columns, owned sequences, etc. */ - if (fout->remoteVersion >= 90100) + if (fout->remoteVersion >= 90400) + { + /* + * Left join to pick up dependency info linking sequences to their + * owning column, if any (note this dependency is AUTO as of 8.2) + */ + appendPQExpBuffer(query, + "SELECT c.tableoid, c.oid, c.relname, " + "c.relacl, c.relkind, c.relnamespace, " + "(%s c.relowner) AS rolname, " + "c.relchecks, c.relhastriggers, " + "c.relhasindex, c.relhasrules, c.relhasoids, " + "c.relfrozenxid, tc.oid AS toid, " + "tc.relfrozenxid AS tfrozenxid, " + "c.relpersistence, c.relispopulated, " + "c.relreplident, c.relpages, " + "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " + "d.refobjid AS owning_tab, " + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, " + "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text " + "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, " + "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " + "d.classid = c.tableoid AND d.objid = c.oid AND " + "d.objsubid = 0 AND " + "d.refclassid = c.tableoid AND d.deptype = 'a') " + "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "ORDER BY c.oid", + username_subquery, + RELKIND_SEQUENCE, + RELKIND_RELATION, RELKIND_SEQUENCE, + RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + } + else if (fout->remoteVersion >= 90300) + { + /* + * Left join to pick up dependency info linking sequences to their + * owning column, if any (note this dependency is AUTO as of 8.2) + */ + appendPQExpBuffer(query, + "SELECT c.tableoid, c.oid, c.relname, " + "c.relacl, c.relkind, c.relnamespace, " + "(%s c.relowner) AS rolname, " + "c.relchecks, c.relhastriggers, " + "c.relhasindex, c.relhasrules, c.relhasoids, " + "c.relfrozenxid, tc.oid AS toid, " + "tc.relfrozenxid AS tfrozenxid, " + "c.relpersistence, c.relispopulated, " + "'d' AS relreplident, c.relpages, " + "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " + "d.refobjid AS owning_tab, " + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, " + "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text " + "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, " + "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " + "d.classid = c.tableoid AND d.objid = c.oid AND " + "d.objsubid = 0 AND " + "d.refclassid = c.tableoid AND d.deptype = 'a') " + "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "ORDER BY c.oid", + username_subquery, + RELKIND_SEQUENCE, + RELKIND_RELATION, RELKIND_SEQUENCE, + RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + } + else if (fout->remoteVersion >= 90100) { /* * Left join to pick up dependency info linking sequences to their @@ -3894,7 +4384,8 @@ getTables(Archive *fout, int *numTables) "c.relhasindex, c.relhasrules, c.relhasoids, " "c.relfrozenxid, tc.oid AS toid, " "tc.relfrozenxid AS tfrozenxid, " - "c.relpersistence, " + "c.relpersistence, 't' as relispopulated, " + "'d' AS relreplident, c.relpages, " "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " "d.refobjid AS owning_tab, " "d.refobjsubid AS owning_col, " @@ -3908,13 +4399,13 @@ getTables(Archive *fout, int *numTables) "d.objsubid = 0 AND " "d.refclassid = c.tableoid AND d.deptype = 'a') " "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", username_subquery, RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); } else if (fout->remoteVersion >= 90000) { @@ -3930,7 +4421,8 @@ getTables(Archive *fout, int *numTables) "c.relhasindex, c.relhasrules, c.relhasoids, " "c.relfrozenxid, tc.oid AS toid, " "tc.relfrozenxid AS tfrozenxid, " - "'p' AS relpersistence, " + "'p' AS relpersistence, 't' as relispopulated, " + "'d' AS relreplident, c.relpages, " "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " "d.refobjid AS owning_tab, " "d.refobjsubid AS owning_col, " @@ -3965,7 +4457,8 @@ getTables(Archive *fout, int *numTables) "c.relhasindex, c.relhasrules, c.relhasoids, " "c.relfrozenxid, tc.oid AS toid, " "tc.relfrozenxid AS tfrozenxid, " - "'p' AS relpersistence, " + "'p' AS relpersistence, 't' as relispopulated, " + "'d' AS relreplident, c.relpages, " "NULL AS reloftype, " "d.refobjid AS owning_tab, " "d.refobjsubid AS owning_col, " @@ -3996,11 +4489,12 @@ getTables(Archive *fout, int *numTables) "SELECT c.tableoid, c.oid, c.relname, " "c.relacl, c.relkind, c.relnamespace, " "(%s c.relowner) AS rolname, " - "c.relchecks, (c.reltriggers <> 0) AS relhastriggers, " + "c.relchecks, (c.reltriggers <> 0) AS relhastriggers, " "c.relhasindex, c.relhasrules, c.relhasoids, " "c.relfrozenxid, tc.oid AS toid, " "tc.relfrozenxid AS tfrozenxid, " - "'p' AS relpersistence, " + "'p' AS relpersistence, 't' as relispopulated, " + "'d' AS relreplident, c.relpages, " "NULL AS reloftype, " "d.refobjid AS owning_tab, " "d.refobjsubid AS owning_col, " @@ -4036,7 +4530,8 @@ getTables(Archive *fout, int *numTables) "0 AS relfrozenxid, " "0 AS toid, " "0 AS tfrozenxid, " - "'p' AS relpersistence, " + "'p' AS relpersistence, 't' as relispopulated, " + "'d' AS relreplident, relpages, " "NULL AS reloftype, " "d.refobjid AS owning_tab, " "d.refobjsubid AS owning_col, " @@ -4071,7 +4566,8 @@ getTables(Archive *fout, int *numTables) "0 AS relfrozenxid, " "0 AS toid, " "0 AS tfrozenxid, " - "'p' AS relpersistence, " + "'p' AS relpersistence, 't' as relispopulated, " + "'d' AS relreplident, relpages, " "NULL AS reloftype, " "d.refobjid AS owning_tab, " "d.refobjsubid AS owning_col, " @@ -4102,7 +4598,8 @@ getTables(Archive *fout, int *numTables) "0 AS relfrozenxid, " "0 AS toid, " "0 AS tfrozenxid, " - "'p' AS relpersistence, " + "'p' AS relpersistence, 't' as relispopulated, " + "'d' AS relreplident, relpages, " "NULL AS reloftype, " "NULL::oid AS owning_tab, " "NULL::int4 AS owning_col, " @@ -4128,7 +4625,8 @@ getTables(Archive *fout, int *numTables) "0 AS relfrozenxid, " "0 AS toid, " "0 AS tfrozenxid, " - "'p' AS relpersistence, " + "'p' AS relpersistence, 't' as relispopulated, " + "'d' AS relreplident, relpages, " "NULL AS reloftype, " "NULL::oid AS owning_tab, " "NULL::int4 AS owning_col, " @@ -4164,7 +4662,8 @@ getTables(Archive *fout, int *numTables) "0 as relfrozenxid, " "0 AS toid, " "0 AS tfrozenxid, " - "'p' AS relpersistence, " + "'p' AS relpersistence, 't' as relispopulated, " + "'d' AS relreplident, 0 AS relpages, " "NULL AS reloftype, " "NULL::oid AS owning_tab, " "NULL::int4 AS owning_col, " @@ -4194,7 +4693,7 @@ getTables(Archive *fout, int *numTables) * only one, because we don't yet know which tables might be inheritance * ancestors of the target table. */ - tblinfo = (TableInfo *) pg_calloc(ntups, sizeof(TableInfo)); + tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo)); i_reltableoid = PQfnumber(res, "tableoid"); i_reloid = PQfnumber(res, "oid"); @@ -4212,10 +4711,14 @@ getTables(Archive *fout, int *numTables) i_toastoid = PQfnumber(res, "toid"); i_toastfrozenxid = PQfnumber(res, "tfrozenxid"); i_relpersistence = PQfnumber(res, "relpersistence"); + i_relispopulated = PQfnumber(res, "relispopulated"); + i_relreplident = PQfnumber(res, "relreplident"); + i_relpages = PQfnumber(res, "relpages"); i_owning_tab = PQfnumber(res, "owning_tab"); i_owning_col = PQfnumber(res, "owning_col"); i_reltablespace = PQfnumber(res, "reltablespace"); i_reloptions = PQfnumber(res, "reloptions"); + i_checkoption = PQfnumber(res, "checkoption"); i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); @@ -4229,7 +4732,7 @@ getTables(Archive *fout, int *numTables) * applied to other things too. */ resetPQExpBuffer(query); - appendPQExpBuffer(query, "SET statement_timeout = "); + appendPQExpBufferStr(query, "SET statement_timeout = "); appendStringLiteralConn(query, lockWaitTimeout, GetConnection(fout)); ExecuteSqlStatement(fout, query->data); } @@ -4253,6 +4756,9 @@ getTables(Archive *fout, int *numTables) tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0); tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0); tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0); + tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0); + tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident)); + tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages)); tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid)); tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid)); tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid)); @@ -4273,6 +4779,10 @@ getTables(Archive *fout, int *numTables) } tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace)); tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions)); + if (i_checkoption == -1 || PQgetisnull(res, i, i_checkoption)) + tblinfo[i].checkoption = NULL; + else + tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); /* other fields were zeroed above */ @@ -4286,6 +4796,8 @@ getTables(Archive *fout, int *numTables) selectDumpableTable(&tblinfo[i]); tblinfo[i].interesting = tblinfo[i].dobj.dump; + tblinfo[i].postponed_def = false; /* might get set during sort */ + /* * Read-lock target tables to make sure they aren't DROPPED or altered * in schema before we get around to dumping them. @@ -4302,9 +4814,9 @@ getTables(Archive *fout, int *numTables) resetPQExpBuffer(query); appendPQExpBuffer(query, "LOCK TABLE %s IN ACCESS SHARE MODE", - fmtQualifiedId(fout, + fmtQualifiedId(fout->remoteVersion, tblinfo[i].dobj.namespace->dobj.name, - tblinfo[i].dobj.name)); + tblinfo[i].dobj.name)); ExecuteSqlStatement(fout, query->data); } @@ -4321,38 +4833,43 @@ getTables(Archive *fout, int *numTables) PQclear(res); + destroyPQExpBuffer(query); + + return tblinfo; +} + +/* + * getOwnedSeqs + * identify owned sequences and mark them as dumpable if owning table is + * + * We used to do this in getTables(), but it's better to do it after the + * index used by findTableByOid() has been set up. + */ +void +getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables) +{ + int i; + /* * Force sequences that are "owned" by table columns to be dumped whenever * their owning table is being dumped. */ - for (i = 0; i < ntups; i++) + for (i = 0; i < numTables; i++) { TableInfo *seqinfo = &tblinfo[i]; - int j; + TableInfo *owning_tab; if (!OidIsValid(seqinfo->owning_tab)) continue; /* not an owned sequence */ if (seqinfo->dobj.dump) continue; /* no need to search */ - - /* can't use findTableByOid yet, unfortunately */ - for (j = 0; j < ntups; j++) + owning_tab = findTableByOid(seqinfo->owning_tab); + if (owning_tab && owning_tab->dobj.dump) { - if (tblinfo[j].dobj.catId.oid == seqinfo->owning_tab) - { - if (tblinfo[j].dobj.dump) - { - seqinfo->interesting = true; - seqinfo->dobj.dump = true; - } - break; - } + seqinfo->interesting = true; + seqinfo->dobj.dump = true; } } - - destroyPQExpBuffer(query); - - return tblinfo; } /* @@ -4379,7 +4896,7 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBuffer(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -4428,6 +4945,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) i_indnkeys, i_indkey, i_indisclustered, + i_indisreplident, i_contype, i_conname, i_condeferrable, @@ -4436,15 +4954,19 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) i_conoid, i_condef, i_tablespace, - i_options; + i_options, + i_relpages; int ntups; for (i = 0; i < numTables; i++) { TableInfo *tbinfo = &tblinfo[i]; - /* Only plain tables have indexes */ - if (tbinfo->relkind != RELKIND_RELATION || !tbinfo->hasindex) + /* Only plain tables and materialized views have indexes. */ + if (tbinfo->relkind != RELKIND_RELATION && + tbinfo->relkind != RELKIND_MATVIEW) + continue; + if (!tbinfo->hasindex) continue; /* Ignore indexes of tables not to be dumped */ @@ -4470,14 +4992,50 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) * is not. */ resetPQExpBuffer(query); - if (fout->remoteVersion >= 90000) + if (fout->remoteVersion >= 90400) + { + /* + * the test on indisready is necessary in 9.2, and harmless in + * earlier/later versions + */ + appendPQExpBuffer(query, + "SELECT t.tableoid, t.oid, " + "t.relname AS indexname, " + "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " + "t.relnatts AS indnkeys, " + "i.indkey, i.indisclustered, " + "i.indisreplident, t.relpages, " + "c.contype, c.conname, " + "c.condeferrable, c.condeferred, " + "c.tableoid AS contableoid, " + "c.oid AS conoid, " + "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, " + "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, " + "array_to_string(t.reloptions, ', ') AS options " + "FROM pg_catalog.pg_index i " + "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) " + "LEFT JOIN pg_catalog.pg_constraint c " + "ON (i.indrelid = c.conrelid AND " + "i.indexrelid = c.conindid AND " + "c.contype IN ('p','u','x')) " + "WHERE i.indrelid = '%u'::pg_catalog.oid " + "AND i.indisvalid AND i.indisready " + "ORDER BY indexname", + tbinfo->dobj.catId.oid); + } + else if (fout->remoteVersion >= 90000) { + /* + * the test on indisready is necessary in 9.2, and harmless in + * earlier/later versions + */ appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, " "t.relname AS indexname, " "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " "t.relnatts AS indnkeys, " "i.indkey, i.indisclustered, " + "false AS indisreplident, t.relpages, " "c.contype, c.conname, " "c.condeferrable, c.condeferred, " "c.tableoid AS contableoid, " @@ -4492,6 +5050,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) "i.indexrelid = c.conindid AND " "c.contype IN ('p','u','x')) " "WHERE i.indrelid = '%u'::pg_catalog.oid " + "AND i.indisvalid AND i.indisready " "ORDER BY indexname", tbinfo->dobj.catId.oid); } @@ -4503,6 +5062,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " "t.relnatts AS indnkeys, " "i.indkey, i.indisclustered, " + "false AS indisreplident, t.relpages, " "c.contype, c.conname, " "c.condeferrable, c.condeferred, " "c.tableoid AS contableoid, " @@ -4520,6 +5080,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) "ON (d.refclassid = c.tableoid " "AND d.refobjid = c.oid) " "WHERE i.indrelid = '%u'::pg_catalog.oid " + "AND i.indisvalid " "ORDER BY indexname", tbinfo->dobj.catId.oid); } @@ -4531,6 +5092,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " "t.relnatts AS indnkeys, " "i.indkey, i.indisclustered, " + "false AS indisreplident, t.relpages, " "c.contype, c.conname, " "c.condeferrable, c.condeferred, " "c.tableoid AS contableoid, " @@ -4559,6 +5121,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, " "t.relnatts AS indnkeys, " "i.indkey, i.indisclustered, " + "false AS indisreplident, t.relpages, " "c.contype, c.conname, " "c.condeferrable, c.condeferred, " "c.tableoid AS contableoid, " @@ -4587,6 +5150,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) "pg_get_indexdef(i.indexrelid) AS indexdef, " "t.relnatts AS indnkeys, " "i.indkey, false AS indisclustered, " + "false AS indisreplident, t.relpages, " "CASE WHEN i.indisprimary THEN 'p'::char " "ELSE '0'::char END AS contype, " "t.relname AS conname, " @@ -4613,6 +5177,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) "pg_get_indexdef(i.indexrelid) AS indexdef, " "t.relnatts AS indnkeys, " "i.indkey, false AS indisclustered, " + "false AS indisreplident, t.relpages, " "CASE WHEN i.indisprimary THEN 'p'::char " "ELSE '0'::char END AS contype, " "t.relname AS conname, " @@ -4641,6 +5206,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) i_indnkeys = PQfnumber(res, "indnkeys"); i_indkey = PQfnumber(res, "indkey"); i_indisclustered = PQfnumber(res, "indisclustered"); + i_indisreplident = PQfnumber(res, "indisreplident"); + i_relpages = PQfnumber(res, "relpages"); i_contype = PQfnumber(res, "contype"); i_conname = PQfnumber(res, "conname"); i_condeferrable = PQfnumber(res, "condeferrable"); @@ -4683,6 +5250,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) parseOidArray(PQgetvalue(res, j, i_indkey), indxinfo[j].indkeys, INDEX_MAX_KEYS); indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't'); + indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't'); + indxinfo[j].relpages = atoi(PQgetvalue(res, j, i_relpages)); contype = *(PQgetvalue(res, j, i_contype)); if (contype == 'p' || contype == 'u' || contype == 'x') @@ -4898,7 +5467,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo) for (i = 0; i < ntups; i++) { - bool validated = PQgetvalue(res, i, 4)[0] == 't'; + bool validated = PQgetvalue(res, i, 4)[0] == 't'; constrinfo[i].dobj.objType = DO_CONSTRAINT; constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); @@ -4920,7 +5489,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo) /* * Make the domain depend on the constraint, ensuring it won't be - * output till any constraint dependencies are OK. If the constraint + * output till any constraint dependencies are OK. If the constraint * has not been validated, it's going to be dumped after the domain * anyway, so this doesn't matter. */ @@ -4961,31 +5530,31 @@ getRules(Archive *fout, int *numRules) if (fout->remoteVersion >= 80300) { - appendPQExpBuffer(query, "SELECT " - "tableoid, oid, rulename, " - "ev_class AS ruletable, ev_type, is_instead, " - "ev_enabled " - "FROM pg_rewrite " - "ORDER BY oid"); + appendPQExpBufferStr(query, "SELECT " + "tableoid, oid, rulename, " + "ev_class AS ruletable, ev_type, is_instead, " + "ev_enabled " + "FROM pg_rewrite " + "ORDER BY oid"); } else if (fout->remoteVersion >= 70100) { - appendPQExpBuffer(query, "SELECT " - "tableoid, oid, rulename, " - "ev_class AS ruletable, ev_type, is_instead, " - "'O'::char AS ev_enabled " - "FROM pg_rewrite " - "ORDER BY oid"); + appendPQExpBufferStr(query, "SELECT " + "tableoid, oid, rulename, " + "ev_class AS ruletable, ev_type, is_instead, " + "'O'::char AS ev_enabled " + "FROM pg_rewrite " + "ORDER BY oid"); } else { - appendPQExpBuffer(query, "SELECT " - "(SELECT oid FROM pg_class WHERE relname = 'pg_rewrite') AS tableoid, " - "oid, rulename, " - "ev_class AS ruletable, ev_type, is_instead, " - "'O'::char AS ev_enabled " - "FROM pg_rewrite " - "ORDER BY oid"); + appendPQExpBufferStr(query, "SELECT " + "(SELECT oid FROM pg_class WHERE relname = 'pg_rewrite') AS tableoid, " + "oid, rulename, " + "ev_class AS ruletable, ev_type, is_instead, " + "'O'::char AS ev_enabled " + "FROM pg_rewrite " + "ORDER BY oid"); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -5026,12 +5595,14 @@ getRules(Archive *fout, int *numRules) if (ruleinfo[i].ruletable) { /* - * If the table is a view, force its ON SELECT rule to be sorted - * before the view itself --- this ensures that any dependencies - * for the rule affect the table's positioning. Other rules are - * forced to appear after their table. + * If the table is a view or materialized view, force its ON + * SELECT rule to be sorted before the view itself --- this + * ensures that any dependencies for the rule affect the table's + * positioning. Other rules are forced to appear after their + * table. */ - if (ruleinfo[i].ruletable->relkind == RELKIND_VIEW && + if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW || + ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) && ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead) { addObjectDependency(&ruleinfo[i].ruletable->dobj, @@ -5048,6 +5619,16 @@ getRules(Archive *fout, int *numRules) } else ruleinfo[i].separate = true; + + /* + * If we're forced to break a dependency loop by dumping a view as a + * table and separate _RETURN rule, we'll move the view's reloptions + * to the rule. (This is necessary because tables and views have + * different valid reloptions, so we can't apply the options until the + * backend knows it's a view.) Otherwise the rule's reloptions stay + * NULL. + */ + ruleinfo[i].reloptions = NULL; } PQclear(res); @@ -5282,6 +5863,89 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables) destroyPQExpBuffer(query); } +/* + * getEventTriggers + * get information about event triggers + */ +EventTriggerInfo * +getEventTriggers(Archive *fout, int *numEventTriggers) +{ + int i; + PQExpBuffer query; + PGresult *res; + EventTriggerInfo *evtinfo; + int i_tableoid, + i_oid, + i_evtname, + i_evtevent, + i_evtowner, + i_evttags, + i_evtfname, + i_evtenabled; + int ntups; + + /* Before 9.3, there are no event triggers */ + if (fout->remoteVersion < 90300) + { + *numEventTriggers = 0; + return NULL; + } + + query = createPQExpBuffer(); + + /* Make sure we are in proper schema */ + selectSourceSchema(fout, "pg_catalog"); + + appendPQExpBuffer(query, + "SELECT e.tableoid, e.oid, evtname, evtenabled, " + "evtevent, (%s evtowner) AS evtowner, " + "array_to_string(array(" + "select quote_literal(x) " + " from unnest(evttags) as t(x)), ', ') as evttags, " + "e.evtfoid::regproc as evtfname " + "FROM pg_event_trigger e " + "ORDER BY e.oid", + username_subquery); + + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + + *numEventTriggers = ntups; + + evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo)); + + i_tableoid = PQfnumber(res, "tableoid"); + i_oid = PQfnumber(res, "oid"); + i_evtname = PQfnumber(res, "evtname"); + i_evtevent = PQfnumber(res, "evtevent"); + i_evtowner = PQfnumber(res, "evtowner"); + i_evttags = PQfnumber(res, "evttags"); + i_evtfname = PQfnumber(res, "evtfname"); + i_evtenabled = PQfnumber(res, "evtenabled"); + + for (i = 0; i < ntups; i++) + { + evtinfo[i].dobj.objType = DO_EVENT_TRIGGER; + evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); + evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&evtinfo[i].dobj); + evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname)); + evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname)); + evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent)); + evtinfo[i].evtowner = pg_strdup(PQgetvalue(res, i, i_evtowner)); + evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags)); + evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname)); + evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled)); + } + + PQclear(res); + + destroyPQExpBuffer(query); + + return evtinfo; +} + /* * getProcLangs * get basic information about every procedural language in the system @@ -5359,17 +6023,17 @@ getProcLangs(Archive *fout, int *numProcLangs) else if (fout->remoteVersion >= 70100) { /* No clear notion of an owner at all before 7.4 ... */ - appendPQExpBuffer(query, "SELECT tableoid, oid, * FROM pg_language " - "WHERE lanispl " - "ORDER BY oid"); + appendPQExpBufferStr(query, "SELECT tableoid, oid, * FROM pg_language " + "WHERE lanispl " + "ORDER BY oid"); } else { - appendPQExpBuffer(query, "SELECT " - "(SELECT oid FROM pg_class WHERE relname = 'pg_language') AS tableoid, " - "oid, * FROM pg_language " - "WHERE lanispl " - "ORDER BY oid"); + appendPQExpBufferStr(query, "SELECT " + "(SELECT oid FROM pg_class WHERE relname = 'pg_language') AS tableoid, " + "oid, * FROM pg_language " + "WHERE lanispl " + "ORDER BY oid"); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -5467,29 +6131,29 @@ getCasts(Archive *fout, int *numCasts) if (fout->remoteVersion >= 80400) { - appendPQExpBuffer(query, "SELECT tableoid, oid, " - "castsource, casttarget, castfunc, castcontext, " - "castmethod " - "FROM pg_cast ORDER BY 3,4"); + appendPQExpBufferStr(query, "SELECT tableoid, oid, " + "castsource, casttarget, castfunc, castcontext, " + "castmethod " + "FROM pg_cast ORDER BY 3,4"); } else if (fout->remoteVersion >= 70300) { - appendPQExpBuffer(query, "SELECT tableoid, oid, " - "castsource, casttarget, castfunc, castcontext, " + appendPQExpBufferStr(query, "SELECT tableoid, oid, " + "castsource, casttarget, castfunc, castcontext, " "CASE WHEN castfunc = 0 THEN 'b' ELSE 'f' END AS castmethod " - "FROM pg_cast ORDER BY 3,4"); + "FROM pg_cast ORDER BY 3,4"); } else { - appendPQExpBuffer(query, "SELECT 0 AS tableoid, p.oid, " - "t1.oid AS castsource, t2.oid AS casttarget, " - "p.oid AS castfunc, 'e' AS castcontext, " - "'f' AS castmethod " - "FROM pg_type t1, pg_type t2, pg_proc p " - "WHERE p.pronargs = 1 AND " - "p.proargtypes[0] = t1.oid AND " - "p.prorettype = t2.oid AND p.proname = t2.typname " - "ORDER BY 3,4"); + appendPQExpBufferStr(query, "SELECT 0 AS tableoid, p.oid, " + "t1.oid AS castsource, t2.oid AS casttarget, " + "p.oid AS castfunc, 'e' AS castcontext, " + "'f' AS castmethod " + "FROM pg_type t1, pg_type t2, pg_proc p " + "WHERE p.pronargs = 1 AND " + "p.proargtypes[0] = t1.oid AND " + "p.prorettype = t2.oid AND p.proname = t2.typname " + "ORDER BY 3,4"); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -5644,11 +6308,11 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " "array_to_string(a.attoptions, ', ') AS attoptions, " "CASE WHEN a.attcollation <> t.typcollation " - "THEN a.attcollation ELSE 0 END AS attcollation, " + "THEN a.attcollation ELSE 0 END AS attcollation, " "pg_catalog.array_to_string(ARRAY(" "SELECT pg_catalog.quote_ident(option_name) || " "' ' || pg_catalog.quote_literal(option_value) " - "FROM pg_catalog.pg_options_to_table(attfdwoptions) " + "FROM pg_catalog.pg_options_to_table(attfdwoptions) " "ORDER BY option_name" "), E',\n ') AS attfdwoptions " "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " @@ -5673,7 +6337,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " "array_to_string(a.attoptions, ', ') AS attoptions, " "CASE WHEN a.attcollation <> t.typcollation " - "THEN a.attcollation ELSE 0 END AS attcollation, " + "THEN a.attcollation ELSE 0 END AS attcollation, " "NULL AS attfdwoptions " "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " "ON a.atttypid = t.oid " @@ -5917,8 +6581,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) /* * Defaults on a VIEW must always be dumped as separate ALTER * TABLE commands. Defaults on regular tables are dumped as - * part of the CREATE TABLE if possible, which it won't be - * if the column is not going to be emitted explicitly. + * part of the CREATE TABLE if possible, which it won't be if + * the column is not going to be emitted explicitly. */ if (tbinfo->relkind == RELKIND_VIEW) { @@ -5938,6 +6602,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) else { attrdefs[j].separate = false; + /* * Mark the default as needing to appear before the table, * so that any dependencies it has must be emitted before @@ -5969,13 +6634,12 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) if (fout->remoteVersion >= 90200) { /* - * conisonly and convalidated are new in 9.2 (actually, the latter - * is there in 9.1, but it wasn't ever false for check constraints - * until 9.2). + * convalidated is new in 9.2 (actually, it is there in 9.1, + * but it wasn't ever false for check constraints until 9.2). */ appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " "pg_catalog.pg_get_constraintdef(oid) AS consrc, " - "conislocal, convalidated, conisonly " + "conislocal, convalidated " "FROM pg_catalog.pg_constraint " "WHERE conrelid = '%u'::pg_catalog.oid " " AND contype = 'c' " @@ -5984,10 +6648,10 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) } else if (fout->remoteVersion >= 80400) { + /* conislocal is new in 8.4 */ appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " "pg_catalog.pg_get_constraintdef(oid) AS consrc, " - "conislocal, true AS convalidated, " - "false as conisonly " + "conislocal, true AS convalidated " "FROM pg_catalog.pg_constraint " "WHERE conrelid = '%u'::pg_catalog.oid " " AND contype = 'c' " @@ -5998,8 +6662,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) { appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " "pg_catalog.pg_get_constraintdef(oid) AS consrc, " - "true AS conislocal, true AS convalidated, " - "false as conisonly " + "true AS conislocal, true AS convalidated " "FROM pg_catalog.pg_constraint " "WHERE conrelid = '%u'::pg_catalog.oid " " AND contype = 'c' " @@ -6011,8 +6674,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) /* no pg_get_constraintdef, must use consrc */ appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " "'CHECK (' || consrc || ')' AS consrc, " - "true AS conislocal, true AS convalidated, " - "false as conisonly " + "true AS conislocal, true AS convalidated " "FROM pg_catalog.pg_constraint " "WHERE conrelid = '%u'::pg_catalog.oid " " AND contype = 'c' " @@ -6025,8 +6687,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) appendPQExpBuffer(q, "SELECT tableoid, 0 AS oid, " "rcname AS conname, " "'CHECK (' || rcsrc || ')' AS consrc, " - "true AS conislocal, true AS convalidated, " - "false as conisonly " + "true AS conislocal, true AS convalidated " "FROM pg_relcheck " "WHERE rcrelid = '%u'::oid " "ORDER BY rcname", @@ -6037,8 +6698,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) appendPQExpBuffer(q, "SELECT tableoid, oid, " "rcname AS conname, " "'CHECK (' || rcsrc || ')' AS consrc, " - "true AS conislocal, true AS convalidated, " - "false as conisonly " + "true AS conislocal, true AS convalidated " "FROM pg_relcheck " "WHERE rcrelid = '%u'::oid " "ORDER BY rcname", @@ -6051,8 +6711,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) "(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, " "oid, rcname AS conname, " "'CHECK (' || rcsrc || ')' AS consrc, " - "true AS conislocal, true AS convalidated, " - "false as conisonly " + "true AS conislocal, true AS convalidated " "FROM pg_relcheck " "WHERE rcrelid = '%u'::oid " "ORDER BY rcname", @@ -6076,8 +6735,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) for (j = 0; j < numConstrs; j++) { - bool validated = PQgetvalue(res, j, 5)[0] == 't'; - bool isonly = PQgetvalue(res, j, 6)[0] == 't'; + bool validated = PQgetvalue(res, j, 5)[0] == 't'; constrs[j].dobj.objType = DO_CONSTRAINT; constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0)); @@ -6094,14 +6752,13 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) constrs[j].condeferrable = false; constrs[j].condeferred = false; constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't'); - constrs[j].conisonly = isonly; + /* * An unvalidated constraint needs to be dumped separately, so * that potentially-violating existing data is loaded before - * the constraint. An ONLY constraint needs to be dumped - * separately too. + * the constraint. */ - constrs[j].separate = !validated || isonly; + constrs[j].separate = !validated; constrs[j].dobj.dump = tbinfo->dobj.dump; @@ -6109,10 +6766,10 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) * Mark the constraint as needing to appear before the table * --- this is so that any other dependencies of the * constraint will be emitted before we try to create the - * table. If the constraint is to be dumped separately, it will be - * dumped after data is loaded anyway, so don't do it. (There's - * an automatic dependency in the opposite direction anyway, so - * don't need to add one manually here.) + * table. If the constraint is to be dumped separately, it + * will be dumped after data is loaded anyway, so don't do it. + * (There's an automatic dependency in the opposite direction + * anyway, so don't need to add one manually here.) */ if (!constrs[j].separate) addObjectDependency(&tbinfo->dobj, @@ -6167,7 +6824,7 @@ getTSParsers(Archive *fout, int *numTSParsers) PGresult *res; int ntups; int i; - PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer query; TSParserInfo *prsinfo; int i_tableoid; int i_oid; @@ -6186,6 +6843,8 @@ getTSParsers(Archive *fout, int *numTSParsers) return NULL; } + query = createPQExpBuffer(); + /* * find all text search objects, including builtin ones; we filter out * system-defined objects at dump-out time. @@ -6194,10 +6853,10 @@ getTSParsers(Archive *fout, int *numTSParsers) /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); - appendPQExpBuffer(query, "SELECT tableoid, oid, prsname, prsnamespace, " - "prsstart::oid, prstoken::oid, " - "prsend::oid, prsheadline::oid, prslextype::oid " - "FROM pg_ts_parser"); + appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, " + "prsstart::oid, prstoken::oid, " + "prsend::oid, prsheadline::oid, prslextype::oid " + "FROM pg_ts_parser"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -6257,7 +6916,7 @@ getTSDictionaries(Archive *fout, int *numTSDicts) PGresult *res; int ntups; int i; - PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer query; TSDictInfo *dictinfo; int i_tableoid; int i_oid; @@ -6274,6 +6933,8 @@ getTSDictionaries(Archive *fout, int *numTSDicts) return NULL; } + query = createPQExpBuffer(); + /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -6340,7 +7001,7 @@ getTSTemplates(Archive *fout, int *numTSTemplates) PGresult *res; int ntups; int i; - PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer query; TSTemplateInfo *tmplinfo; int i_tableoid; int i_oid; @@ -6356,12 +7017,14 @@ getTSTemplates(Archive *fout, int *numTSTemplates) return NULL; } + query = createPQExpBuffer(); + /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); - appendPQExpBuffer(query, "SELECT tableoid, oid, tmplname, " - "tmplnamespace, tmplinit::oid, tmpllexize::oid " - "FROM pg_ts_template"); + appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, " + "tmplnamespace, tmplinit::oid, tmpllexize::oid " + "FROM pg_ts_template"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -6415,7 +7078,7 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) PGresult *res; int ntups; int i; - PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer query; TSConfigInfo *cfginfo; int i_tableoid; int i_oid; @@ -6431,6 +7094,8 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) return NULL; } + query = createPQExpBuffer(); + /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -6491,7 +7156,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) PGresult *res; int ntups; int i; - PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer query; FdwInfo *fdwinfo; int i_tableoid; int i_oid; @@ -6509,6 +7174,8 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) return NULL; } + query = createPQExpBuffer(); + /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -6597,7 +7264,7 @@ getForeignServers(Archive *fout, int *numForeignServers) PGresult *res; int ntups; int i; - PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer query; ForeignServerInfo *srvinfo; int i_tableoid; int i_oid; @@ -6616,8 +7283,10 @@ getForeignServers(Archive *fout, int *numForeignServers) return NULL; } + query = createPQExpBuffer(); + /* Make sure we are in proper schema */ - selectSourceSchema(fout,"pg_catalog"); + selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, " "(%s srvowner) AS rolname, " @@ -6819,7 +7488,7 @@ dumpComment(Archive *fout, const char *target, appendPQExpBuffer(query, "COMMENT ON %s IS ", target); appendStringLiteralAH(query, comments->descr, fout); - appendPQExpBuffer(query, ";\n"); + appendPQExpBufferStr(query, ";\n"); /* * We mark comments as SECTION_NONE because they really belong in the @@ -6883,7 +7552,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, resetPQExpBuffer(query); appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data); appendStringLiteralAH(query, descr, fout); - appendPQExpBuffer(query, ";\n"); + appendPQExpBufferStr(query, ";\n"); ArchiveEntry(fout, nilCatalogId, createDumpId(), target->data, @@ -6899,13 +7568,12 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, resetPQExpBuffer(target); appendPQExpBuffer(target, "COLUMN %s.", fmtId(tbinfo->dobj.name)); - appendPQExpBuffer(target, "%s", - fmtId(tbinfo->attnames[objsubid - 1])); + appendPQExpBufferStr(target, fmtId(tbinfo->attnames[objsubid - 1])); resetPQExpBuffer(query); appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data); appendStringLiteralAH(query, descr, fout); - appendPQExpBuffer(query, ";\n"); + appendPQExpBufferStr(query, ";\n"); ArchiveEntry(fout, nilCatalogId, createDumpId(), target->data, @@ -7046,22 +7714,22 @@ collectComments(Archive *fout, CommentItem **items) if (fout->remoteVersion >= 70300) { - appendPQExpBuffer(query, "SELECT description, classoid, objoid, objsubid " - "FROM pg_catalog.pg_description " - "ORDER BY classoid, objoid, objsubid"); + appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid " + "FROM pg_catalog.pg_description " + "ORDER BY classoid, objoid, objsubid"); } else if (fout->remoteVersion >= 70200) { - appendPQExpBuffer(query, "SELECT description, classoid, objoid, objsubid " - "FROM pg_description " - "ORDER BY classoid, objoid, objsubid"); + appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid " + "FROM pg_description " + "ORDER BY classoid, objoid, objsubid"); } else { /* Note: this will fail to find attribute comments in pre-7.2... */ - appendPQExpBuffer(query, "SELECT description, 0 AS classoid, objoid, 0 AS objsubid " - "FROM pg_description " - "ORDER BY objoid"); + appendPQExpBufferStr(query, "SELECT description, 0 AS classoid, objoid, 0 AS objsubid " + "FROM pg_description " + "ORDER BY objoid"); } res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -7101,28 +7769,6 @@ collectComments(Archive *fout, CommentItem **items) static void dumpDumpableObject(Archive *fout, DumpableObject *dobj) { - - bool skip = false; - - switch (dobj->objType) - { - case DO_INDEX: - case DO_TRIGGER: - case DO_CONSTRAINT: - case DO_FK_CONSTRAINT: - case DO_RULE: - skip = !(dumpSections & DUMP_POST_DATA); - break; - case DO_TABLE_DATA: - skip = !(dumpSections & DUMP_DATA); - break; - default: - skip = !(dumpSections & DUMP_PRE_DATA); - } - - if (skip) - return; - switch (dobj->objType) { case DO_NAMESPACE: @@ -7167,12 +7813,18 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) case DO_INDEX: dumpIndex(fout, (IndxInfo *) dobj); break; + case DO_REFRESH_MATVIEW: + refreshMatViewData(fout, (TableDataInfo *) dobj); + break; case DO_RULE: dumpRule(fout, (RuleInfo *) dobj); break; case DO_TRIGGER: dumpTrigger(fout, (TriggerInfo *) dobj); break; + case DO_EVENT_TRIGGER: + dumpEventTrigger(fout, (EventTriggerInfo *) dobj); + break; case DO_CONSTRAINT: dumpConstraint(fout, (ConstraintInfo *) dobj); break; @@ -7186,7 +7838,10 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) dumpCast(fout, (CastInfo *) dobj); break; case DO_TABLE_DATA: - dumpTableData(fout, (TableDataInfo *) dobj); + if (((TableDataInfo *) dobj)->tdtable->relkind == RELKIND_SEQUENCE) + dumpSequenceData(fout, (TableDataInfo *) dobj); + else + dumpTableData(fout, (TableDataInfo *) dobj); break; case DO_DUMMY_TYPE: /* table rowtypes and array types are never dumped separately */ @@ -7220,9 +7875,13 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) dobj->name, NULL, NULL, "", false, "BLOBS", SECTION_DATA, "", "", NULL, - dobj->dependencies, dobj->nDeps, + NULL, 0, dumpBlobs, NULL); break; + case DO_PRE_DATA_BOUNDARY: + case DO_POST_DATA_BOUNDARY: + /* never dumped, nothing to do */ + break; } } @@ -7267,7 +7926,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) nspinfo->rolname, false, "SCHEMA", SECTION_PRE_DATA, q->data, delq->data, NULL, - nspinfo->dobj.dependencies, nspinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Schema Comments and Security Labels */ @@ -7333,16 +7992,26 @@ dumpExtension(Archive *fout, ExtensionInfo *extinfo) int i; int n; - appendPQExpBuffer(q, "-- For binary upgrade, create an empty extension and insert objects into it\n"); - appendPQExpBuffer(q, + appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n"); + + /* + * We unconditionally create the extension, so we must drop it if it + * exists. This could happen if the user deleted 'plpgsql' and then + * readded it, causing its oid to be greater than FirstNormalObjectId. + * The FirstNormalObjectId test was kept to avoid repeatedly dropping + * and recreating extensions like 'plpgsql'. + */ + appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname); + + appendPQExpBufferStr(q, "SELECT binary_upgrade.create_empty_extension("); appendStringLiteralAH(q, extinfo->dobj.name, fout); - appendPQExpBuffer(q, ", "); + appendPQExpBufferStr(q, ", "); appendStringLiteralAH(q, extinfo->namespace, fout); - appendPQExpBuffer(q, ", "); + appendPQExpBufferStr(q, ", "); appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false"); appendStringLiteralAH(q, extinfo->extversion, fout); - appendPQExpBuffer(q, ", "); + appendPQExpBufferStr(q, ", "); /* * Note that we're pushing extconfig (an OID array) back into @@ -7352,14 +8021,14 @@ dumpExtension(Archive *fout, ExtensionInfo *extinfo) if (strlen(extinfo->extconfig) > 2) appendStringLiteralAH(q, extinfo->extconfig, fout); else - appendPQExpBuffer(q, "NULL"); - appendPQExpBuffer(q, ", "); + appendPQExpBufferStr(q, "NULL"); + appendPQExpBufferStr(q, ", "); if (strlen(extinfo->extcondition) > 2) appendStringLiteralAH(q, extinfo->extcondition, fout); else - appendPQExpBuffer(q, "NULL"); - appendPQExpBuffer(q, ", "); - appendPQExpBuffer(q, "ARRAY["); + appendPQExpBufferStr(q, "NULL"); + appendPQExpBufferStr(q, ", "); + appendPQExpBufferStr(q, "ARRAY["); n = 0; for (i = 0; i < extinfo->dobj.nDeps; i++) { @@ -7369,12 +8038,12 @@ dumpExtension(Archive *fout, ExtensionInfo *extinfo) if (extobj && extobj->objType == DO_EXTENSION) { if (n++ > 0) - appendPQExpBuffer(q, ","); + appendPQExpBufferChar(q, ','); appendStringLiteralAH(q, extobj->name, fout); } } - appendPQExpBuffer(q, "]::pg_catalog.text[]"); - appendPQExpBuffer(q, ");\n"); + appendPQExpBufferStr(q, "]::pg_catalog.text[]"); + appendPQExpBufferStr(q, ");\n"); } appendPQExpBuffer(labelq, "EXTENSION %s", qextname); @@ -7385,7 +8054,7 @@ dumpExtension(Archive *fout, ExtensionInfo *extinfo) "", false, "EXTENSION", SECTION_PRE_DATA, q->data, delq->data, NULL, - extinfo->dobj.dependencies, extinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Extension Comments and Security Labels */ @@ -7445,6 +8114,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) int num, i; Oid enum_oid; + char *qtypname; char *label; /* Set proper schema search path */ @@ -7467,6 +8137,8 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) num = PQntuples(res); + qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); + /* * DROP must be fully qualified in case same name appears in pg_catalog. * CASCADE shouldn't be required here as for normal types since the I/O @@ -7475,14 +8147,14 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(delq, "DROP TYPE %s.", fmtId(tyinfo->dobj.namespace->dobj.name)); appendPQExpBuffer(delq, "%s;\n", - fmtId(tyinfo->dobj.name)); + qtypname); if (binary_upgrade) binary_upgrade_set_type_oids_by_type_oid(fout, q, tyinfo->dobj.catId.oid); appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (", - fmtId(tyinfo->dobj.name)); + qtypname); if (!binary_upgrade) { @@ -7491,13 +8163,13 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) { label = PQgetvalue(res, i, PQfnumber(res, "enumlabel")); if (i > 0) - appendPQExpBuffer(q, ","); - appendPQExpBuffer(q, "\n "); + appendPQExpBufferChar(q, ','); + appendPQExpBufferStr(q, "\n "); appendStringLiteralAH(q, label, fout); } } - appendPQExpBuffer(q, "\n);\n"); + appendPQExpBufferStr(q, "\n);\n"); if (binary_upgrade) { @@ -7508,20 +8180,20 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) label = PQgetvalue(res, i, PQfnumber(res, "enumlabel")); if (i == 0) - appendPQExpBuffer(q, "\n-- For binary upgrade, must preserve pg_enum oids\n"); + appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n"); appendPQExpBuffer(q, "SELECT binary_upgrade.set_next_pg_enum_oid('%u'::pg_catalog.oid);\n", enum_oid); appendPQExpBuffer(q, "ALTER TYPE %s.", fmtId(tyinfo->dobj.namespace->dobj.name)); appendPQExpBuffer(q, "%s ADD VALUE ", - fmtId(tyinfo->dobj.name)); + qtypname); appendStringLiteralAH(q, label, fout); - appendPQExpBuffer(q, ";\n\n"); + appendPQExpBufferStr(q, ";\n\n"); } } - appendPQExpBuffer(labelq, "TYPE %s", fmtId(tyinfo->dobj.name)); + appendPQExpBuffer(labelq, "TYPE %s", qtypname); if (binary_upgrade) binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data); @@ -7533,7 +8205,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) tyinfo->rolname, false, "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, - tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Type Comments and Security Labels */ @@ -7544,6 +8216,11 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE", + qtypname, NULL, tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + tyinfo->rolname, tyinfo->typacl); + PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); @@ -7564,6 +8241,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) PQExpBuffer query = createPQExpBuffer(); PGresult *res; Oid collationOid; + char *qtypname; char *procname; /* @@ -7573,7 +8251,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) selectSourceSchema(fout, tyinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, " + "SELECT pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, " "opc.opcname AS opcname, " "(SELECT nspname FROM pg_catalog.pg_namespace nsp " " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, " @@ -7589,6 +8267,8 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) res = ExecuteSqlQueryForSingleRow(fout, query->data); + qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); + /* * DROP must be fully qualified in case same name appears in pg_catalog. * CASCADE shouldn't be required here as for normal types since the I/O @@ -7597,14 +8277,14 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(delq, "DROP TYPE %s.", fmtId(tyinfo->dobj.namespace->dobj.name)); appendPQExpBuffer(delq, "%s;\n", - fmtId(tyinfo->dobj.name)); + qtypname); if (binary_upgrade) binary_upgrade_set_type_oids_by_type_oid(fout, q, tyinfo->dobj.catId.oid); appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (", - fmtId(tyinfo->dobj.name)); + qtypname); appendPQExpBuffer(q, "\n subtype = %s", PQgetvalue(res, 0, PQfnumber(res, "rngsubtype"))); @@ -7612,13 +8292,13 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) /* print subtype_opclass only if not default for subtype */ if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't') { - char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname")); - char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp")); + char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname")); + char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp")); /* always schema-qualify, don't try to be smart */ appendPQExpBuffer(q, ",\n subtype_opclass = %s.", fmtId(nspname)); - appendPQExpBuffer(q, "%s", fmtId(opcname)); + appendPQExpBufferStr(q, fmtId(opcname)); } collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation"))); @@ -7631,8 +8311,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) /* always schema-qualify, don't try to be smart */ appendPQExpBuffer(q, ",\n collation = %s.", fmtId(coll->dobj.namespace->dobj.name)); - appendPQExpBuffer(q, "%s", - fmtId(coll->dobj.name)); + appendPQExpBufferStr(q, fmtId(coll->dobj.name)); } } @@ -7644,9 +8323,9 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) if (strcmp(procname, "-") != 0) appendPQExpBuffer(q, ",\n subtype_diff = %s", procname); - appendPQExpBuffer(q, "\n);\n"); + appendPQExpBufferStr(q, "\n);\n"); - appendPQExpBuffer(labelq, "TYPE %s", fmtId(tyinfo->dobj.name)); + appendPQExpBuffer(labelq, "TYPE %s", qtypname); if (binary_upgrade) binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data); @@ -7658,7 +8337,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) tyinfo->rolname, false, "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, - tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Type Comments and Security Labels */ @@ -7669,6 +8348,11 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE", + qtypname, NULL, tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + tyinfo->rolname, tyinfo->typacl); + PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); @@ -7688,6 +8372,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) PQExpBuffer labelq = createPQExpBuffer(); PQExpBuffer query = createPQExpBuffer(); PGresult *res; + char *qtypname; char *typlen; char *typinput; char *typoutput; @@ -7920,6 +8605,8 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) else typdefault = NULL; + qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); + /* * DROP must be fully qualified in case same name appears in pg_catalog. * The reason we include CASCADE is that the circular dependency between @@ -7929,7 +8616,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(delq, "DROP TYPE %s.", fmtId(tyinfo->dobj.namespace->dobj.name)); appendPQExpBuffer(delq, "%s CASCADE;\n", - fmtId(tyinfo->dobj.name)); + qtypname); /* We might already have a shell type, but setting pg_type_oid is harmless */ if (binary_upgrade) @@ -7939,7 +8626,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(q, "CREATE TYPE %s (\n" " INTERNALLENGTH = %s", - fmtId(tyinfo->dobj.name), + qtypname, (strcmp(typlen, "-1") == 0) ? "variable" : typlen); if (fout->remoteVersion >= 70300) @@ -7968,11 +8655,11 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) } if (strcmp(typcollatable, "t") == 0) - appendPQExpBuffer(q, ",\n COLLATABLE = true"); + appendPQExpBufferStr(q, ",\n COLLATABLE = true"); if (typdefault != NULL) { - appendPQExpBuffer(q, ",\n DEFAULT = "); + appendPQExpBufferStr(q, ",\n DEFAULT = "); if (typdefault_is_literal) appendStringLiteralAH(q, typdefault, fout); else @@ -7992,43 +8679,43 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) if (strcmp(typcategory, "U") != 0) { - appendPQExpBuffer(q, ",\n CATEGORY = "); + appendPQExpBufferStr(q, ",\n CATEGORY = "); appendStringLiteralAH(q, typcategory, fout); } if (strcmp(typispreferred, "t") == 0) - appendPQExpBuffer(q, ",\n PREFERRED = true"); + appendPQExpBufferStr(q, ",\n PREFERRED = true"); if (typdelim && strcmp(typdelim, ",") != 0) { - appendPQExpBuffer(q, ",\n DELIMITER = "); + appendPQExpBufferStr(q, ",\n DELIMITER = "); appendStringLiteralAH(q, typdelim, fout); } if (strcmp(typalign, "c") == 0) - appendPQExpBuffer(q, ",\n ALIGNMENT = char"); + appendPQExpBufferStr(q, ",\n ALIGNMENT = char"); else if (strcmp(typalign, "s") == 0) - appendPQExpBuffer(q, ",\n ALIGNMENT = int2"); + appendPQExpBufferStr(q, ",\n ALIGNMENT = int2"); else if (strcmp(typalign, "i") == 0) - appendPQExpBuffer(q, ",\n ALIGNMENT = int4"); + appendPQExpBufferStr(q, ",\n ALIGNMENT = int4"); else if (strcmp(typalign, "d") == 0) - appendPQExpBuffer(q, ",\n ALIGNMENT = double"); + appendPQExpBufferStr(q, ",\n ALIGNMENT = double"); if (strcmp(typstorage, "p") == 0) - appendPQExpBuffer(q, ",\n STORAGE = plain"); + appendPQExpBufferStr(q, ",\n STORAGE = plain"); else if (strcmp(typstorage, "e") == 0) - appendPQExpBuffer(q, ",\n STORAGE = external"); + appendPQExpBufferStr(q, ",\n STORAGE = external"); else if (strcmp(typstorage, "x") == 0) - appendPQExpBuffer(q, ",\n STORAGE = extended"); + appendPQExpBufferStr(q, ",\n STORAGE = extended"); else if (strcmp(typstorage, "m") == 0) - appendPQExpBuffer(q, ",\n STORAGE = main"); + appendPQExpBufferStr(q, ",\n STORAGE = main"); if (strcmp(typbyval, "t") == 0) - appendPQExpBuffer(q, ",\n PASSEDBYVALUE"); + appendPQExpBufferStr(q, ",\n PASSEDBYVALUE"); - appendPQExpBuffer(q, "\n);\n"); + appendPQExpBufferStr(q, "\n);\n"); - appendPQExpBuffer(labelq, "TYPE %s", fmtId(tyinfo->dobj.name)); + appendPQExpBuffer(labelq, "TYPE %s", qtypname); if (binary_upgrade) binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data); @@ -8040,7 +8727,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) tyinfo->rolname, false, "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, - tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Type Comments and Security Labels */ @@ -8051,6 +8738,11 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE", + qtypname, NULL, tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + tyinfo->rolname, tyinfo->typacl); + PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); @@ -8071,6 +8763,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) PQExpBuffer query = createPQExpBuffer(); PGresult *res; int i; + char *qtypname; char *typnotnull; char *typdefn; char *typdefault; @@ -8126,9 +8819,11 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) binary_upgrade_set_type_oids_by_type_oid(fout, q, tyinfo->dobj.catId.oid); + qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); + appendPQExpBuffer(q, "CREATE DOMAIN %s AS %s", - fmtId(tyinfo->dobj.name), + qtypname, typdefn); /* Print collation only if different from base type's collation */ @@ -8142,17 +8837,16 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) /* always schema-qualify, don't try to be smart */ appendPQExpBuffer(q, " COLLATE %s.", fmtId(coll->dobj.namespace->dobj.name)); - appendPQExpBuffer(q, "%s", - fmtId(coll->dobj.name)); + appendPQExpBufferStr(q, fmtId(coll->dobj.name)); } } if (typnotnull[0] == 't') - appendPQExpBuffer(q, " NOT NULL"); + appendPQExpBufferStr(q, " NOT NULL"); if (typdefault != NULL) { - appendPQExpBuffer(q, " DEFAULT "); + appendPQExpBufferStr(q, " DEFAULT "); if (typdefault_is_literal) appendStringLiteralAH(q, typdefault, fout); else @@ -8173,7 +8867,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) fmtId(domcheck->dobj.name), domcheck->condef); } - appendPQExpBuffer(q, ";\n"); + appendPQExpBufferStr(q, ";\n"); /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -8181,9 +8875,9 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(delq, "DROP DOMAIN %s.", fmtId(tyinfo->dobj.namespace->dobj.name)); appendPQExpBuffer(delq, "%s;\n", - fmtId(tyinfo->dobj.name)); + qtypname); - appendPQExpBuffer(labelq, "DOMAIN %s", fmtId(tyinfo->dobj.name)); + appendPQExpBuffer(labelq, "DOMAIN %s", qtypname); if (binary_upgrade) binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data); @@ -8195,7 +8889,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) tyinfo->rolname, false, "DOMAIN", SECTION_PRE_DATA, q->data, delq->data, NULL, - tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Domain Comments and Security Labels */ @@ -8206,6 +8900,11 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE", + qtypname, NULL, tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + tyinfo->rolname, tyinfo->typacl); + destroyPQExpBuffer(q); destroyPQExpBuffer(delq); destroyPQExpBuffer(labelq); @@ -8226,6 +8925,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) PQExpBuffer labelq = createPQExpBuffer(); PQExpBuffer query = createPQExpBuffer(); PGresult *res; + char *qtypname; int ntups; int i_attname; int i_atttypdefn; @@ -8303,8 +9003,10 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) binary_upgrade_set_pg_class_oids(fout, q, typrelid, false); } + qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); + appendPQExpBuffer(q, "CREATE TYPE %s AS (", - fmtId(tyinfo->dobj.name)); + qtypname); actual_atts = 0; for (i = 0; i < ntups; i++) @@ -8328,8 +9030,8 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) /* Format properly if not first attr */ if (actual_atts++ > 0) - appendPQExpBuffer(q, ","); - appendPQExpBuffer(q, "\n\t"); + appendPQExpBufferChar(q, ','); + appendPQExpBufferStr(q, "\n\t"); if (!attisdropped) { @@ -8346,8 +9048,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) /* always schema-qualify, don't try to be smart */ appendPQExpBuffer(q, " COLLATE %s.", fmtId(coll->dobj.namespace->dobj.name)); - appendPQExpBuffer(q, "%s", - fmtId(coll->dobj.name)); + appendPQExpBufferStr(q, fmtId(coll->dobj.name)); } } } @@ -8362,24 +9063,24 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname)); /* stash separately for insertion after the CREATE TYPE */ - appendPQExpBuffer(dropped, + appendPQExpBufferStr(dropped, "\n-- For binary upgrade, recreate dropped column.\n"); appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n" "SET attlen = %s, " "attalign = '%s', attbyval = false\n" "WHERE attname = ", attlen, attalign); appendStringLiteralAH(dropped, attname, fout); - appendPQExpBuffer(dropped, "\n AND attrelid = "); - appendStringLiteralAH(dropped, fmtId(tyinfo->dobj.name), fout); - appendPQExpBuffer(dropped, "::pg_catalog.regclass;\n"); + appendPQExpBufferStr(dropped, "\n AND attrelid = "); + appendStringLiteralAH(dropped, qtypname, fout); + appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n"); appendPQExpBuffer(dropped, "ALTER TYPE %s ", - fmtId(tyinfo->dobj.name)); + qtypname); appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n", fmtId(attname)); } } - appendPQExpBuffer(q, "\n);\n"); + appendPQExpBufferStr(q, "\n);\n"); appendPQExpBufferStr(q, dropped->data); /* @@ -8388,9 +9089,9 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(delq, "DROP TYPE %s.", fmtId(tyinfo->dobj.namespace->dobj.name)); appendPQExpBuffer(delq, "%s;\n", - fmtId(tyinfo->dobj.name)); + qtypname); - appendPQExpBuffer(labelq, "TYPE %s", fmtId(tyinfo->dobj.name)); + appendPQExpBuffer(labelq, "TYPE %s", qtypname); if (binary_upgrade) binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data); @@ -8402,7 +9103,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) tyinfo->rolname, false, "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, - tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, + NULL, 0, NULL, NULL); @@ -8414,6 +9115,11 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE", + qtypname, NULL, tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + tyinfo->rolname, tyinfo->typacl); + PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(dropped); @@ -8507,13 +9213,12 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo) resetPQExpBuffer(target); appendPQExpBuffer(target, "COLUMN %s.", fmtId(tyinfo->dobj.name)); - appendPQExpBuffer(target, "%s", - fmtId(attname)); + appendPQExpBufferStr(target, fmtId(attname)); resetPQExpBuffer(query); appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data); appendStringLiteralAH(query, descr, fout); - appendPQExpBuffer(query, ";\n"); + appendPQExpBufferStr(query, ";\n"); ArchiveEntry(fout, nilCatalogId, createDumpId(), target->data, @@ -8574,7 +9279,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo) stinfo->baseType->rolname, false, "SHELL TYPE", SECTION_PRE_DATA, q->data, "", NULL, - stinfo->dobj.dependencies, stinfo->dobj.nDeps, + NULL, 0, NULL, NULL); destroyPQExpBuffer(q); @@ -8703,23 +9408,21 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) fmtId(funcInfo->dobj.name)); if (OidIsValid(plang->laninline)) { - appendPQExpBuffer(defqry, " INLINE "); + appendPQExpBufferStr(defqry, " INLINE "); /* Cope with possibility that inline is in different schema */ if (inlineInfo->dobj.namespace != funcInfo->dobj.namespace) appendPQExpBuffer(defqry, "%s.", fmtId(inlineInfo->dobj.namespace->dobj.name)); - appendPQExpBuffer(defqry, "%s", - fmtId(inlineInfo->dobj.name)); + appendPQExpBufferStr(defqry, fmtId(inlineInfo->dobj.name)); } if (OidIsValid(plang->lanvalidator)) { - appendPQExpBuffer(defqry, " VALIDATOR "); + appendPQExpBufferStr(defqry, " VALIDATOR "); /* Cope with possibility that validator is in different schema */ if (validatorInfo->dobj.namespace != funcInfo->dobj.namespace) appendPQExpBuffer(defqry, "%s.", fmtId(validatorInfo->dobj.namespace->dobj.name)); - appendPQExpBuffer(defqry, "%s", - fmtId(validatorInfo->dobj.name)); + appendPQExpBufferStr(defqry, fmtId(validatorInfo->dobj.name)); } } else @@ -8736,7 +9439,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s", qlanname); } - appendPQExpBuffer(defqry, ";\n"); + appendPQExpBufferStr(defqry, ";\n"); appendPQExpBuffer(labelq, "LANGUAGE %s", qlanname); @@ -8748,7 +9451,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) lanschema, NULL, plang->lanowner, false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA, defqry->data, delqry->data, NULL, - plang->dobj.dependencies, plang->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Proc Lang Comments and Security Labels */ @@ -8776,15 +9479,20 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) * format_function_arguments: generate function name and argument list * * This is used when we can rely on pg_get_function_arguments to format - * the argument list. + * the argument list. Note, however, that pg_get_function_arguments + * does not special-case zero-argument aggregates. */ static char * -format_function_arguments(FuncInfo *finfo, char *funcargs) +format_function_arguments(FuncInfo *finfo, char *funcargs, bool is_agg) { PQExpBufferData fn; initPQExpBuffer(&fn); - appendPQExpBuffer(&fn, "%s(%s)", fmtId(finfo->dobj.name), funcargs); + appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name)); + if (is_agg && finfo->nargs == 0) + appendPQExpBufferStr(&fn, "(*)"); + else + appendPQExpBuffer(&fn, "(%s)", funcargs); return fn.data; } @@ -8855,7 +9563,7 @@ format_function_arguments_old(Archive *fout, typname); free(typname); } - appendPQExpBuffer(&fn, ")"); + appendPQExpBufferChar(&fn, ')'); return fn.data; } @@ -8884,15 +9592,15 @@ format_function_signature(Archive *fout, FuncInfo *finfo, bool honor_quotes) { char *typname; + if (j > 0) + appendPQExpBufferStr(&fn, ", "); + typname = getFormattedTypeName(fout, finfo->argtypes[j], zeroAsOpaque); - - appendPQExpBuffer(&fn, "%s%s", - (j > 0) ? ", " : "", - typname); + appendPQExpBufferStr(&fn, typname); free(typname); } - appendPQExpBuffer(&fn, ")"); + appendPQExpBufferChar(&fn, ')'); return fn.data; } @@ -8911,7 +9619,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) PQExpBuffer asPart; PGresult *res; char *funcsig; /* identity signature */ - char *funcfullsig; /* full signature */ + char *funcfullsig = NULL; /* full signature */ char *funcsig_tag; char *proretset; char *prosrc; @@ -9124,11 +9832,11 @@ dumpFunc(Archive *fout, FuncInfo *finfo) */ if (probin[0] != '\0' && strcmp(probin, "-") != 0) { - appendPQExpBuffer(asPart, "AS "); + appendPQExpBufferStr(asPart, "AS "); appendStringLiteralAH(asPart, probin, fout); if (strcmp(prosrc, "-") != 0) { - appendPQExpBuffer(asPart, ", "); + appendPQExpBufferStr(asPart, ", "); /* * where we have bin, use dollar quoting if allowed and src @@ -9145,7 +9853,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) { if (strcmp(prosrc, "-") != 0) { - appendPQExpBuffer(asPart, "AS "); + appendPQExpBufferStr(asPart, "AS "); /* with no bin, dollar quote src unconditionally if allowed */ if (disable_dollar_quoting) appendStringLiteralAH(asPart, prosrc, fout); @@ -9215,17 +9923,14 @@ dumpFunc(Archive *fout, FuncInfo *finfo) if (funcargs) { /* 8.4 or later; we rely on server-side code for most of the work */ - funcfullsig = format_function_arguments(finfo, funcargs); - funcsig = format_function_arguments(finfo, funciargs); + funcfullsig = format_function_arguments(finfo, funcargs, false); + funcsig = format_function_arguments(finfo, funciargs, false); } else - { /* pre-8.4, do it ourselves */ funcsig = format_function_arguments_old(fout, finfo, nallargs, allargtypes, argmodes, argnames); - funcfullsig = funcsig; - } funcsig_tag = format_function_signature(fout, finfo, false); @@ -9236,7 +9941,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) fmtId(finfo->dobj.namespace->dobj.name), funcsig); - appendPQExpBuffer(q, "CREATE FUNCTION %s ", funcfullsig); + appendPQExpBuffer(q, "CREATE FUNCTION %s ", funcfullsig ? funcfullsig : + funcsig); if (funcresult) appendPQExpBuffer(q, "RETURNS %s", funcresult); else @@ -9252,27 +9958,27 @@ dumpFunc(Archive *fout, FuncInfo *finfo) appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname)); if (proiswindow[0] == 't') - appendPQExpBuffer(q, " WINDOW"); + appendPQExpBufferStr(q, " WINDOW"); if (provolatile[0] != PROVOLATILE_VOLATILE) { if (provolatile[0] == PROVOLATILE_IMMUTABLE) - appendPQExpBuffer(q, " IMMUTABLE"); + appendPQExpBufferStr(q, " IMMUTABLE"); else if (provolatile[0] == PROVOLATILE_STABLE) - appendPQExpBuffer(q, " STABLE"); + appendPQExpBufferStr(q, " STABLE"); else if (provolatile[0] != PROVOLATILE_VOLATILE) exit_horribly(NULL, "unrecognized provolatile value for function \"%s\"\n", finfo->dobj.name); } if (proisstrict[0] == 't') - appendPQExpBuffer(q, " STRICT"); + appendPQExpBufferStr(q, " STRICT"); if (prosecdef[0] == 't') - appendPQExpBuffer(q, " SECURITY DEFINER"); + appendPQExpBufferStr(q, " SECURITY DEFINER"); if (proleakproof[0] == 't') - appendPQExpBuffer(q, " LEAKPROOF"); + appendPQExpBufferStr(q, " LEAKPROOF"); /* * COST and ROWS are emitted only if present and not default, so as not to @@ -9316,7 +10022,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) */ if (pg_strcasecmp(configitem, "DateStyle") == 0 || pg_strcasecmp(configitem, "search_path") == 0) - appendPQExpBuffer(q, "%s", pos); + appendPQExpBufferStr(q, pos); else appendStringLiteralAH(q, pos, fout); } @@ -9335,7 +10041,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) finfo->rolname, false, "FUNCTION", SECTION_PRE_DATA, q->data, delqry->data, NULL, - finfo->dobj.dependencies, finfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Function Comments and Security Labels */ @@ -9359,6 +10065,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) destroyPQExpBuffer(labelq); destroyPQExpBuffer(asPart); free(funcsig); + if (funcfullsig) + free(funcfullsig); free(funcsig_tag); if (allargtypes) free(allargtypes); @@ -9451,45 +10159,51 @@ dumpCast(Archive *fout, CastInfo *cast) labelq = createPQExpBuffer(); appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n", - getFormattedTypeName(fout, cast->castsource, zeroAsNone), - getFormattedTypeName(fout, cast->casttarget, zeroAsNone)); + getFormattedTypeName(fout, cast->castsource, zeroAsNone), + getFormattedTypeName(fout, cast->casttarget, zeroAsNone)); appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ", - getFormattedTypeName(fout, cast->castsource, zeroAsNone), - getFormattedTypeName(fout, cast->casttarget, zeroAsNone)); + getFormattedTypeName(fout, cast->castsource, zeroAsNone), + getFormattedTypeName(fout, cast->casttarget, zeroAsNone)); switch (cast->castmethod) { case COERCION_METHOD_BINARY: - appendPQExpBuffer(defqry, "WITHOUT FUNCTION"); + appendPQExpBufferStr(defqry, "WITHOUT FUNCTION"); break; case COERCION_METHOD_INOUT: - appendPQExpBuffer(defqry, "WITH INOUT"); + appendPQExpBufferStr(defqry, "WITH INOUT"); break; case COERCION_METHOD_FUNCTION: + if (funcInfo) + { + char *fsig = format_function_signature(fout, funcInfo, true); - /* - * Always qualify the function name, in case it is not in - * pg_catalog schema (format_function_signature won't qualify it). - */ - appendPQExpBuffer(defqry, "WITH FUNCTION %s.", - fmtId(funcInfo->dobj.namespace->dobj.name)); - appendPQExpBuffer(defqry, "%s", - format_function_signature(fout, funcInfo, true)); + /* + * Always qualify the function name, in case it is not in + * pg_catalog schema (format_function_signature won't qualify + * it). + */ + appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s", + fmtId(funcInfo->dobj.namespace->dobj.name), fsig); + free(fsig); + } + else + write_msg(NULL, "WARNING: bogus value in pg_cast.castfunc or pg_cast.castmethod field\n"); break; default: write_msg(NULL, "WARNING: bogus value in pg_cast.castmethod field\n"); } if (cast->castcontext == 'a') - appendPQExpBuffer(defqry, " AS ASSIGNMENT"); + appendPQExpBufferStr(defqry, " AS ASSIGNMENT"); else if (cast->castcontext == 'i') - appendPQExpBuffer(defqry, " AS IMPLICIT"); - appendPQExpBuffer(defqry, ";\n"); + appendPQExpBufferStr(defqry, " AS IMPLICIT"); + appendPQExpBufferStr(defqry, ";\n"); appendPQExpBuffer(labelq, "CAST (%s AS %s)", - getFormattedTypeName(fout, cast->castsource, zeroAsNone), - getFormattedTypeName(fout, cast->casttarget, zeroAsNone)); + getFormattedTypeName(fout, cast->castsource, zeroAsNone), + getFormattedTypeName(fout, cast->casttarget, zeroAsNone)); if (binary_upgrade) binary_upgrade_extension_member(defqry, &cast->dobj, labelq->data); @@ -9499,7 +10213,7 @@ dumpCast(Archive *fout, CastInfo *cast) "pg_catalog", NULL, "", false, "CAST", SECTION_PRE_DATA, defqry->data, delqry->data, NULL, - cast->dobj.dependencies, cast->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Cast Comments */ @@ -9547,6 +10261,8 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) char *oprjoin; char *oprcanmerge; char *oprcanhash; + char *oprregproc; + char *oprref; /* Skip if not to be dumped */ if (!oprinfo->dobj.dump || dataOnly) @@ -9653,8 +10369,12 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge); oprcanhash = PQgetvalue(res, 0, i_oprcanhash); - appendPQExpBuffer(details, " PROCEDURE = %s", - convertRegProcReference(fout, oprcode)); + oprregproc = convertRegProcReference(fout, oprcode); + if (oprregproc) + { + appendPQExpBuffer(details, " PROCEDURE = %s", oprregproc); + free(oprregproc); + } appendPQExpBuffer(oprid, "%s (", oprinfo->dobj.name); @@ -9671,10 +10391,10 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) else name = fmtId(oprleft); appendPQExpBuffer(details, ",\n LEFTARG = %s", name); - appendPQExpBuffer(oprid, "%s", name); + appendPQExpBufferStr(oprid, name); } else - appendPQExpBuffer(oprid, "NONE"); + appendPQExpBufferStr(oprid, "NONE"); if (strcmp(oprkind, "l") == 0 || strcmp(oprkind, "b") == 0) @@ -9687,29 +10407,41 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) appendPQExpBuffer(oprid, ", %s)", name); } else - appendPQExpBuffer(oprid, ", NONE)"); + appendPQExpBufferStr(oprid, ", NONE)"); - name = convertOperatorReference(fout, oprcom); - if (name) - appendPQExpBuffer(details, ",\n COMMUTATOR = %s", name); + oprref = convertOperatorReference(fout, oprcom); + if (oprref) + { + appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref); + free(oprref); + } - name = convertOperatorReference(fout, oprnegate); - if (name) - appendPQExpBuffer(details, ",\n NEGATOR = %s", name); + oprref = convertOperatorReference(fout, oprnegate); + if (oprref) + { + appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref); + free(oprref); + } if (strcmp(oprcanmerge, "t") == 0) - appendPQExpBuffer(details, ",\n MERGES"); + appendPQExpBufferStr(details, ",\n MERGES"); if (strcmp(oprcanhash, "t") == 0) - appendPQExpBuffer(details, ",\n HASHES"); + appendPQExpBufferStr(details, ",\n HASHES"); - name = convertRegProcReference(fout, oprrest); - if (name) - appendPQExpBuffer(details, ",\n RESTRICT = %s", name); + oprregproc = convertRegProcReference(fout, oprrest); + if (oprregproc) + { + appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc); + free(oprregproc); + } - name = convertRegProcReference(fout, oprjoin); - if (name) - appendPQExpBuffer(details, ",\n JOIN = %s", name); + oprregproc = convertRegProcReference(fout, oprjoin); + if (oprregproc) + { + appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc); + free(oprregproc); + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -9733,7 +10465,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) oprinfo->rolname, false, "OPERATOR", SECTION_PRE_DATA, q->data, delq->data, NULL, - oprinfo->dobj.dependencies, oprinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Operator Comments */ @@ -9754,12 +10486,13 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) /* * Convert a function reference obtained from pg_operator * - * Returns what to print, or NULL if function references is InvalidOid + * Returns allocated string of what to print, or NULL if function references + * is InvalidOid. Returned string is expected to be free'd by the caller. * * In 7.3 the input is a REGPROCEDURE display; we have to strip the * argument-types part. In prior versions, the input is a REGPROC display. */ -static const char * +static char * convertRegProcReference(Archive *fout, const char *proc) { /* In all cases "-" means a null reference */ @@ -9789,20 +10522,21 @@ convertRegProcReference(Archive *fout, const char *proc) } /* REGPROC before 7.3 does not quote its result */ - return fmtId(proc); + return pg_strdup(fmtId(proc)); } /* * Convert an operator cross-reference obtained from pg_operator * - * Returns what to print, or NULL to print nothing + * Returns an allocated string of what to print, or NULL to print nothing. + * Caller is responsible for free'ing result string. * * In 7.3 and up the input is a REGOPERATOR display; we have to strip the * argument-types part, and add OPERATOR() decoration if the name is * schema-qualified. In older versions, the input is just a numeric OID, * which we search our operator list for. */ -static const char * +static char * convertOperatorReference(Archive *fout, const char *opr) { OprInfo *oprInfo; @@ -9838,8 +10572,7 @@ convertOperatorReference(Archive *fout, const char *opr) /* If not schema-qualified, don't need to add OPERATOR() */ if (!sawdot) return name; - oname = pg_malloc(strlen(name) + 11); - sprintf(oname, "OPERATOR(%s)", name); + oname = psprintf("OPERATOR(%s)", name); free(name); return oname; } @@ -9851,7 +10584,7 @@ convertOperatorReference(Archive *fout, const char *opr) opr); return NULL; } - return oprInfo->dobj.name; + return pg_strdup(oprInfo->dobj.name); } /* @@ -10011,7 +10744,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ", fmtId(opcinfo->dobj.name)); if (strcmp(opcdefault, "t") == 0) - appendPQExpBuffer(q, "DEFAULT "); + appendPQExpBufferStr(q, "DEFAULT "); appendPQExpBuffer(q, "FOR TYPE %s USING %s", opcintype, fmtId(amname)); @@ -10019,12 +10752,12 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) (strcmp(opcfamilyname, opcinfo->dobj.name) != 0 || strcmp(opcfamilynsp, opcinfo->dobj.namespace->dobj.name) != 0)) { - appendPQExpBuffer(q, " FAMILY "); + appendPQExpBufferStr(q, " FAMILY "); if (strcmp(opcfamilynsp, opcinfo->dobj.namespace->dobj.name) != 0) appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp)); appendPQExpBuffer(q, "%s", fmtId(opcfamilyname)); } - appendPQExpBuffer(q, " AS\n "); + appendPQExpBufferStr(q, " AS\n "); needComma = false; @@ -10131,21 +10864,21 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp); if (needComma) - appendPQExpBuffer(q, " ,\n "); + appendPQExpBufferStr(q, " ,\n "); appendPQExpBuffer(q, "OPERATOR %s %s", amopstrategy, amopopr); if (strlen(sortfamily) > 0) { - appendPQExpBuffer(q, " FOR ORDER BY "); + appendPQExpBufferStr(q, " FOR ORDER BY "); if (strcmp(sortfamilynsp, opcinfo->dobj.namespace->dobj.name) != 0) appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp)); - appendPQExpBuffer(q, "%s", fmtId(sortfamily)); + appendPQExpBufferStr(q, fmtId(sortfamily)); } if (strcmp(amopreqcheck, "t") == 0) - appendPQExpBuffer(q, " RECHECK"); + appendPQExpBufferStr(q, " RECHECK"); needComma = true; } @@ -10209,7 +10942,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) amprocrighttype = PQgetvalue(res, i, i_amprocrighttype); if (needComma) - appendPQExpBuffer(q, " ,\n "); + appendPQExpBufferStr(q, " ,\n "); appendPQExpBuffer(q, "FUNCTION %s", amprocnum); @@ -10223,7 +10956,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) PQclear(res); - appendPQExpBuffer(q, ";\n"); + appendPQExpBufferStr(q, ";\n"); appendPQExpBuffer(labelq, "OPERATOR CLASS %s", fmtId(opcinfo->dobj.name)); @@ -10240,7 +10973,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) opcinfo->rolname, false, "OPERATOR CLASS", SECTION_PRE_DATA, q->data, delq->data, NULL, - opcinfo->dobj.dependencies, opcinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Operator Class Comments */ @@ -10489,21 +11222,21 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp); if (needComma) - appendPQExpBuffer(q, " ,\n "); + appendPQExpBufferStr(q, " ,\n "); appendPQExpBuffer(q, "OPERATOR %s %s", amopstrategy, amopopr); if (strlen(sortfamily) > 0) { - appendPQExpBuffer(q, " FOR ORDER BY "); + appendPQExpBufferStr(q, " FOR ORDER BY "); if (strcmp(sortfamilynsp, opfinfo->dobj.namespace->dobj.name) != 0) appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp)); - appendPQExpBuffer(q, "%s", fmtId(sortfamily)); + appendPQExpBufferStr(q, fmtId(sortfamily)); } if (strcmp(amopreqcheck, "t") == 0) - appendPQExpBuffer(q, " RECHECK"); + appendPQExpBufferStr(q, " RECHECK"); needComma = true; } @@ -10526,7 +11259,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype); if (needComma) - appendPQExpBuffer(q, " ,\n "); + appendPQExpBufferStr(q, " ,\n "); appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s", amprocnum, amproclefttype, amprocrighttype, @@ -10535,7 +11268,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) needComma = true; } - appendPQExpBuffer(q, ";\n"); + appendPQExpBufferStr(q, ";\n"); } appendPQExpBuffer(labelq, "OPERATOR FAMILY %s", @@ -10553,7 +11286,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) opfinfo->rolname, false, "OPERATOR FAMILY", SECTION_PRE_DATA, q->data, delq->data, NULL, - opfinfo->dobj.dependencies, opfinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Operator Family Comments */ @@ -10626,9 +11359,9 @@ dumpCollation(Archive *fout, CollInfo *collinfo) appendPQExpBuffer(q, "CREATE COLLATION %s (lc_collate = ", fmtId(collinfo->dobj.name)); appendStringLiteralAH(q, collcollate, fout); - appendPQExpBuffer(q, ", lc_ctype = "); + appendPQExpBufferStr(q, ", lc_ctype = "); appendStringLiteralAH(q, collctype, fout); - appendPQExpBuffer(q, ");\n"); + appendPQExpBufferStr(q, ");\n"); appendPQExpBuffer(labelq, "COLLATION %s", fmtId(collinfo->dobj.name)); @@ -10642,7 +11375,7 @@ dumpCollation(Archive *fout, CollInfo *collinfo) collinfo->rolname, false, "COLLATION", SECTION_PRE_DATA, q->data, delq->data, NULL, - collinfo->dobj.dependencies, collinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Collation Comments */ @@ -10724,7 +11457,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) (condefault) ? "DEFAULT " : "", fmtId(convinfo->dobj.name)); appendStringLiteralAH(q, conforencoding, fout); - appendPQExpBuffer(q, " TO "); + appendPQExpBufferStr(q, " TO "); appendStringLiteralAH(q, contoencoding, fout); /* regproc is automatically quoted in 7.3 and above */ appendPQExpBuffer(q, " FROM %s;\n", conproc); @@ -10741,7 +11474,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) convinfo->rolname, false, "CONVERSION", SECTION_PRE_DATA, q->data, delq->data, NULL, - convinfo->dobj.dependencies, convinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Conversion Comments */ @@ -10771,16 +11504,15 @@ format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes) initPQExpBuffer(&buf); if (honor_quotes) - appendPQExpBuffer(&buf, "%s", - fmtId(agginfo->aggfn.dobj.name)); + appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name)); else - appendPQExpBuffer(&buf, "%s", agginfo->aggfn.dobj.name); + appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name); if (agginfo->aggfn.nargs == 0) appendPQExpBuffer(&buf, "(*)"); else { - appendPQExpBuffer(&buf, "("); + appendPQExpBufferChar(&buf, '('); for (j = 0; j < agginfo->aggfn.nargs; j++) { char *typname; @@ -10793,7 +11525,7 @@ format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes) typname); free(typname); } - appendPQExpBuffer(&buf, ")"); + appendPQExpBufferChar(&buf, ')'); } return buf.data; } @@ -10810,20 +11542,42 @@ dumpAgg(Archive *fout, AggInfo *agginfo) PQExpBuffer delq; PQExpBuffer labelq; PQExpBuffer details; - char *aggsig; + char *aggsig; /* identity signature */ + char *aggfullsig = NULL; /* full signature */ char *aggsig_tag; PGresult *res; int i_aggtransfn; int i_aggfinalfn; + int i_aggmtransfn; + int i_aggminvtransfn; + int i_aggmfinalfn; + int i_aggfinalextra; + int i_aggmfinalextra; int i_aggsortop; + int i_hypothetical; int i_aggtranstype; + int i_aggtransspace; + int i_aggmtranstype; + int i_aggmtransspace; int i_agginitval; + int i_aggminitval; int i_convertok; const char *aggtransfn; const char *aggfinalfn; + const char *aggmtransfn; + const char *aggminvtransfn; + const char *aggmfinalfn; + bool aggfinalextra; + bool aggmfinalextra; const char *aggsortop; + char *aggsortconvop; + bool hypothetical; const char *aggtranstype; + const char *aggtransspace; + const char *aggmtranstype; + const char *aggmtransspace; const char *agginitval; + const char *aggminitval; bool convertok; /* Skip if not to be dumped */ @@ -10840,25 +11594,73 @@ dumpAgg(Archive *fout, AggInfo *agginfo) selectSourceSchema(fout, agginfo->aggfn.dobj.namespace->dobj.name); /* Get aggregate-specific details */ - if (fout->remoteVersion >= 80100) + if (fout->remoteVersion >= 90400) + { + appendPQExpBuffer(query, "SELECT aggtransfn, " + "aggfinalfn, aggtranstype::pg_catalog.regtype, " + "aggmtransfn, aggminvtransfn, aggmfinalfn, " + "aggmtranstype::pg_catalog.regtype, " + "aggfinalextra, aggmfinalextra, " + "aggsortop::pg_catalog.regoperator, " + "(aggkind = 'h') AS hypothetical, " + "aggtransspace, agginitval, " + "aggmtransspace, aggminitval, " + "true AS convertok, " + "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, " + "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs " + "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " + "WHERE a.aggfnoid = p.oid " + "AND p.oid = '%u'::pg_catalog.oid", + agginfo->aggfn.dobj.catId.oid); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT aggtransfn, " "aggfinalfn, aggtranstype::pg_catalog.regtype, " + "'-' AS aggmtransfn, '-' AS aggminvtransfn, " + "'-' AS aggmfinalfn, 0 AS aggmtranstype, " + "false AS aggfinalextra, false AS aggmfinalextra, " "aggsortop::pg_catalog.regoperator, " - "agginitval, " - "'t'::boolean AS convertok " + "false AS hypothetical, " + "0 AS aggtransspace, agginitval, " + "0 AS aggmtransspace, NULL AS aggminitval, " + "true AS convertok, " + "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, " + "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs " "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " "WHERE a.aggfnoid = p.oid " "AND p.oid = '%u'::pg_catalog.oid", agginfo->aggfn.dobj.catId.oid); } + else if (fout->remoteVersion >= 80100) + { + appendPQExpBuffer(query, "SELECT aggtransfn, " + "aggfinalfn, aggtranstype::pg_catalog.regtype, " + "'-' AS aggmtransfn, '-' AS aggminvtransfn, " + "'-' AS aggmfinalfn, 0 AS aggmtranstype, " + "false AS aggfinalextra, false AS aggmfinalextra, " + "aggsortop::pg_catalog.regoperator, " + "false AS hypothetical, " + "0 AS aggtransspace, agginitval, " + "0 AS aggmtransspace, NULL AS aggminitval, " + "true AS convertok " + "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " + "WHERE a.aggfnoid = p.oid " + "AND p.oid = '%u'::pg_catalog.oid", + agginfo->aggfn.dobj.catId.oid); + } else if (fout->remoteVersion >= 70300) { appendPQExpBuffer(query, "SELECT aggtransfn, " "aggfinalfn, aggtranstype::pg_catalog.regtype, " + "'-' AS aggmtransfn, '-' AS aggminvtransfn, " + "'-' AS aggmfinalfn, 0 AS aggmtranstype, " + "false AS aggfinalextra, false AS aggmfinalextra, " "0 AS aggsortop, " - "agginitval, " - "'t'::boolean AS convertok " + "false AS hypothetical, " + "0 AS aggtransspace, agginitval, " + "0 AS aggmtransspace, NULL AS aggminitval, " + "true AS convertok " "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " "WHERE a.aggfnoid = p.oid " "AND p.oid = '%u'::pg_catalog.oid", @@ -10868,9 +11670,14 @@ dumpAgg(Archive *fout, AggInfo *agginfo) { appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, " "format_type(aggtranstype, NULL) AS aggtranstype, " + "'-' AS aggmtransfn, '-' AS aggminvtransfn, " + "'-' AS aggmfinalfn, 0 AS aggmtranstype, " + "false AS aggfinalextra, false AS aggmfinalextra, " "0 AS aggsortop, " - "agginitval, " - "'t'::boolean AS convertok " + "false AS hypothetical, " + "0 AS aggtransspace, agginitval, " + "0 AS aggmtransspace, NULL AS aggminitval, " + "true AS convertok " "FROM pg_aggregate " "WHERE oid = '%u'::oid", agginfo->aggfn.dobj.catId.oid); @@ -10880,8 +11687,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo) appendPQExpBuffer(query, "SELECT aggtransfn1 AS aggtransfn, " "aggfinalfn, " "(SELECT typname FROM pg_type WHERE oid = aggtranstype1) AS aggtranstype, " + "'-' AS aggmtransfn, '-' AS aggminvtransfn, " + "'-' AS aggmfinalfn, 0 AS aggmtranstype, " + "false AS aggfinalextra, false AS aggmfinalextra, " "0 AS aggsortop, " - "agginitval1 AS agginitval, " + "false AS hypothetical, " + "0 AS aggtransspace, agginitval1 AS agginitval, " + "0 AS aggmtransspace, NULL AS aggminitval, " "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok " "FROM pg_aggregate " "WHERE oid = '%u'::oid", @@ -10892,25 +11704,65 @@ dumpAgg(Archive *fout, AggInfo *agginfo) i_aggtransfn = PQfnumber(res, "aggtransfn"); i_aggfinalfn = PQfnumber(res, "aggfinalfn"); + i_aggmtransfn = PQfnumber(res, "aggmtransfn"); + i_aggminvtransfn = PQfnumber(res, "aggminvtransfn"); + i_aggmfinalfn = PQfnumber(res, "aggmfinalfn"); + i_aggfinalextra = PQfnumber(res, "aggfinalextra"); + i_aggmfinalextra = PQfnumber(res, "aggmfinalextra"); i_aggsortop = PQfnumber(res, "aggsortop"); + i_hypothetical = PQfnumber(res, "hypothetical"); i_aggtranstype = PQfnumber(res, "aggtranstype"); + i_aggtransspace = PQfnumber(res, "aggtransspace"); + i_aggmtranstype = PQfnumber(res, "aggmtranstype"); + i_aggmtransspace = PQfnumber(res, "aggmtransspace"); i_agginitval = PQfnumber(res, "agginitval"); + i_aggminitval = PQfnumber(res, "aggminitval"); i_convertok = PQfnumber(res, "convertok"); aggtransfn = PQgetvalue(res, 0, i_aggtransfn); aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn); + aggmtransfn = PQgetvalue(res, 0, i_aggmtransfn); + aggminvtransfn = PQgetvalue(res, 0, i_aggminvtransfn); + aggmfinalfn = PQgetvalue(res, 0, i_aggmfinalfn); + aggfinalextra = (PQgetvalue(res, 0, i_aggfinalextra)[0] == 't'); + aggmfinalextra = (PQgetvalue(res, 0, i_aggmfinalextra)[0] == 't'); aggsortop = PQgetvalue(res, 0, i_aggsortop); + hypothetical = (PQgetvalue(res, 0, i_hypothetical)[0] == 't'); aggtranstype = PQgetvalue(res, 0, i_aggtranstype); + aggtransspace = PQgetvalue(res, 0, i_aggtransspace); + aggmtranstype = PQgetvalue(res, 0, i_aggmtranstype); + aggmtransspace = PQgetvalue(res, 0, i_aggmtransspace); agginitval = PQgetvalue(res, 0, i_agginitval); + aggminitval = PQgetvalue(res, 0, i_aggminitval); convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't'); - aggsig = format_aggregate_signature(agginfo, fout, true); + if (fout->remoteVersion >= 80400) + { + /* 8.4 or later; we rely on server-side code for most of the work */ + char *funcargs; + char *funciargs; + + funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs")); + funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs")); + aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true); + aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true); + } + else + /* pre-8.4, do it ourselves */ + aggsig = format_aggregate_signature(agginfo, fout, true); + aggsig_tag = format_aggregate_signature(agginfo, fout, false); if (!convertok) { write_msg(NULL, "WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n", aggsig); + + if (aggfullsig) + free(aggfullsig); + + free(aggsig); + return; } @@ -10937,9 +11789,15 @@ dumpAgg(Archive *fout, AggInfo *agginfo) fmtId(aggtranstype)); } + if (strcmp(aggtransspace, "0") != 0) + { + appendPQExpBuffer(details, ",\n SSPACE = %s", + aggtransspace); + } + if (!PQgetisnull(res, 0, i_agginitval)) { - appendPQExpBuffer(details, ",\n INITCOND = "); + appendPQExpBufferStr(details, ",\n INITCOND = "); appendStringLiteralAH(details, agginitval, fout); } @@ -10947,15 +11805,49 @@ dumpAgg(Archive *fout, AggInfo *agginfo) { appendPQExpBuffer(details, ",\n FINALFUNC = %s", aggfinalfn); + if (aggfinalextra) + appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA"); + } + + if (strcmp(aggmtransfn, "-") != 0) + { + appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s", + aggmtransfn, + aggminvtransfn, + aggmtranstype); + } + + if (strcmp(aggmtransspace, "0") != 0) + { + appendPQExpBuffer(details, ",\n MSSPACE = %s", + aggmtransspace); } - aggsortop = convertOperatorReference(fout, aggsortop); - if (aggsortop) + if (!PQgetisnull(res, 0, i_aggminitval)) + { + appendPQExpBufferStr(details, ",\n MINITCOND = "); + appendStringLiteralAH(details, aggminitval, fout); + } + + if (strcmp(aggmfinalfn, "-") != 0) + { + appendPQExpBuffer(details, ",\n MFINALFUNC = %s", + aggmfinalfn); + if (aggmfinalextra) + appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA"); + } + + aggsortconvop = convertOperatorReference(fout, aggsortop); + if (aggsortconvop) { appendPQExpBuffer(details, ",\n SORTOP = %s", - aggsortop); + aggsortconvop); + free(aggsortconvop); } + if (hypothetical) + appendPQExpBufferStr(details, ",\n HYPOTHETICAL"); + /* * DROP must be fully qualified in case same name appears in pg_catalog */ @@ -10964,7 +11856,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) aggsig); appendPQExpBuffer(q, "CREATE AGGREGATE %s (\n%s\n);\n", - aggsig, details->data); + aggfullsig ? aggfullsig : aggsig, details->data); appendPQExpBuffer(labelq, "AGGREGATE %s", aggsig); @@ -10978,7 +11870,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) agginfo->aggfn.rolname, false, "AGGREGATE", SECTION_PRE_DATA, q->data, delq->data, NULL, - agginfo->aggfn.dobj.dependencies, agginfo->aggfn.dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Aggregate Comments */ @@ -10992,7 +11884,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) /* * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL * command look like a function's GRANT; in particular this affects the - * syntax for zero-argument aggregates. + * syntax for zero-argument aggregates and ordered-set aggregates. */ free(aggsig); free(aggsig_tag); @@ -11007,6 +11899,8 @@ dumpAgg(Archive *fout, AggInfo *agginfo) agginfo->aggfn.rolname, agginfo->aggfn.proacl); free(aggsig); + if (aggfullsig) + free(aggfullsig); free(aggsig_tag); PQclear(res); @@ -11076,7 +11970,7 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) "", false, "TEXT SEARCH PARSER", SECTION_PRE_DATA, q->data, delq->data, NULL, - prsinfo->dobj.dependencies, prsinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Parser Comments */ @@ -11129,10 +12023,10 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n", fmtId(dictinfo->dobj.name)); - appendPQExpBuffer(q, " TEMPLATE = "); + appendPQExpBufferStr(q, " TEMPLATE = "); if (strcmp(nspname, dictinfo->dobj.namespace->dobj.name) != 0) appendPQExpBuffer(q, "%s.", fmtId(nspname)); - appendPQExpBuffer(q, "%s", fmtId(tmplname)); + appendPQExpBufferStr(q, fmtId(tmplname)); PQclear(res); @@ -11140,7 +12034,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) if (dictinfo->dictinitoption) appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption); - appendPQExpBuffer(q, " );\n"); + appendPQExpBufferStr(q, " );\n"); /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -11163,7 +12057,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) dictinfo->rolname, false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA, q->data, delq->data, NULL, - dictinfo->dobj.dependencies, dictinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Dictionary Comments */ @@ -11229,7 +12123,7 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) "", false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA, q->data, delq->data, NULL, - tmplinfo->dobj.dependencies, tmplinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Template Comments */ @@ -11286,7 +12180,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n", fmtId(cfginfo->dobj.name)); - appendPQExpBuffer(q, " PARSER = "); + appendPQExpBufferStr(q, " PARSER = "); if (strcmp(nspname, cfginfo->dobj.namespace->dobj.name) != 0) appendPQExpBuffer(q, "%s.", fmtId(nspname)); appendPQExpBuffer(q, "%s );\n", fmtId(prsname)); @@ -11320,7 +12214,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) { /* starting a new token type, so start a new command */ if (i > 0) - appendPQExpBuffer(q, ";\n"); + appendPQExpBufferStr(q, ";\n"); appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n", fmtId(cfginfo->dobj.name)); /* tokenname needs quoting, dictname does NOT */ @@ -11332,7 +12226,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) } if (ntups > 0) - appendPQExpBuffer(q, ";\n"); + appendPQExpBufferStr(q, ";\n"); PQclear(res); @@ -11357,7 +12251,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) cfginfo->rolname, false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA, q->data, delq->data, NULL, - cfginfo->dobj.dependencies, cfginfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump Configuration Comments */ @@ -11413,7 +12307,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) if (strlen(fdwinfo->fdwoptions) > 0) appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions); - appendPQExpBuffer(q, ";\n"); + appendPQExpBufferStr(q, ";\n"); appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n", qfdwname); @@ -11431,7 +12325,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) fdwinfo->rolname, false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA, q->data, delq->data, NULL, - fdwinfo->dobj.dependencies, fdwinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Handle the ACL */ @@ -11491,22 +12385,22 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname); if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0) { - appendPQExpBuffer(q, " TYPE "); + appendPQExpBufferStr(q, " TYPE "); appendStringLiteralAH(q, srvinfo->srvtype, fout); } if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0) { - appendPQExpBuffer(q, " VERSION "); + appendPQExpBufferStr(q, " VERSION "); appendStringLiteralAH(q, srvinfo->srvversion, fout); } - appendPQExpBuffer(q, " FOREIGN DATA WRAPPER "); - appendPQExpBuffer(q, "%s", fmtId(fdwname)); + appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER "); + appendPQExpBufferStr(q, fmtId(fdwname)); if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0) appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions); - appendPQExpBuffer(q, ";\n"); + appendPQExpBufferStr(q, ";\n"); appendPQExpBuffer(delq, "DROP SERVER %s;\n", qsrvname); @@ -11523,7 +12417,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) srvinfo->rolname, false, "SERVER", SECTION_PRE_DATA, q->data, delq->data, NULL, - srvinfo->dobj.dependencies, srvinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Handle the ACL */ @@ -11623,7 +12517,7 @@ dumpUserMappings(Archive *fout, if (umoptions && strlen(umoptions) > 0) appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions); - appendPQExpBuffer(q, ";\n"); + appendPQExpBufferStr(q, ";\n"); resetPQExpBuffer(delq); appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename)); @@ -11648,6 +12542,7 @@ dumpUserMappings(Archive *fout, destroyPQExpBuffer(query); destroyPQExpBuffer(delq); + destroyPQExpBuffer(tag); destroyPQExpBuffer(q); } @@ -11679,10 +12574,13 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) case DEFACLOBJ_FUNCTION: type = "FUNCTIONS"; break; + case DEFACLOBJ_TYPE: + type = "TYPES"; + break; default: /* shouldn't get here */ exit_horribly(NULL, - "unknown object type (%d) in default privileges\n", + "unrecognized object type in default privileges: %d\n", (int) daclinfo->defaclobjtype); type = ""; /* keep compiler quiet */ } @@ -11705,9 +12603,9 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL, NULL, daclinfo->defaclrole, - false, "DEFAULT ACL", SECTION_NONE, + false, "DEFAULT ACL", SECTION_POST_DATA, q->data, "", NULL, - daclinfo->dobj.dependencies, daclinfo->dobj.nDeps, + NULL, 0, NULL, NULL); destroyPQExpBuffer(tag); @@ -11752,7 +12650,7 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, if (!buildACLCommands(name, subname, type, acls, owner, "", fout->remoteVersion, sql)) exit_horribly(NULL, - "could not parse ACL list (%s) for object \"%s\" (%s)\n", + "could not parse ACL list (%s) for object \"%s\" (%s)\n", acls, name, type); if (sql->len > 0) @@ -11829,7 +12727,7 @@ dumpSecLabel(Archive *fout, const char *target, "SECURITY LABEL FOR %s ON %s IS ", fmtId(labels[i].provider), target); appendStringLiteralAH(query, labels[i].label, fout); - appendPQExpBuffer(query, ";\n"); + appendPQExpBufferStr(query, ";\n"); } if (query->len > 0) @@ -11903,7 +12801,7 @@ dumpTableSecLabel(Archive *fout, TableInfo *tbinfo, const char *reltypename) appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ", fmtId(provider), target->data); appendStringLiteralAH(query, label, fout); - appendPQExpBuffer(query, ";\n"); + appendPQExpBufferStr(query, ";\n"); } if (query->len > 0) { @@ -12033,10 +12931,10 @@ collectSecLabels(Archive *fout, SecLabelItem **items) query = createPQExpBuffer(); - appendPQExpBuffer(query, - "SELECT label, provider, classoid, objoid, objsubid " - "FROM pg_catalog.pg_seclabel " - "ORDER BY classoid, objoid, objsubid"); + appendPQExpBufferStr(query, + "SELECT label, provider, classoid, objoid, objsubid " + "FROM pg_catalog.pg_seclabel " + "ORDER BY classoid, objoid, objsubid"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -12074,13 +12972,13 @@ collectSecLabels(Archive *fout, SecLabelItem **items) static void dumpTable(Archive *fout, TableInfo *tbinfo) { - if (tbinfo->dobj.dump) + if (tbinfo->dobj.dump && !dataOnly) { char *namecopy; if (tbinfo->relkind == RELKIND_SEQUENCE) dumpSequence(fout, tbinfo); - else if (!dataOnly) + else dumpTableSchema(fout, tbinfo); /* Handle the ACL here */ @@ -12118,8 +13016,7 @@ dumpTable(Archive *fout, TableInfo *tbinfo) char *acltag; attnamecopy = pg_strdup(fmtId(attname)); - acltag = pg_malloc(strlen(tbinfo->dobj.name) + strlen(attname) + 2); - sprintf(acltag, "%s.%s", tbinfo->dobj.name, attname); + acltag = psprintf("%s.%s", tbinfo->dobj.name, attname); /* Column's GRANT type is always TABLE */ dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, "TABLE", namecopy, attnamecopy, acltag, @@ -12136,6 +13033,63 @@ dumpTable(Archive *fout, TableInfo *tbinfo) } } +/* + * Create the AS clause for a view or materialized view. The semicolon is + * stripped because a materialized view must add a WITH NO DATA clause. + * + * This returns a new buffer which must be freed by the caller. + */ +static PQExpBuffer +createViewAsClause(Archive *fout, TableInfo *tbinfo) +{ + PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer result = createPQExpBuffer(); + PGresult *res; + int len; + + /* Fetch the view definition */ + if (fout->remoteVersion >= 70300) + { + /* Beginning in 7.3, viewname is not unique; rely on OID */ + appendPQExpBuffer(query, + "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef", + tbinfo->dobj.catId.oid); + } + else + { + appendPQExpBufferStr(query, "SELECT definition AS viewdef " + "FROM pg_views WHERE viewname = "); + appendStringLiteralAH(query, tbinfo->dobj.name, fout); + } + + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + + if (PQntuples(res) != 1) + { + if (PQntuples(res) < 1) + exit_horribly(NULL, "query to obtain definition of view \"%s\" returned no data\n", + tbinfo->dobj.name); + else + exit_horribly(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n", + tbinfo->dobj.name); + } + + len = PQgetlength(res, 0, 0); + + if (len == 0) + exit_horribly(NULL, "definition of view \"%s\" appears to be empty (length zero)\n", + tbinfo->dobj.name); + + /* Strip off the trailing semicolon so that other things may follow. */ + Assert(PQgetvalue(res, 0, 0)[len - 1] == ';'); + appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1); + + PQclear(res); + destroyPQExpBuffer(query); + + return result; +} + /* * dumpTableSchema * write the declaration (not data) of one user-defined table or view @@ -12143,14 +13097,12 @@ dumpTable(Archive *fout, TableInfo *tbinfo) static void dumpTableSchema(Archive *fout, TableInfo *tbinfo) { - PQExpBuffer query = createPQExpBuffer(); PQExpBuffer q = createPQExpBuffer(); PQExpBuffer delq = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - PGresult *res; int numParents; TableInfo **parents; - int actual_atts; /* number of attrs in this CREATE statment */ + int actual_atts; /* number of attrs in this CREATE statement */ const char *reltypename; char *storage; char *srvname; @@ -12168,44 +13120,10 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) /* Is it a table or a view? */ if (tbinfo->relkind == RELKIND_VIEW) { - char *viewdef; + PQExpBuffer result; reltypename = "VIEW"; - /* Fetch the view definition */ - if (fout->remoteVersion >= 70300) - { - /* Beginning in 7.3, viewname is not unique; rely on OID */ - appendPQExpBuffer(query, - "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef", - tbinfo->dobj.catId.oid); - } - else - { - appendPQExpBuffer(query, "SELECT definition AS viewdef " - "FROM pg_views WHERE viewname = "); - appendStringLiteralAH(query, tbinfo->dobj.name, fout); - appendPQExpBuffer(query, ";"); - } - - res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); - - if (PQntuples(res) != 1) - { - if (PQntuples(res) < 1) - exit_horribly(NULL, "query to obtain definition of view \"%s\" returned no data\n", - tbinfo->dobj.name); - else - exit_horribly(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n", - tbinfo->dobj.name); - } - - viewdef = PQgetvalue(res, 0, 0); - - if (strlen(viewdef) == 0) - exit_horribly(NULL, "definition of view \"%s\" appears to be empty (length zero)\n", - tbinfo->dobj.name); - /* * DROP must be fully qualified in case same name appears in * pg_catalog @@ -12222,49 +13140,64 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBuffer(q, "CREATE VIEW %s", fmtId(tbinfo->dobj.name)); if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) appendPQExpBuffer(q, " WITH (%s)", tbinfo->reloptions); - appendPQExpBuffer(q, " AS\n %s\n", viewdef); + result = createViewAsClause(fout, tbinfo); + appendPQExpBuffer(q, " AS\n%s", result->data); + destroyPQExpBuffer(result); + + if (tbinfo->checkoption != NULL) + appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption); + appendPQExpBufferStr(q, ";\n"); appendPQExpBuffer(labelq, "VIEW %s", fmtId(tbinfo->dobj.name)); - - PQclear(res); } else { - if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) - { - int i_srvname; - int i_ftoptions; - - reltypename = "FOREIGN TABLE"; - - /* retrieve name of foreign server and generic options */ - appendPQExpBuffer(query, - "SELECT fs.srvname, " - "pg_catalog.array_to_string(ARRAY(" - "SELECT pg_catalog.quote_ident(option_name) || " - "' ' || pg_catalog.quote_literal(option_value) " - "FROM pg_catalog.pg_options_to_table(ftoptions) " - "ORDER BY option_name" - "), E',\n ') AS ftoptions " - "FROM pg_catalog.pg_foreign_table ft " - "JOIN pg_catalog.pg_foreign_server fs " - "ON (fs.oid = ft.ftserver) " - "WHERE ft.ftrelid = '%u'", - tbinfo->dobj.catId.oid); - res = ExecuteSqlQueryForSingleRow(fout, query->data); - i_srvname = PQfnumber(res, "srvname"); - i_ftoptions = PQfnumber(res, "ftoptions"); - srvname = pg_strdup(PQgetvalue(res, 0, i_srvname)); - ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions)); - PQclear(res); - } - else + switch (tbinfo->relkind) { - reltypename = "TABLE"; - srvname = NULL; - ftoptions = NULL; + case (RELKIND_FOREIGN_TABLE): + { + PQExpBuffer query = createPQExpBuffer(); + PGresult *res; + int i_srvname; + int i_ftoptions; + + reltypename = "FOREIGN TABLE"; + + /* retrieve name of foreign server and generic options */ + appendPQExpBuffer(query, + "SELECT fs.srvname, " + "pg_catalog.array_to_string(ARRAY(" + "SELECT pg_catalog.quote_ident(option_name) || " + "' ' || pg_catalog.quote_literal(option_value) " + "FROM pg_catalog.pg_options_to_table(ftoptions) " + "ORDER BY option_name" + "), E',\n ') AS ftoptions " + "FROM pg_catalog.pg_foreign_table ft " + "JOIN pg_catalog.pg_foreign_server fs " + "ON (fs.oid = ft.ftserver) " + "WHERE ft.ftrelid = '%u'", + tbinfo->dobj.catId.oid); + res = ExecuteSqlQueryForSingleRow(fout, query->data); + i_srvname = PQfnumber(res, "srvname"); + i_ftoptions = PQfnumber(res, "ftoptions"); + srvname = pg_strdup(PQgetvalue(res, 0, i_srvname)); + ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions)); + PQclear(res); + destroyPQExpBuffer(query); + break; + } + case (RELKIND_MATVIEW): + reltypename = "MATERIALIZED VIEW"; + srvname = NULL; + ftoptions = NULL; + break; + default: + reltypename = "TABLE"; + srvname = NULL; + ftoptions = NULL; } + numParents = tbinfo->numParents; parents = tbinfo->parents; @@ -12297,181 +13230,197 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) if (tbinfo->reloftype && !binary_upgrade) appendPQExpBuffer(q, " OF %s", tbinfo->reloftype); - /* Dump the attributes */ - actual_atts = 0; - for (j = 0; j < tbinfo->numatts; j++) + if (tbinfo->relkind != RELKIND_MATVIEW) { - /* - * Normally, dump if it's locally defined in this table, and not - * dropped. But for binary upgrade, we'll dump all the columns, - * and then fix up the dropped and nonlocal cases below. - */ - if (shouldPrintColumn(tbinfo, j)) + /* Dump the attributes */ + actual_atts = 0; + for (j = 0; j < tbinfo->numatts; j++) { /* - * Default value --- suppress if to be printed separately. - */ - bool has_default = (tbinfo->attrdefs[j] != NULL && - !tbinfo->attrdefs[j]->separate); - - /* - * Not Null constraint --- suppress if inherited, except in - * binary-upgrade case where that won't work. + * Normally, dump if it's locally defined in this table, and + * not dropped. But for binary upgrade, we'll dump all the + * columns, and then fix up the dropped and nonlocal cases + * below. */ - bool has_notnull = (tbinfo->notnull[j] && - (!tbinfo->inhNotNull[j] || - binary_upgrade)); - - /* Skip column if fully defined by reloftype */ - if (tbinfo->reloftype && - !has_default && !has_notnull && !binary_upgrade) - continue; - - /* Format properly if not first attr */ - if (actual_atts == 0) - appendPQExpBuffer(q, " ("); - else - appendPQExpBuffer(q, ","); - appendPQExpBuffer(q, "\n "); - actual_atts++; - - /* Attribute name */ - appendPQExpBuffer(q, "%s ", - fmtId(tbinfo->attnames[j])); - - if (tbinfo->attisdropped[j]) + if (shouldPrintColumn(tbinfo, j)) { /* - * ALTER TABLE DROP COLUMN clears pg_attribute.atttypid, - * so we will not have gotten a valid type name; insert - * INTEGER as a stopgap. We'll clean things up later. + * Default value --- suppress if to be printed separately. */ - appendPQExpBuffer(q, "INTEGER /* dummy */"); - /* Skip all the rest, too */ - continue; - } + bool has_default = (tbinfo->attrdefs[j] != NULL && + !tbinfo->attrdefs[j]->separate); - /* Attribute type */ - if (tbinfo->reloftype && !binary_upgrade) - { - appendPQExpBuffer(q, "WITH OPTIONS"); - } - else if (fout->remoteVersion >= 70100) - { - appendPQExpBuffer(q, "%s", - tbinfo->atttypnames[j]); - } - else - { - /* If no format_type, fake it */ - appendPQExpBuffer(q, "%s", - myFormatType(tbinfo->atttypnames[j], - tbinfo->atttypmod[j])); - } + /* + * Not Null constraint --- suppress if inherited, except + * in binary-upgrade case where that won't work. + */ + bool has_notnull = (tbinfo->notnull[j] && + (!tbinfo->inhNotNull[j] || + binary_upgrade)); + + /* Skip column if fully defined by reloftype */ + if (tbinfo->reloftype && + !has_default && !has_notnull && !binary_upgrade) + continue; + + /* Format properly if not first attr */ + if (actual_atts == 0) + appendPQExpBufferStr(q, " ("); + else + appendPQExpBufferStr(q, ","); + appendPQExpBufferStr(q, "\n "); + actual_atts++; - /* Add collation if not default for the type */ - if (OidIsValid(tbinfo->attcollation[j])) - { - CollInfo *coll; + /* Attribute name */ + appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j])); - coll = findCollationByOid(tbinfo->attcollation[j]); - if (coll) + if (tbinfo->attisdropped[j]) { - /* always schema-qualify, don't try to be smart */ - appendPQExpBuffer(q, " COLLATE %s.", + /* + * ALTER TABLE DROP COLUMN clears + * pg_attribute.atttypid, so we will not have gotten a + * valid type name; insert INTEGER as a stopgap. We'll + * clean things up later. + */ + appendPQExpBufferStr(q, " INTEGER /* dummy */"); + /* Skip all the rest, too */ + continue; + } + + /* Attribute type */ + if (tbinfo->reloftype && !binary_upgrade) + { + appendPQExpBufferStr(q, " WITH OPTIONS"); + } + else if (fout->remoteVersion >= 70100) + { + appendPQExpBuffer(q, " %s", + tbinfo->atttypnames[j]); + } + else + { + /* If no format_type, fake it */ + appendPQExpBuffer(q, " %s", + myFormatType(tbinfo->atttypnames[j], + tbinfo->atttypmod[j])); + } + + /* Add collation if not default for the type */ + if (OidIsValid(tbinfo->attcollation[j])) + { + CollInfo *coll; + + coll = findCollationByOid(tbinfo->attcollation[j]); + if (coll) + { + /* always schema-qualify, don't try to be smart */ + appendPQExpBuffer(q, " COLLATE %s.", fmtId(coll->dobj.namespace->dobj.name)); - appendPQExpBuffer(q, "%s", - fmtId(coll->dobj.name)); + appendPQExpBufferStr(q, fmtId(coll->dobj.name)); + } } - } - if (has_default) - appendPQExpBuffer(q, " DEFAULT %s", - tbinfo->attrdefs[j]->adef_expr); + if (has_default) + appendPQExpBuffer(q, " DEFAULT %s", + tbinfo->attrdefs[j]->adef_expr); - if (has_notnull) - appendPQExpBuffer(q, " NOT NULL"); + if (has_notnull) + appendPQExpBufferStr(q, " NOT NULL"); + } } - } - /* - * Add non-inherited CHECK constraints, if any. - */ - for (j = 0; j < tbinfo->ncheck; j++) - { - ConstraintInfo *constr = &(tbinfo->checkexprs[j]); + /* + * Add non-inherited CHECK constraints, if any. + */ + for (j = 0; j < tbinfo->ncheck; j++) + { + ConstraintInfo *constr = &(tbinfo->checkexprs[j]); - if (constr->separate || !constr->conislocal) - continue; + if (constr->separate || !constr->conislocal) + continue; - if (actual_atts == 0) - appendPQExpBuffer(q, " (\n "); - else - appendPQExpBuffer(q, ",\n "); + if (actual_atts == 0) + appendPQExpBufferStr(q, " (\n "); + else + appendPQExpBufferStr(q, ",\n "); - appendPQExpBuffer(q, "CONSTRAINT %s ", - fmtId(constr->dobj.name)); - appendPQExpBuffer(q, "%s", constr->condef); + appendPQExpBuffer(q, "CONSTRAINT %s ", + fmtId(constr->dobj.name)); + appendPQExpBufferStr(q, constr->condef); - actual_atts++; - } + actual_atts++; + } - if (actual_atts) - appendPQExpBuffer(q, "\n)"); - else if (!(tbinfo->reloftype && !binary_upgrade)) - { - /* - * We must have a parenthesized attribute list, even though empty, - * when not using the OF TYPE syntax. - */ - appendPQExpBuffer(q, " (\n)"); - } + if (actual_atts) + appendPQExpBufferStr(q, "\n)"); + else if (!(tbinfo->reloftype && !binary_upgrade)) + { + /* + * We must have a parenthesized attribute list, even though + * empty, when not using the OF TYPE syntax. + */ + appendPQExpBufferStr(q, " (\n)"); + } - if (numParents > 0 && !binary_upgrade) - { - appendPQExpBuffer(q, "\nINHERITS ("); - for (k = 0; k < numParents; k++) + if (numParents > 0 && !binary_upgrade) { - TableInfo *parentRel = parents[k]; + appendPQExpBufferStr(q, "\nINHERITS ("); + for (k = 0; k < numParents; k++) + { + TableInfo *parentRel = parents[k]; - if (k > 0) - appendPQExpBuffer(q, ", "); - if (parentRel->dobj.namespace != tbinfo->dobj.namespace) - appendPQExpBuffer(q, "%s.", + if (k > 0) + appendPQExpBufferStr(q, ", "); + if (parentRel->dobj.namespace != tbinfo->dobj.namespace) + appendPQExpBuffer(q, "%s.", fmtId(parentRel->dobj.namespace->dobj.name)); - appendPQExpBuffer(q, "%s", - fmtId(parentRel->dobj.name)); + appendPQExpBufferStr(q, fmtId(parentRel->dobj.name)); + } + appendPQExpBufferChar(q, ')'); } - appendPQExpBuffer(q, ")"); - } - if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) - appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) + appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); + } if ((tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) || (tbinfo->toast_reloptions && strlen(tbinfo->toast_reloptions) > 0)) { bool addcomma = false; - appendPQExpBuffer(q, "\nWITH ("); + appendPQExpBufferStr(q, "\nWITH ("); if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) { addcomma = true; - appendPQExpBuffer(q, "%s", tbinfo->reloptions); + appendPQExpBufferStr(q, tbinfo->reloptions); } if (tbinfo->toast_reloptions && strlen(tbinfo->toast_reloptions) > 0) { appendPQExpBuffer(q, "%s%s", addcomma ? ", " : "", tbinfo->toast_reloptions); } - appendPQExpBuffer(q, ")"); + appendPQExpBufferChar(q, ')'); } /* Dump generic options if any */ if (ftoptions && ftoptions[0]) appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions); - appendPQExpBuffer(q, ";\n"); + /* + * For materialized views, create the AS clause just like a view. At + * this point, we always mark the view as not populated. + */ + if (tbinfo->relkind == RELKIND_MATVIEW) + { + PQExpBuffer result; + + result = createViewAsClause(fout, tbinfo); + appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n", + result->data); + destroyPQExpBuffer(result); + } + else + appendPQExpBufferStr(q, ";\n"); /* * To create binary-compatible heap files, we have to ensure the same @@ -12486,13 +13435,14 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * attislocal correctly, plus fix up any inherited CHECK constraints. * Analogously, we set up typed tables using ALTER TABLE / OF here. */ - if (binary_upgrade && tbinfo->relkind == RELKIND_RELATION) + if (binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_FOREIGN_TABLE) ) { for (j = 0; j < tbinfo->numatts; j++) { if (tbinfo->attisdropped[j]) { - appendPQExpBuffer(q, "\n-- For binary upgrade, recreate dropped column.\n"); + appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped column.\n"); appendPQExpBuffer(q, "UPDATE pg_catalog.pg_attribute\n" "SET attlen = %d, " "attalign = '%c', attbyval = false\n" @@ -12500,25 +13450,31 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) tbinfo->attlen[j], tbinfo->attalign[j]); appendStringLiteralAH(q, tbinfo->attnames[j], fout); - appendPQExpBuffer(q, "\n AND attrelid = "); + appendPQExpBufferStr(q, "\n AND attrelid = "); appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); - appendPQExpBuffer(q, "::pg_catalog.regclass;\n"); + appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); + + if (tbinfo->relkind == RELKIND_RELATION) + appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", + fmtId(tbinfo->dobj.name)); + else + appendPQExpBuffer(q, "ALTER FOREIGN TABLE %s ", + fmtId(tbinfo->dobj.name)); - appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", - fmtId(tbinfo->dobj.name)); appendPQExpBuffer(q, "DROP COLUMN %s;\n", fmtId(tbinfo->attnames[j])); } else if (!tbinfo->attislocal[j]) { - appendPQExpBuffer(q, "\n-- For binary upgrade, recreate inherited column.\n"); - appendPQExpBuffer(q, "UPDATE pg_catalog.pg_attribute\n" + Assert(tbinfo->relkind != RELKIND_FOREIGN_TABLE); + appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited column.\n"); + appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n" "SET attislocal = false\n" "WHERE attname = "); appendStringLiteralAH(q, tbinfo->attnames[j], fout); - appendPQExpBuffer(q, "\n AND attrelid = "); + appendPQExpBufferStr(q, "\n AND attrelid = "); appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); - appendPQExpBuffer(q, "::pg_catalog.regclass;\n"); + appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); } } @@ -12529,24 +13485,24 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) if (constr->separate || constr->conislocal) continue; - appendPQExpBuffer(q, "\n-- For binary upgrade, set up inherited constraint.\n"); + appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraint.\n"); appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); appendPQExpBuffer(q, " ADD CONSTRAINT %s ", fmtId(constr->dobj.name)); appendPQExpBuffer(q, "%s;\n", constr->condef); - appendPQExpBuffer(q, "UPDATE pg_catalog.pg_constraint\n" + appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n" "SET conislocal = false\n" "WHERE contype = 'c' AND conname = "); appendStringLiteralAH(q, constr->dobj.name, fout); - appendPQExpBuffer(q, "\n AND conrelid = "); + appendPQExpBufferStr(q, "\n AND conrelid = "); appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); - appendPQExpBuffer(q, "::pg_catalog.regclass;\n"); + appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); } if (numParents > 0) { - appendPQExpBuffer(q, "\n-- For binary upgrade, set up inheritance this way.\n"); + appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n"); for (k = 0; k < numParents; k++) { TableInfo *parentRel = parents[k]; @@ -12563,24 +13519,24 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) if (tbinfo->reloftype) { - appendPQExpBuffer(q, "\n-- For binary upgrade, set up typed tables this way.\n"); + appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n"); appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n", fmtId(tbinfo->dobj.name), tbinfo->reloftype); } - appendPQExpBuffer(q, "\n-- For binary upgrade, set heap's relfrozenxid\n"); + appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid\n"); appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n" "SET relfrozenxid = '%u'\n" "WHERE oid = ", tbinfo->frozenxid); appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); - appendPQExpBuffer(q, "::pg_catalog.regclass;\n"); + appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); if (tbinfo->toast_oid) { /* We preserve the toast oids, so we can use it during restore */ - appendPQExpBuffer(q, "\n-- For binary upgrade, set toast's relfrozenxid\n"); + appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid\n"); appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n" "SET relfrozenxid = '%u'\n" "WHERE oid = '%u';\n", @@ -12588,6 +13544,23 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) } } + /* + * In binary_upgrade mode, restore matviews' populated status by + * poking pg_class directly. This is pretty ugly, but we can't use + * REFRESH MATERIALIZED VIEW since it's possible that some underlying + * matview is not populated even though this matview is. + */ + if (binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW && + tbinfo->relispopulated) + { + appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n"); + appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n" + "SET relispopulated = 't'\n" + "WHERE oid = "); + appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); + appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); + } + /* * Dump additional per-column properties that we can't handle in the * main CREATE TABLE command. @@ -12695,6 +13668,28 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) } } + /* + * dump properties we only have ALTER TABLE syntax for + */ + if ((tbinfo->relkind == RELKIND_RELATION || tbinfo->relkind == RELKIND_MATVIEW) && + tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) + { + if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX) + { + /* nothing to do, will be set when the index is dumped */ + } + else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING) + { + appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n", + fmtId(tbinfo->dobj.name)); + } + else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL) + { + appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n", + fmtId(tbinfo->dobj.name)); + } + } + if (binary_upgrade) binary_upgrade_extension_member(q, &tbinfo->dobj, labelq->data); @@ -12704,9 +13699,10 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace, tbinfo->rolname, (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false, - reltypename, SECTION_PRE_DATA, + reltypename, + tbinfo->postponed_def ? SECTION_POST_DATA : SECTION_PRE_DATA, q->data, delq->data, NULL, - tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, + NULL, 0, NULL, NULL); @@ -12727,7 +13723,6 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) dumpTableConstraintComment(fout, constr); } - destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); destroyPQExpBuffer(labelq); @@ -12778,7 +13773,7 @@ dumpAttrDef(Archive *fout, AttrDefInfo *adinfo) tbinfo->rolname, false, "DEFAULT", SECTION_PRE_DATA, q->data, delq->data, NULL, - adinfo->dobj.dependencies, adinfo->dobj.nDeps, + NULL, 0, NULL, NULL); destroyPQExpBuffer(q); @@ -12827,6 +13822,7 @@ static void dumpIndex(Archive *fout, IndxInfo *indxinfo) { TableInfo *tbinfo = indxinfo->indextable; + bool is_constraint = (indxinfo->indexconstraint != 0); PQExpBuffer q; PQExpBuffer delq; PQExpBuffer labelq; @@ -12844,9 +13840,11 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) /* * If there's an associated constraint, don't dump the index per se, but * do dump any comment for it. (This is safe because dependency ordering - * will have ensured the constraint is emitted first.) + * will have ensured the constraint is emitted first.) Note that the + * emitted comment has to be shown as depending on the constraint, not + * the index, in such cases. */ - if (indxinfo->indexconstraint == 0) + if (!is_constraint) { if (binary_upgrade) binary_upgrade_set_pg_class_oids(fout, q, @@ -12864,6 +13862,15 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) fmtId(indxinfo->dobj.name)); } + /* If the index defines identity, we need to record that. */ + if (indxinfo->indisreplident) + { + appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING", + fmtId(tbinfo->dobj.name)); + appendPQExpBuffer(q, " INDEX %s;\n", + fmtId(indxinfo->dobj.name)); + } + /* * DROP must be fully qualified in case same name appears in * pg_catalog @@ -12880,7 +13887,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) tbinfo->rolname, false, "INDEX", SECTION_POST_DATA, q->data, delq->data, NULL, - indxinfo->dobj.dependencies, indxinfo->dobj.nDeps, + NULL, 0, NULL, NULL); } @@ -12888,7 +13895,9 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) dumpComment(fout, labelq->data, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, - indxinfo->dobj.catId, 0, indxinfo->dobj.dumpId); + indxinfo->dobj.catId, 0, + is_constraint ? indxinfo->indexconstraint : + indxinfo->dobj.dumpId); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); @@ -12959,19 +13968,19 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) fmtId(attname)); } - appendPQExpBuffer(q, ")"); + appendPQExpBufferChar(q, ')'); if (indxinfo->options && strlen(indxinfo->options) > 0) appendPQExpBuffer(q, " WITH (%s)", indxinfo->options); if (coninfo->condeferrable) { - appendPQExpBuffer(q, " DEFERRABLE"); + appendPQExpBufferStr(q, " DEFERRABLE"); if (coninfo->condeferred) - appendPQExpBuffer(q, " INITIALLY DEFERRED"); + appendPQExpBufferStr(q, " INITIALLY DEFERRED"); } - appendPQExpBuffer(q, ";\n"); + appendPQExpBufferStr(q, ";\n"); } /* If the index is clustered, we need to record that. */ @@ -13001,7 +14010,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) tbinfo->rolname, false, "CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, - coninfo->dobj.dependencies, coninfo->dobj.nDeps, + NULL, 0, NULL, NULL); } else if (coninfo->contype == 'f') @@ -13034,7 +14043,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) tbinfo->rolname, false, "FK CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, - coninfo->dobj.dependencies, coninfo->dobj.nDeps, + NULL, 0, NULL, NULL); } else if (coninfo->contype == 'c' && tbinfo) @@ -13044,9 +14053,9 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) /* Ignore if not to be dumped separately */ if (coninfo->separate) { - /* add ONLY if we do not want it to propagate to children */ - appendPQExpBuffer(q, "ALTER TABLE %s %s\n", - coninfo->conisonly ? "ONLY" : "", fmtId(tbinfo->dobj.name)); + /* not ONLY since we want it to propagate to children */ + appendPQExpBuffer(q, "ALTER TABLE %s\n", + fmtId(tbinfo->dobj.name)); appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n", fmtId(coninfo->dobj.name), coninfo->condef); @@ -13069,7 +14078,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) tbinfo->rolname, false, "CHECK CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, - coninfo->dobj.dependencies, coninfo->dobj.nDeps, + NULL, 0, NULL, NULL); } } @@ -13105,7 +14114,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) tyinfo->rolname, false, "CHECK CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, - coninfo->dobj.dependencies, coninfo->dobj.nDeps, + NULL, 0, NULL, NULL); } } @@ -13164,7 +14173,7 @@ findLastBuiltinOid_V71(Archive *fout, const char *dbname) PQExpBuffer query = createPQExpBuffer(); resetPQExpBuffer(query); - appendPQExpBuffer(query, "SELECT datlastsysoid from pg_database where datname = "); + appendPQExpBufferStr(query, "SELECT datlastsysoid from pg_database where datname = "); appendStringLiteralAH(query, dbname, fout); res = ExecuteSqlQueryForSingleRow(fout, query->data); @@ -13189,26 +14198,28 @@ findLastBuiltinOid_V70(Archive *fout) int last_oid; res = ExecuteSqlQueryForSingleRow(fout, - "SELECT oid FROM pg_class WHERE relname = 'pg_indexes'"); + "SELECT oid FROM pg_class WHERE relname = 'pg_indexes'"); last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid"))); PQclear(res); return last_oid; } +/* + * dumpSequence + * write the declaration (not data) of one user-defined sequence + */ static void dumpSequence(Archive *fout, TableInfo *tbinfo) { PGresult *res; char *startv, - *last, *incby, *maxv = NULL, *minv = NULL, *cache; char bufm[100], bufx[100]; - bool cycled, - called; + bool cycled; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -13223,7 +14234,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, last_value, increment_by, " + "start_value, increment_by, " "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " " WHEN increment_by < 0 AND max_value = -1 THEN NULL " " ELSE max_value " @@ -13232,7 +14243,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) " WHEN increment_by < 0 AND min_value = %s THEN NULL " " ELSE min_value " "END AS min_value, " - "cache_value, is_cycled, is_called from %s", + "cache_value, is_cycled FROM %s", bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -13240,7 +14251,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, last_value, increment_by, " + "0 AS start_value, increment_by, " "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " " WHEN increment_by < 0 AND max_value = -1 THEN NULL " " ELSE max_value " @@ -13249,7 +14260,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) " WHEN increment_by < 0 AND min_value = %s THEN NULL " " ELSE min_value " "END AS min_value, " - "cache_value, is_cycled, is_called from %s", + "cache_value, is_cycled FROM %s", bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -13276,165 +14287,120 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) #endif startv = PQgetvalue(res, 0, 1); - last = PQgetvalue(res, 0, 2); - incby = PQgetvalue(res, 0, 3); + incby = PQgetvalue(res, 0, 2); + if (!PQgetisnull(res, 0, 3)) + maxv = PQgetvalue(res, 0, 3); if (!PQgetisnull(res, 0, 4)) - maxv = PQgetvalue(res, 0, 4); - if (!PQgetisnull(res, 0, 5)) - minv = PQgetvalue(res, 0, 5); - cache = PQgetvalue(res, 0, 6); - cycled = (strcmp(PQgetvalue(res, 0, 7), "t") == 0); - called = (strcmp(PQgetvalue(res, 0, 8), "t") == 0); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); /* - * The logic we use for restoring sequences is as follows: - * - * Add a CREATE SEQUENCE statement as part of a "schema" dump (use - * last_val for start if called is false, else use min_val for start_val). - * Also, if the sequence is owned by a column, add an ALTER SEQUENCE OWNED - * BY command for it. - * - * Add a 'SETVAL(seq, last_val, iscalled)' as part of a "data" dump. + * DROP must be fully qualified in case same name appears in pg_catalog */ - if (!dataOnly) - { - /* - * DROP must be fully qualified in case same name appears in - * pg_catalog - */ - appendPQExpBuffer(delqry, "DROP SEQUENCE %s.", - fmtId(tbinfo->dobj.namespace->dobj.name)); - appendPQExpBuffer(delqry, "%s;\n", - fmtId(tbinfo->dobj.name)); + appendPQExpBuffer(delqry, "DROP SEQUENCE %s.", + fmtId(tbinfo->dobj.namespace->dobj.name)); + appendPQExpBuffer(delqry, "%s;\n", + fmtId(tbinfo->dobj.name)); - resetPQExpBuffer(query); + resetPQExpBuffer(query); - if (binary_upgrade) - { - binary_upgrade_set_pg_class_oids(fout, query, - tbinfo->dobj.catId.oid, false); - binary_upgrade_set_type_oids_by_rel_oid(fout, query, - tbinfo->dobj.catId.oid); - } + if (binary_upgrade) + { + binary_upgrade_set_pg_class_oids(fout, query, + tbinfo->dobj.catId.oid, false); + binary_upgrade_set_type_oids_by_rel_oid(fout, query, + tbinfo->dobj.catId.oid); + } - appendPQExpBuffer(query, - "CREATE SEQUENCE %s\n", - fmtId(tbinfo->dobj.name)); + appendPQExpBuffer(query, + "CREATE SEQUENCE %s\n", + fmtId(tbinfo->dobj.name)); - if (fout->remoteVersion >= 80400) - appendPQExpBuffer(query, " START WITH %s\n", startv); - else - { - /* - * Versions before 8.4 did not remember the true start value. If - * is_called is false then the sequence has never been incremented - * so we can use last_val. Otherwise punt and let it default. - */ - if (!called) - appendPQExpBuffer(query, " START WITH %s\n", last); - } + if (fout->remoteVersion >= 80400) + appendPQExpBuffer(query, " START WITH %s\n", startv); - appendPQExpBuffer(query, " INCREMENT BY %s\n", incby); + appendPQExpBuffer(query, " INCREMENT BY %s\n", incby); - if (minv) - appendPQExpBuffer(query, " MINVALUE %s\n", minv); - else - appendPQExpBuffer(query, " NO MINVALUE\n"); + if (minv) + appendPQExpBuffer(query, " MINVALUE %s\n", minv); + else + appendPQExpBufferStr(query, " NO MINVALUE\n"); - if (maxv) - appendPQExpBuffer(query, " MAXVALUE %s\n", maxv); - else - appendPQExpBuffer(query, " NO MAXVALUE\n"); + if (maxv) + appendPQExpBuffer(query, " MAXVALUE %s\n", maxv); + else + appendPQExpBufferStr(query, " NO MAXVALUE\n"); - appendPQExpBuffer(query, - " CACHE %s%s", - cache, (cycled ? "\n CYCLE" : "")); + appendPQExpBuffer(query, + " CACHE %s%s", + cache, (cycled ? "\n CYCLE" : "")); - appendPQExpBuffer(query, ";\n"); + appendPQExpBufferStr(query, ";\n"); - appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name)); + appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name)); - /* binary_upgrade: no need to clear TOAST table oid */ + /* binary_upgrade: no need to clear TOAST table oid */ - if (binary_upgrade) - binary_upgrade_extension_member(query, &tbinfo->dobj, - labelq->data); + if (binary_upgrade) + binary_upgrade_extension_member(query, &tbinfo->dobj, + labelq->data); - ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, - tbinfo->dobj.name, - tbinfo->dobj.namespace->dobj.name, - NULL, - tbinfo->rolname, - false, "SEQUENCE", SECTION_PRE_DATA, - query->data, delqry->data, NULL, - tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, - NULL, NULL); + ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, + tbinfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + NULL, + tbinfo->rolname, + false, "SEQUENCE", SECTION_PRE_DATA, + query->data, delqry->data, NULL, + NULL, 0, + NULL, NULL); - /* - * If the sequence is owned by a table column, emit the ALTER for it - * as a separate TOC entry immediately following the sequence's own - * entry. It's OK to do this rather than using full sorting logic, - * because the dependency that tells us it's owned will have forced - * the table to be created first. We can't just include the ALTER in - * the TOC entry because it will fail if we haven't reassigned the - * sequence owner to match the table's owner. - * - * We need not schema-qualify the table reference because both - * sequence and table must be in the same schema. - */ - if (OidIsValid(tbinfo->owning_tab)) - { - TableInfo *owning_tab = findTableByOid(tbinfo->owning_tab); + /* + * If the sequence is owned by a table column, emit the ALTER for it as a + * separate TOC entry immediately following the sequence's own entry. It's + * OK to do this rather than using full sorting logic, because the + * dependency that tells us it's owned will have forced the table to be + * created first. We can't just include the ALTER in the TOC entry + * because it will fail if we haven't reassigned the sequence owner to + * match the table's owner. + * + * We need not schema-qualify the table reference because both sequence + * and table must be in the same schema. + */ + if (OidIsValid(tbinfo->owning_tab)) + { + TableInfo *owning_tab = findTableByOid(tbinfo->owning_tab); - if (owning_tab && owning_tab->dobj.dump) - { - resetPQExpBuffer(query); - appendPQExpBuffer(query, "ALTER SEQUENCE %s", - fmtId(tbinfo->dobj.name)); - appendPQExpBuffer(query, " OWNED BY %s", - fmtId(owning_tab->dobj.name)); - appendPQExpBuffer(query, ".%s;\n", + if (owning_tab && owning_tab->dobj.dump) + { + resetPQExpBuffer(query); + appendPQExpBuffer(query, "ALTER SEQUENCE %s", + fmtId(tbinfo->dobj.name)); + appendPQExpBuffer(query, " OWNED BY %s", + fmtId(owning_tab->dobj.name)); + appendPQExpBuffer(query, ".%s;\n", fmtId(owning_tab->attnames[tbinfo->owning_col - 1])); - ArchiveEntry(fout, nilCatalogId, createDumpId(), - tbinfo->dobj.name, - tbinfo->dobj.namespace->dobj.name, - NULL, - tbinfo->rolname, - false, "SEQUENCE OWNED BY", SECTION_PRE_DATA, - query->data, "", NULL, - &(tbinfo->dobj.dumpId), 1, - NULL, NULL); - } + ArchiveEntry(fout, nilCatalogId, createDumpId(), + tbinfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + NULL, + tbinfo->rolname, + false, "SEQUENCE OWNED BY", SECTION_PRE_DATA, + query->data, "", NULL, + &(tbinfo->dobj.dumpId), 1, + NULL, NULL); } - - /* Dump Sequence Comments and Security Labels */ - dumpComment(fout, labelq->data, - tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, - tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); - dumpSecLabel(fout, labelq->data, - tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, - tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); } - if (!schemaOnly) - { - resetPQExpBuffer(query); - appendPQExpBuffer(query, "SELECT pg_catalog.setval("); - appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout); - appendPQExpBuffer(query, ", %s, %s);\n", - last, (called ? "true" : "false")); - - ArchiveEntry(fout, nilCatalogId, createDumpId(), - tbinfo->dobj.name, - tbinfo->dobj.namespace->dobj.name, - NULL, - tbinfo->rolname, - false, "SEQUENCE SET", SECTION_PRE_DATA, - query->data, "", NULL, - &(tbinfo->dobj.dumpId), 1, - NULL, NULL); - } + /* Dump Sequence Comments and Security Labels */ + dumpComment(fout, labelq->data, + tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, + tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); + dumpSecLabel(fout, labelq->data, + tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, + tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); PQclear(res); @@ -13443,6 +14409,65 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) destroyPQExpBuffer(labelq); } +/* + * dumpSequenceData + * write the data of one user-defined sequence + */ +static void +dumpSequenceData(Archive *fout, TableDataInfo *tdinfo) +{ + TableInfo *tbinfo = tdinfo->tdtable; + PGresult *res; + char *last; + bool called; + PQExpBuffer query = createPQExpBuffer(); + + /* Make sure we are in proper schema */ + selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); + + appendPQExpBuffer(query, + "SELECT last_value, is_called FROM %s", + fmtId(tbinfo->dobj.name)); + + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + + if (PQntuples(res) != 1) + { + write_msg(NULL, ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)\n", + "query to get data of sequence \"%s\" returned %d rows (expected 1)\n", + PQntuples(res)), + tbinfo->dobj.name, PQntuples(res)); + exit_nicely(1); + } + + last = PQgetvalue(res, 0, 0); + called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0); + + resetPQExpBuffer(query); + appendPQExpBufferStr(query, "SELECT pg_catalog.setval("); + appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout); + appendPQExpBuffer(query, ", %s, %s);\n", + last, (called ? "true" : "false")); + + ArchiveEntry(fout, nilCatalogId, createDumpId(), + tbinfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + NULL, + tbinfo->rolname, + false, "SEQUENCE SET", SECTION_DATA, + query->data, "", NULL, + &(tbinfo->dobj.dumpId), 1, + NULL, NULL); + + PQclear(res); + + destroyPQExpBuffer(query); +} + +/* + * dumpTrigger + * write the declaration of one user-defined table trigger + */ static void dumpTrigger(Archive *fout, TriggerInfo *tginfo) { @@ -13455,6 +14480,10 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) const char *p; int findx; + /* + * we needn't check dobj.dump because TriggerInfo wouldn't have been + * created in the first place for non-dumpable triggers + */ if (dataOnly) return; @@ -13480,23 +14509,23 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) { if (tginfo->tgisconstraint) { - appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER "); + appendPQExpBufferStr(query, "CREATE CONSTRAINT TRIGGER "); appendPQExpBufferStr(query, fmtId(tginfo->tgconstrname)); } else { - appendPQExpBuffer(query, "CREATE TRIGGER "); + appendPQExpBufferStr(query, "CREATE TRIGGER "); appendPQExpBufferStr(query, fmtId(tginfo->dobj.name)); } - appendPQExpBuffer(query, "\n "); + appendPQExpBufferStr(query, "\n "); /* Trigger type */ if (TRIGGER_FOR_BEFORE(tginfo->tgtype)) - appendPQExpBuffer(query, "BEFORE"); + appendPQExpBufferStr(query, "BEFORE"); else if (TRIGGER_FOR_AFTER(tginfo->tgtype)) - appendPQExpBuffer(query, "AFTER"); + appendPQExpBufferStr(query, "AFTER"); else if (TRIGGER_FOR_INSTEAD(tginfo->tgtype)) - appendPQExpBuffer(query, "INSTEAD OF"); + appendPQExpBufferStr(query, "INSTEAD OF"); else { write_msg(NULL, "unexpected tgtype value: %d\n", tginfo->tgtype); @@ -13506,31 +14535,31 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) findx = 0; if (TRIGGER_FOR_INSERT(tginfo->tgtype)) { - appendPQExpBuffer(query, " INSERT"); + appendPQExpBufferStr(query, " INSERT"); findx++; } if (TRIGGER_FOR_DELETE(tginfo->tgtype)) { if (findx > 0) - appendPQExpBuffer(query, " OR DELETE"); + appendPQExpBufferStr(query, " OR DELETE"); else - appendPQExpBuffer(query, " DELETE"); + appendPQExpBufferStr(query, " DELETE"); findx++; } if (TRIGGER_FOR_UPDATE(tginfo->tgtype)) { if (findx > 0) - appendPQExpBuffer(query, " OR UPDATE"); + appendPQExpBufferStr(query, " OR UPDATE"); else - appendPQExpBuffer(query, " UPDATE"); + appendPQExpBufferStr(query, " UPDATE"); findx++; } if (TRIGGER_FOR_TRUNCATE(tginfo->tgtype)) { if (findx > 0) - appendPQExpBuffer(query, " OR TRUNCATE"); + appendPQExpBufferStr(query, " OR TRUNCATE"); else - appendPQExpBuffer(query, " TRUNCATE"); + appendPQExpBufferStr(query, " TRUNCATE"); findx++; } appendPQExpBuffer(query, " ON %s\n", @@ -13549,18 +14578,18 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) fmtId(tginfo->tgconstrrelname)); } if (!tginfo->tgdeferrable) - appendPQExpBuffer(query, "NOT "); - appendPQExpBuffer(query, "DEFERRABLE INITIALLY "); + appendPQExpBufferStr(query, "NOT "); + appendPQExpBufferStr(query, "DEFERRABLE INITIALLY "); if (tginfo->tginitdeferred) - appendPQExpBuffer(query, "DEFERRED\n"); + appendPQExpBufferStr(query, "DEFERRED\n"); else - appendPQExpBuffer(query, "IMMEDIATE\n"); + appendPQExpBufferStr(query, "IMMEDIATE\n"); } if (TRIGGER_FOR_ROW(tginfo->tgtype)) - appendPQExpBuffer(query, " FOR EACH ROW\n "); + appendPQExpBufferStr(query, " FOR EACH ROW\n "); else - appendPQExpBuffer(query, " FOR EACH STATEMENT\n "); + appendPQExpBufferStr(query, " FOR EACH STATEMENT\n "); /* In 7.3, result of regproc is already quoted */ if (fout->remoteVersion >= 70300) @@ -13589,12 +14618,12 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) } if (findx > 0) - appendPQExpBuffer(query, ", "); + appendPQExpBufferStr(query, ", "); appendStringLiteralAH(query, p, fout); p += tlen + 1; } free(tgargs); - appendPQExpBuffer(query, ");\n"); + appendPQExpBufferStr(query, ");\n"); } if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O') @@ -13605,16 +14634,16 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) { case 'D': case 'f': - appendPQExpBuffer(query, "DISABLE"); + appendPQExpBufferStr(query, "DISABLE"); break; case 'A': - appendPQExpBuffer(query, "ENABLE ALWAYS"); + appendPQExpBufferStr(query, "ENABLE ALWAYS"); break; case 'R': - appendPQExpBuffer(query, "ENABLE REPLICA"); + appendPQExpBufferStr(query, "ENABLE REPLICA"); break; default: - appendPQExpBuffer(query, "ENABLE"); + appendPQExpBufferStr(query, "ENABLE"); break; } appendPQExpBuffer(query, " TRIGGER %s;\n", @@ -13633,7 +14662,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) tbinfo->rolname, false, "TRIGGER", SECTION_POST_DATA, query->data, delqry->data, NULL, - tginfo->dobj.dependencies, tginfo->dobj.nDeps, + NULL, 0, NULL, NULL); dumpComment(fout, labelq->data, @@ -13645,6 +14674,77 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) destroyPQExpBuffer(labelq); } +/* + * dumpEventTrigger + * write the declaration of one user-defined event trigger + */ +static void +dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo) +{ + PQExpBuffer query; + PQExpBuffer labelq; + + /* Skip if not to be dumped */ + if (!evtinfo->dobj.dump || dataOnly) + return; + + query = createPQExpBuffer(); + labelq = createPQExpBuffer(); + + appendPQExpBufferStr(query, "CREATE EVENT TRIGGER "); + appendPQExpBufferStr(query, fmtId(evtinfo->dobj.name)); + appendPQExpBufferStr(query, " ON "); + appendPQExpBufferStr(query, fmtId(evtinfo->evtevent)); + appendPQExpBufferStr(query, " "); + + if (strcmp("", evtinfo->evttags) != 0) + { + appendPQExpBufferStr(query, "\n WHEN TAG IN ("); + appendPQExpBufferStr(query, evtinfo->evttags); + appendPQExpBufferStr(query, ") "); + } + + appendPQExpBufferStr(query, "\n EXECUTE PROCEDURE "); + appendPQExpBufferStr(query, evtinfo->evtfname); + appendPQExpBufferStr(query, "();\n"); + + if (evtinfo->evtenabled != 'O') + { + appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ", + fmtId(evtinfo->dobj.name)); + switch (evtinfo->evtenabled) + { + case 'D': + appendPQExpBufferStr(query, "DISABLE"); + break; + case 'A': + appendPQExpBufferStr(query, "ENABLE ALWAYS"); + break; + case 'R': + appendPQExpBufferStr(query, "ENABLE REPLICA"); + break; + default: + appendPQExpBufferStr(query, "ENABLE"); + break; + } + appendPQExpBufferStr(query, ";\n"); + } + appendPQExpBuffer(labelq, "EVENT TRIGGER %s ", + fmtId(evtinfo->dobj.name)); + + ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId, + evtinfo->dobj.name, NULL, NULL, evtinfo->evtowner, false, + "EVENT TRIGGER", SECTION_POST_DATA, + query->data, "", NULL, NULL, 0, NULL, NULL); + + dumpComment(fout, labelq->data, + NULL, NULL, + evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId); + + destroyPQExpBuffer(query); + destroyPQExpBuffer(labelq); +} + /* * dumpRule * Dump a rule @@ -13711,10 +14811,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) */ if (rinfo->ev_enabled != 'O') { - appendPQExpBuffer(cmd, "ALTER TABLE %s.", - fmtId(tbinfo->dobj.namespace->dobj.name)); - appendPQExpBuffer(cmd, "%s ", - fmtId(tbinfo->dobj.name)); + appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtId(tbinfo->dobj.name)); switch (rinfo->ev_enabled) { case 'A': @@ -13732,6 +14829,16 @@ dumpRule(Archive *fout, RuleInfo *rinfo) } } + /* + * Apply view's reloptions when its ON SELECT rule is separate. + */ + if (rinfo->reloptions && strlen(rinfo->reloptions) > 0) + { + appendPQExpBuffer(cmd, "ALTER VIEW %s SET (%s);\n", + fmtId(tbinfo->dobj.name), + rinfo->reloptions); + } + /* * DROP must be fully qualified in case same name appears in pg_catalog */ @@ -13754,7 +14861,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) tbinfo->rolname, false, "RULE", SECTION_POST_DATA, cmd->data, delcmd->data, NULL, - rinfo->dobj.dependencies, rinfo->dobj.nDeps, + NULL, 0, NULL, NULL); /* Dump rule comments */ @@ -13799,12 +14906,12 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], query = createPQExpBuffer(); /* refclassid constraint is redundant but may speed the search */ - appendPQExpBuffer(query, "SELECT " - "classid, objid, refclassid, refobjid " - "FROM pg_depend " - "WHERE refclassid = 'pg_extension'::regclass " - "AND deptype = 'e' " - "ORDER BY 3,4"); + appendPQExpBufferStr(query, "SELECT " + "classid, objid, refclassid, refobjid " + "FROM pg_depend " + "WHERE refclassid = 'pg_extension'::regclass " + "AND deptype = 'e' " + "ORDER BY 3,4"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -13900,10 +15007,6 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], int nconfigitems; int nconditionitems; - /* Tables of not-to-be-dumped extensions shouldn't be dumped */ - if (!curext->dobj.dump) - continue; - if (parsePGArray(extconfig, &extconfigarray, &nconfigitems) && parsePGArray(extcondition, &extconditionarray, &nconditionitems) && nconfigitems == nconditionitems) @@ -13913,21 +15016,54 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], for (j = 0; j < nconfigitems; j++) { TableInfo *configtbl; + Oid configtbloid = atooid(extconfigarray[j]); + bool dumpobj = curext->dobj.dump; - configtbl = findTableByOid(atooid(extconfigarray[j])); + configtbl = findTableByOid(configtbloid); if (configtbl == NULL) continue; /* - * Note: config tables are dumped without OIDs regardless - * of the --oids setting. This is because row filtering - * conditions aren't compatible with dumping OIDs. + * Tables of not-to-be-dumped extensions shouldn't be dumped + * unless the table or its schema is explicitly included */ - makeTableDataInfo(configtbl, false); - if (configtbl->dataObj != NULL) + if (!curext->dobj.dump) + { + /* check table explicitly requested */ + if (table_include_oids.head != NULL && + simple_oid_list_member(&table_include_oids, + configtbloid)) + dumpobj = true; + + /* check table's schema explicitly requested */ + if (configtbl->dobj.namespace->dobj.dump) + dumpobj = true; + } + + /* check table excluded by an exclusion switch */ + if (table_exclude_oids.head != NULL && + simple_oid_list_member(&table_exclude_oids, + configtbloid)) + dumpobj = false; + + /* check schema excluded by an exclusion switch */ + if (simple_oid_list_member(&schema_exclude_oids, + configtbl->dobj.namespace->dobj.catId.oid)) + dumpobj = false; + + if (dumpobj) { - if (strlen(extconditionarray[j]) > 0) - configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]); + /* + * Note: config tables are dumped without OIDs regardless + * of the --oids setting. This is because row filtering + * conditions aren't compatible with dumping OIDs. + */ + makeTableDataInfo(configtbl, false); + if (configtbl->dataObj != NULL) + { + if (strlen(extconditionarray[j]) > 0) + configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]); + } } } } @@ -13974,11 +15110,11 @@ getDependencies(Archive *fout) * PIN dependencies aren't interesting, and EXTENSION dependencies were * already processed by getExtensionMembership. */ - appendPQExpBuffer(query, "SELECT " - "classid, objid, refclassid, refobjid, deptype " - "FROM pg_depend " - "WHERE deptype != 'p' AND deptype != 'e' " - "ORDER BY 1,2"); + appendPQExpBufferStr(query, "SELECT " + "classid, objid, refclassid, refobjid, deptype " + "FROM pg_depend " + "WHERE deptype != 'p' AND deptype != 'e' " + "ORDER BY 1,2"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -14060,6 +15196,233 @@ getDependencies(Archive *fout) } +/* + * createBoundaryObjects - create dummy DumpableObjects to represent + * dump section boundaries. + */ +static DumpableObject * +createBoundaryObjects(void) +{ + DumpableObject *dobjs; + + dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject)); + + dobjs[0].objType = DO_PRE_DATA_BOUNDARY; + dobjs[0].catId = nilCatalogId; + AssignDumpId(dobjs + 0); + dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY"); + + dobjs[1].objType = DO_POST_DATA_BOUNDARY; + dobjs[1].catId = nilCatalogId; + AssignDumpId(dobjs + 1); + dobjs[1].name = pg_strdup("POST-DATA BOUNDARY"); + + return dobjs; +} + +/* + * addBoundaryDependencies - add dependencies as needed to enforce the dump + * section boundaries. + */ +static void +addBoundaryDependencies(DumpableObject **dobjs, int numObjs, + DumpableObject *boundaryObjs) +{ + DumpableObject *preDataBound = boundaryObjs + 0; + DumpableObject *postDataBound = boundaryObjs + 1; + int i; + + for (i = 0; i < numObjs; i++) + { + DumpableObject *dobj = dobjs[i]; + + /* + * The classification of object types here must match the SECTION_xxx + * values assigned during subsequent ArchiveEntry calls! + */ + switch (dobj->objType) + { + case DO_NAMESPACE: + case DO_EXTENSION: + case DO_TYPE: + case DO_SHELL_TYPE: + case DO_FUNC: + case DO_AGG: + case DO_OPERATOR: + case DO_OPCLASS: + case DO_OPFAMILY: + case DO_COLLATION: + case DO_CONVERSION: + case DO_TABLE: + case DO_ATTRDEF: + case DO_PROCLANG: + case DO_CAST: + case DO_DUMMY_TYPE: + case DO_TSPARSER: + case DO_TSDICT: + case DO_TSTEMPLATE: + case DO_TSCONFIG: + case DO_FDW: + case DO_FOREIGN_SERVER: + case DO_BLOB: + /* Pre-data objects: must come before the pre-data boundary */ + addObjectDependency(preDataBound, dobj->dumpId); + break; + case DO_TABLE_DATA: + case DO_BLOB_DATA: + /* Data objects: must come between the boundaries */ + addObjectDependency(dobj, preDataBound->dumpId); + addObjectDependency(postDataBound, dobj->dumpId); + break; + case DO_INDEX: + case DO_REFRESH_MATVIEW: + case DO_TRIGGER: + case DO_EVENT_TRIGGER: + case DO_DEFAULT_ACL: + /* Post-data objects: must come after the post-data boundary */ + addObjectDependency(dobj, postDataBound->dumpId); + break; + case DO_RULE: + /* Rules are post-data, but only if dumped separately */ + if (((RuleInfo *) dobj)->separate) + addObjectDependency(dobj, postDataBound->dumpId); + break; + case DO_CONSTRAINT: + case DO_FK_CONSTRAINT: + /* Constraints are post-data, but only if dumped separately */ + if (((ConstraintInfo *) dobj)->separate) + addObjectDependency(dobj, postDataBound->dumpId); + break; + case DO_PRE_DATA_BOUNDARY: + /* nothing to do */ + break; + case DO_POST_DATA_BOUNDARY: + /* must come after the pre-data boundary */ + addObjectDependency(dobj, preDataBound->dumpId); + break; + } + } +} + + +/* + * BuildArchiveDependencies - create dependency data for archive TOC entries + * + * The raw dependency data obtained by getDependencies() is not terribly + * useful in an archive dump, because in many cases there are dependency + * chains linking through objects that don't appear explicitly in the dump. + * For example, a view will depend on its _RETURN rule while the _RETURN rule + * will depend on other objects --- but the rule will not appear as a separate + * object in the dump. We need to adjust the view's dependencies to include + * whatever the rule depends on that is included in the dump. + * + * Just to make things more complicated, there are also "special" dependencies + * such as the dependency of a TABLE DATA item on its TABLE, which we must + * not rearrange because pg_restore knows that TABLE DATA only depends on + * its table. In these cases we must leave the dependencies strictly as-is + * even if they refer to not-to-be-dumped objects. + * + * To handle this, the convention is that "special" dependencies are created + * during ArchiveEntry calls, and an archive TOC item that has any such + * entries will not be touched here. Otherwise, we recursively search the + * DumpableObject data structures to build the correct dependencies for each + * archive TOC item. + */ +static void +BuildArchiveDependencies(Archive *fout) +{ + ArchiveHandle *AH = (ArchiveHandle *) fout; + TocEntry *te; + + /* Scan all TOC entries in the archive */ + for (te = AH->toc->next; te != AH->toc; te = te->next) + { + DumpableObject *dobj; + DumpId *dependencies; + int nDeps; + int allocDeps; + + /* No need to process entries that will not be dumped */ + if (te->reqs == 0) + continue; + /* Ignore entries that already have "special" dependencies */ + if (te->nDeps > 0) + continue; + /* Otherwise, look up the item's original DumpableObject, if any */ + dobj = findObjectByDumpId(te->dumpId); + if (dobj == NULL) + continue; + /* No work if it has no dependencies */ + if (dobj->nDeps <= 0) + continue; + /* Set up work array */ + allocDeps = 64; + dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId)); + nDeps = 0; + /* Recursively find all dumpable dependencies */ + findDumpableDependencies(AH, dobj, + &dependencies, &nDeps, &allocDeps); + /* And save 'em ... */ + if (nDeps > 0) + { + dependencies = (DumpId *) pg_realloc(dependencies, + nDeps * sizeof(DumpId)); + te->dependencies = dependencies; + te->nDeps = nDeps; + } + else + free(dependencies); + } +} + +/* Recursive search subroutine for BuildArchiveDependencies */ +static void +findDumpableDependencies(ArchiveHandle *AH, DumpableObject *dobj, + DumpId **dependencies, int *nDeps, int *allocDeps) +{ + int i; + + /* + * Ignore section boundary objects: if we search through them, we'll + * report lots of bogus dependencies. + */ + if (dobj->objType == DO_PRE_DATA_BOUNDARY || + dobj->objType == DO_POST_DATA_BOUNDARY) + return; + + for (i = 0; i < dobj->nDeps; i++) + { + DumpId depid = dobj->dependencies[i]; + + if (TocIDRequired(AH, depid) != 0) + { + /* Object will be dumped, so just reference it as a dependency */ + if (*nDeps >= *allocDeps) + { + *allocDeps *= 2; + *dependencies = (DumpId *) pg_realloc(*dependencies, + *allocDeps * sizeof(DumpId)); + } + (*dependencies)[*nDeps] = depid; + (*nDeps)++; + } + else + { + /* + * Object will not be dumped, so recursively consider its deps. We + * rely on the assumption that sortDumpableObjects already broke + * any dependency loops, else we might recurse infinitely. + */ + DumpableObject *otherdobj = findObjectByDumpId(depid); + + if (otherdobj) + findDumpableDependencies(AH, otherdobj, + dependencies, nDeps, allocDeps); + } + } +} + + /* * selectSourceSchema - make the specified schema the active search path * in the source database. @@ -14071,35 +15434,31 @@ getDependencies(Archive *fout) * * Whenever the selected schema is not pg_catalog, be careful to qualify * references to system catalogs and types in our emitted commands! + * + * This function is called only from selectSourceSchemaOnAH and + * selectSourceSchema. */ static void selectSourceSchema(Archive *fout, const char *schemaName) { - static char *curSchemaName = NULL; PQExpBuffer query; + /* This is checked by the callers already */ + Assert(schemaName != NULL && *schemaName != '\0'); + /* Not relevant if fetching from pre-7.3 DB */ if (fout->remoteVersion < 70300) return; - /* Ignore null schema names */ - if (schemaName == NULL || *schemaName == '\0') - return; - /* Optimize away repeated selection of same schema */ - if (curSchemaName && strcmp(curSchemaName, schemaName) == 0) - return; query = createPQExpBuffer(); appendPQExpBuffer(query, "SET search_path = %s", fmtId(schemaName)); if (strcmp(schemaName, "pg_catalog") != 0) - appendPQExpBuffer(query, ", pg_catalog"); + appendPQExpBufferStr(query, ", pg_catalog"); ExecuteSqlStatement(fout, query->data); destroyPQExpBuffer(query); - if (curSchemaName) - free(curSchemaName); - curSchemaName = pg_strdup(schemaName); } /* @@ -14188,21 +15547,21 @@ myFormatType(const char *typname, int32 typmod) { int len = (typmod - VARHDRSZ); - appendPQExpBuffer(buf, "character"); + appendPQExpBufferStr(buf, "character"); if (len > 1) appendPQExpBuffer(buf, "(%d)", typmod - VARHDRSZ); } else if (strcmp(typname, "varchar") == 0) { - appendPQExpBuffer(buf, "character varying"); + appendPQExpBufferStr(buf, "character varying"); if (typmod != -1) appendPQExpBuffer(buf, "(%d)", typmod - VARHDRSZ); } else if (strcmp(typname, "numeric") == 0) { - appendPQExpBuffer(buf, "numeric"); + appendPQExpBufferStr(buf, "numeric"); if (typmod != -1) { int32 tmp_typmod; @@ -14222,13 +15581,13 @@ myFormatType(const char *typname, int32 typmod) * through with quotes. - thomas 1998-12-13 */ else if (strcmp(typname, "char") == 0) - appendPQExpBuffer(buf, "\"char\""); + appendPQExpBufferStr(buf, "\"char\""); else - appendPQExpBuffer(buf, "%s", fmtId(typname)); + appendPQExpBufferStr(buf, fmtId(typname)); /* Append array qualifier for array types */ if (isarray) - appendPQExpBuffer(buf, "[]"); + appendPQExpBufferStr(buf, "[]"); result = pg_strdup(buf->data); destroyPQExpBuffer(buf); @@ -14236,34 +15595,6 @@ myFormatType(const char *typname, int32 typmod) return result; } -/* - * fmtQualifiedId - convert a qualified name to the proper format for - * the source database. - * - * Like fmtId, use the result before calling again. - */ -static const char * -fmtQualifiedId(Archive *fout, const char *schema, const char *id) -{ - static PQExpBuffer id_return = NULL; - - if (id_return) /* first time through? */ - resetPQExpBuffer(id_return); - else - id_return = createPQExpBuffer(); - - /* Suppress schema name if fetching from pre-7.3 DB */ - if (fout->remoteVersion >= 70300 && schema && *schema) - { - appendPQExpBuffer(id_return, "%s.", - fmtId(schema)); - } - appendPQExpBuffer(id_return, "%s", - fmtId(id)); - - return id_return->data; -} - /* * Return a column list clause for the given relation. * @@ -14271,37 +15602,31 @@ fmtQualifiedId(Archive *fout, const char *schema, const char *id) * "", not an invalid "()" column list. */ static const char * -fmtCopyColumnList(const TableInfo *ti) +fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer) { - static PQExpBuffer q = NULL; int numatts = ti->numatts; char **attnames = ti->attnames; bool *attisdropped = ti->attisdropped; bool needComma; int i; - if (q) /* first time through? */ - resetPQExpBuffer(q); - else - q = createPQExpBuffer(); - - appendPQExpBuffer(q, "("); + appendPQExpBufferChar(buffer, '('); needComma = false; for (i = 0; i < numatts; i++) { if (attisdropped[i]) continue; if (needComma) - appendPQExpBuffer(q, ", "); - appendPQExpBuffer(q, "%s", fmtId(attnames[i])); + appendPQExpBufferStr(buffer, ", "); + appendPQExpBufferStr(buffer, fmtId(attnames[i])); needComma = true; } if (!needComma) return ""; /* no undropped columns */ - appendPQExpBuffer(q, ")"); - return q->data; + appendPQExpBufferChar(buffer, ')'); + return buffer->data; } /* @@ -14321,7 +15646,7 @@ ExecuteSqlQueryForSingleRow(Archive *fout, char *query) exit_horribly(NULL, ngettext("query returned %d row instead of one: %s\n", "query returned %d rows instead of one: %s\n", - ntups), + ntups), ntups, query); return res;