PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_dump.c File Reference
#include "postgres_fe.h"
#include <unistd.h>
#include <ctype.h>
#include <limits.h>
#include "access/attnum.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "catalog/pg_aggregate_d.h"
#include "catalog/pg_am_d.h"
#include "catalog/pg_attribute_d.h"
#include "catalog/pg_authid_d.h"
#include "catalog/pg_cast_d.h"
#include "catalog/pg_class_d.h"
#include "catalog/pg_constraint_d.h"
#include "catalog/pg_default_acl_d.h"
#include "catalog/pg_largeobject_d.h"
#include "catalog/pg_largeobject_metadata_d.h"
#include "catalog/pg_proc_d.h"
#include "catalog/pg_publication_d.h"
#include "catalog/pg_shdepend_d.h"
#include "catalog/pg_subscription_d.h"
#include "catalog/pg_type_d.h"
#include "common/connect.h"
#include "common/int.h"
#include "common/relpath.h"
#include "common/shortest_dec.h"
#include "compress_io.h"
#include "dumputils.h"
#include "fe_utils/option_utils.h"
#include "fe_utils/string_utils.h"
#include "filter.h"
#include "getopt_long.h"
#include "libpq/libpq-fs.h"
#include "parallel.h"
#include "pg_backup_db.h"
#include "pg_backup_utils.h"
#include "pg_dump.h"
#include "statistics/statistics_format.h"
#include "storage/block.h"
Include dependency graph for pg_dump.c:

Go to the source code of this file.

Data Structures

struct  RoleNameItem
 
struct  CommentItem
 
struct  SecLabelItem
 
struct  BinaryUpgradeClassOidItem
 
struct  SequenceItem
 

Macros

#define MAX_ATTR_STATS_RELS   64
 
#define DUMP_DEFAULT_ROWS_PER_INSERT   1
 
#define MAX_BLOBS_PER_ARCHIVE_ENTRY   1000
 
#define fmtQualifiedDumpable(obj)
 

Typedefs

typedef enum SeqType SeqType
 
typedef enum OidOptions OidOptions
 

Enumerations

enum  SeqType { SEQTYPE_SMALLINT , SEQTYPE_INTEGER , SEQTYPE_BIGINT }
 
enum  OidOptions { zeroIsError = 1 , zeroAsStar = 2 , zeroAsNone = 4 }
 

Functions

 StaticAssertDecl (lengthof(SeqTypeNames)==(SEQTYPE_BIGINT+1), "array length mismatch")
 
static void help (const char *progname)
 
static void setup_connection (Archive *AH, const char *dumpencoding, const char *dumpsnapshot, char *use_role)
 
static ArchiveFormat parseArchiveFormat (const char *format, ArchiveMode *mode)
 
static void expand_schema_name_patterns (Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names)
 
static void expand_extension_name_patterns (Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names)
 
static void expand_foreign_server_name_patterns (Archive *fout, SimpleStringList *patterns, SimpleOidList *oids)
 
static void expand_table_name_patterns (Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names, bool with_child_tables)
 
static void prohibit_crossdb_refs (PGconn *conn, const char *dbname, const char *pattern)
 
static NamespaceInfofindNamespace (Oid nsoid)
 
static void dumpTableData (Archive *fout, const TableDataInfo *tdinfo)
 
static void refreshMatViewData (Archive *fout, const TableDataInfo *tdinfo)
 
static const chargetRoleName (const char *roleoid_str)
 
static void collectRoleNames (Archive *fout)
 
static void getAdditionalACLs (Archive *fout)
 
static void dumpCommentExtended (Archive *fout, const char *type, const char *name, const char *namespace, const char *owner, CatalogId catalogId, int subid, DumpId dumpId, const char *initdb_comment)
 
static void dumpComment (Archive *fout, const char *type, const char *name, const char *namespace, const char *owner, CatalogId catalogId, int subid, DumpId dumpId)
 
static int findComments (Oid classoid, Oid objoid, CommentItem **items)
 
static void collectComments (Archive *fout)
 
static void dumpSecLabel (Archive *fout, const char *type, const char *name, const char *namespace, const char *owner, CatalogId catalogId, int subid, DumpId dumpId)
 
static int findSecLabels (Oid classoid, Oid objoid, SecLabelItem **items)
 
static void collectSecLabels (Archive *fout)
 
static void dumpDumpableObject (Archive *fout, DumpableObject *dobj)
 
static void dumpNamespace (Archive *fout, const NamespaceInfo *nspinfo)
 
static void dumpExtension (Archive *fout, const ExtensionInfo *extinfo)
 
static void dumpType (Archive *fout, const TypeInfo *tyinfo)
 
static void dumpBaseType (Archive *fout, const TypeInfo *tyinfo)
 
static void dumpEnumType (Archive *fout, const TypeInfo *tyinfo)
 
static void dumpRangeType (Archive *fout, const TypeInfo *tyinfo)
 
static void dumpUndefinedType (Archive *fout, const TypeInfo *tyinfo)
 
static void dumpDomain (Archive *fout, const TypeInfo *tyinfo)
 
static void dumpCompositeType (Archive *fout, const TypeInfo *tyinfo)
 
static void dumpCompositeTypeColComments (Archive *fout, const TypeInfo *tyinfo, PGresult *res)
 
static void dumpShellType (Archive *fout, const ShellTypeInfo *stinfo)
 
static void dumpProcLang (Archive *fout, const ProcLangInfo *plang)
 
static void dumpFunc (Archive *fout, const FuncInfo *finfo)
 
static void dumpCast (Archive *fout, const CastInfo *cast)
 
static void dumpTransform (Archive *fout, const TransformInfo *transform)
 
static void dumpOpr (Archive *fout, const OprInfo *oprinfo)
 
static void dumpAccessMethod (Archive *fout, const AccessMethodInfo *aminfo)
 
static void dumpOpclass (Archive *fout, const OpclassInfo *opcinfo)
 
static void dumpOpfamily (Archive *fout, const OpfamilyInfo *opfinfo)
 
static void dumpCollation (Archive *fout, const CollInfo *collinfo)
 
static void dumpConversion (Archive *fout, const ConvInfo *convinfo)
 
static void dumpRule (Archive *fout, const RuleInfo *rinfo)
 
static void dumpAgg (Archive *fout, const AggInfo *agginfo)
 
static void dumpTrigger (Archive *fout, const TriggerInfo *tginfo)
 
static void dumpEventTrigger (Archive *fout, const EventTriggerInfo *evtinfo)
 
static void dumpTable (Archive *fout, const TableInfo *tbinfo)
 
static void dumpTableSchema (Archive *fout, const TableInfo *tbinfo)
 
static void dumpTableAttach (Archive *fout, const TableAttachInfo *attachinfo)
 
static void dumpAttrDef (Archive *fout, const AttrDefInfo *adinfo)
 
static void collectSequences (Archive *fout)
 
static void dumpSequence (Archive *fout, const TableInfo *tbinfo)
 
static void dumpSequenceData (Archive *fout, const TableDataInfo *tdinfo)
 
static void dumpIndex (Archive *fout, const IndxInfo *indxinfo)
 
static void dumpIndexAttach (Archive *fout, const IndexAttachInfo *attachinfo)
 
static void dumpStatisticsExt (Archive *fout, const StatsExtInfo *statsextinfo)
 
static void dumpStatisticsExtStats (Archive *fout, const StatsExtInfo *statsextinfo)
 
static void dumpConstraint (Archive *fout, const ConstraintInfo *coninfo)
 
static void dumpTableConstraintComment (Archive *fout, const ConstraintInfo *coninfo)
 
static void dumpTSParser (Archive *fout, const TSParserInfo *prsinfo)
 
static void dumpTSDictionary (Archive *fout, const TSDictInfo *dictinfo)
 
static void dumpTSTemplate (Archive *fout, const TSTemplateInfo *tmplinfo)
 
static void dumpTSConfig (Archive *fout, const TSConfigInfo *cfginfo)
 
static void dumpForeignDataWrapper (Archive *fout, const FdwInfo *fdwinfo)
 
static void dumpForeignServer (Archive *fout, const ForeignServerInfo *srvinfo)
 
static void dumpUserMappings (Archive *fout, const char *servername, const char *namespace, const char *owner, CatalogId catalogId, DumpId dumpId)
 
static void dumpDefaultACL (Archive *fout, const DefaultACLInfo *daclinfo)
 
static DumpId dumpACL (Archive *fout, DumpId objDumpId, DumpId altDumpId, const char *type, const char *name, const char *subname, const char *nspname, const char *tag, const char *owner, const DumpableAcl *dacl)
 
static void getDependencies (Archive *fout)
 
static void BuildArchiveDependencies (Archive *fout)
 
static void findDumpableDependencies (ArchiveHandle *AH, const DumpableObject *dobj, DumpId **dependencies, int *nDeps, int *allocDeps)
 
static DumpableObjectcreateBoundaryObjects (void)
 
static void addBoundaryDependencies (DumpableObject **dobjs, int numObjs, DumpableObject *boundaryObjs)
 
static void addConstrChildIdxDeps (DumpableObject *dobj, const IndxInfo *refidx)
 
static void getDomainConstraints (Archive *fout, TypeInfo *tyinfo)
 
static void getTableData (DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
 
static void makeTableDataInfo (DumpOptions *dopt, TableInfo *tbinfo)
 
static void buildMatViewRefreshDependencies (Archive *fout)
 
static void getTableDataFKConstraints (void)
 
static void determineNotNullFlags (Archive *fout, PGresult *res, int r, TableInfo *tbinfo, int j, int i_notnull_name, int i_notnull_comment, int i_notnull_invalidoid, int i_notnull_noinherit, int i_notnull_islocal, PQExpBuffer *invalidnotnulloids)
 
static charformat_function_arguments (const FuncInfo *finfo, const char *funcargs, bool is_agg)
 
static charformat_function_signature (Archive *fout, const FuncInfo *finfo, bool honor_quotes)
 
static charconvertRegProcReference (const char *proc)
 
static chargetFormattedOperatorName (const char *oproid)
 
static charconvertTSFunction (Archive *fout, Oid funcOid)
 
static const chargetFormattedTypeName (Archive *fout, Oid oid, OidOptions opts)
 
static void getLOs (Archive *fout)
 
static void dumpLO (Archive *fout, const LoInfo *loinfo)
 
static int dumpLOs (Archive *fout, const void *arg)
 
static void dumpPolicy (Archive *fout, const PolicyInfo *polinfo)
 
static void dumpPublication (Archive *fout, const PublicationInfo *pubinfo)
 
static void dumpPublicationTable (Archive *fout, const PublicationRelInfo *pubrinfo)
 
static void dumpSubscription (Archive *fout, const SubscriptionInfo *subinfo)
 
static void dumpSubscriptionTable (Archive *fout, const SubRelInfo *subrinfo)
 
static void dumpDatabase (Archive *fout)
 
static void dumpDatabaseConfig (Archive *AH, PQExpBuffer outbuf, const char *dbname, Oid dboid)
 
static void dumpEncoding (Archive *AH)
 
static void dumpStdStrings (Archive *AH)
 
static void dumpSearchPath (Archive *AH)
 
static void binary_upgrade_set_type_oids_by_type_oid (Archive *fout, PQExpBuffer upgrade_buffer, Oid pg_type_oid, bool force_array_type, bool include_multirange_type)
 
static void binary_upgrade_set_type_oids_by_rel (Archive *fout, PQExpBuffer upgrade_buffer, const TableInfo *tbinfo)
 
static void collectBinaryUpgradeClassOids (Archive *fout)
 
static void binary_upgrade_set_pg_class_oids (Archive *fout, PQExpBuffer upgrade_buffer, Oid pg_class_oid)
 
static void binary_upgrade_extension_member (PQExpBuffer upgrade_buffer, const DumpableObject *dobj, const char *objtype, const char *objname, const char *objnamespace)
 
static const chargetAttrName (int attrnum, const TableInfo *tblInfo)
 
static const charfmtCopyColumnList (const TableInfo *ti, PQExpBuffer buffer)
 
static bool nonemptyReloptions (const char *reloptions)
 
static void appendReloptionsArrayAH (PQExpBuffer buffer, const char *reloptions, const char *prefix, Archive *fout)
 
static charget_synchronized_snapshot (Archive *fout)
 
static void set_restrict_relation_kind (Archive *AH, const char *value)
 
static void setupDumpWorker (Archive *AH)
 
static TableInfogetRootTableInfo (const TableInfo *tbinfo)
 
static bool forcePartitionRootLoad (const TableInfo *tbinfo)
 
static void read_dump_filters (const char *filename, DumpOptions *dopt)
 
int main (int argc, char **argv)
 
static bool checkExtensionMembership (DumpableObject *dobj, Archive *fout)
 
static void selectDumpableNamespace (NamespaceInfo *nsinfo, Archive *fout)
 
static void selectDumpableTable (TableInfo *tbinfo, Archive *fout)
 
static void selectDumpableType (TypeInfo *tyinfo, Archive *fout)
 
static void selectDumpableDefaultACL (DefaultACLInfo *dinfo, DumpOptions *dopt)
 
static void selectDumpableCast (CastInfo *cast, Archive *fout)
 
static void selectDumpableProcLang (ProcLangInfo *plang, Archive *fout)
 
static void selectDumpableAccessMethod (AccessMethodInfo *method, Archive *fout)
 
static void selectDumpableExtension (ExtensionInfo *extinfo, DumpOptions *dopt)
 
static void selectDumpablePublicationObject (DumpableObject *dobj, Archive *fout)
 
static void selectDumpableStatisticsObject (StatsExtInfo *sobj, Archive *fout)
 
static void selectDumpableObject (DumpableObject *dobj, Archive *fout)
 
static int dumpTableData_copy (Archive *fout, const void *dcontext)
 
static int dumpTableData_insert (Archive *fout, const void *dcontext)
 
void getPolicies (Archive *fout, TableInfo tblinfo[], int numTables)
 
void getPublications (Archive *fout)
 
void getPublicationNamespaces (Archive *fout)
 
void getPublicationTables (Archive *fout, TableInfo tblinfo[], int numTables)
 
static void dumpPublicationNamespace (Archive *fout, const PublicationSchemaInfo *pubsinfo)
 
static bool is_superuser (Archive *fout)
 
void getSubscriptions (Archive *fout)
 
void getSubscriptionRelations (Archive *fout)
 
static void append_depends_on_extension (Archive *fout, PQExpBuffer create, const DumpableObject *dobj, const char *catalog, const char *keyword, const char *objname)
 
static Oid get_next_possible_free_pg_type_oid (Archive *fout, PQExpBuffer upgrade_query)
 
static int BinaryUpgradeClassOidItemCmp (const void *p1, const void *p2)
 
void getNamespaces (Archive *fout)
 
ExtensionInfogetExtensions (Archive *fout, int *numExtensions)
 
void getTypes (Archive *fout)
 
void getOperators (Archive *fout)
 
void getCollations (Archive *fout)
 
void getConversions (Archive *fout)
 
void getAccessMethods (Archive *fout)
 
void getOpclasses (Archive *fout)
 
void getOpfamilies (Archive *fout)
 
void getAggregates (Archive *fout)
 
void getFuncs (Archive *fout)
 
static RelStatsInfogetRelationStatistics (Archive *fout, DumpableObject *rel, int32 relpages, char *reltuples, int32 relallvisible, int32 relallfrozen, char relkind, char **indAttNames, int nindAttNames)
 
TableInfogetTables (Archive *fout, int *numTables)
 
void getOwnedSeqs (Archive *fout, TableInfo tblinfo[], int numTables)
 
InhInfogetInherits (Archive *fout, int *numInherits)
 
void getPartitioningInfo (Archive *fout)
 
void getIndexes (Archive *fout, TableInfo tblinfo[], int numTables)
 
void getExtendedStatistics (Archive *fout)
 
void getConstraints (Archive *fout, TableInfo tblinfo[], int numTables)
 
void getRules (Archive *fout)
 
void getTriggers (Archive *fout, TableInfo tblinfo[], int numTables)
 
void getEventTriggers (Archive *fout)
 
void getProcLangs (Archive *fout)
 
void getCasts (Archive *fout)
 
static charget_language_name (Archive *fout, Oid langid)
 
void getTransforms (Archive *fout)
 
void getTableAttrs (Archive *fout, TableInfo *tblinfo, int numTables)
 
bool shouldPrintColumn (const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
 
void getTSParsers (Archive *fout)
 
void getTSDictionaries (Archive *fout)
 
void getTSTemplates (Archive *fout)
 
void getTSConfigurations (Archive *fout)
 
void getForeignDataWrappers (Archive *fout)
 
void getForeignServers (Archive *fout)
 
void getDefaultACLs (Archive *fout)
 
static void appendNamedArgument (PQExpBuffer out, Archive *fout, const char *argname, const char *argtype, const char *argval)
 
static PGresultfetchAttributeStats (Archive *fout)
 
static chardumpRelationStats_dumper (Archive *fout, const void *userArg, const TocEntry *te)
 
static void dumpRelationStats (Archive *fout, const RelStatsInfo *rsinfo)
 
static void dumpTableComment (Archive *fout, const TableInfo *tbinfo, const char *reltypename)
 
static charformat_aggregate_signature (const AggInfo *agginfo, Archive *fout, bool honor_quotes)
 
static void dumpTableSecLabel (Archive *fout, const TableInfo *tbinfo, const char *reltypename)
 
static PQExpBuffer createViewAsClause (Archive *fout, const TableInfo *tbinfo)
 
static PQExpBuffer createDummyViewAsClause (Archive *fout, const TableInfo *tbinfo)
 
static SeqType parse_sequence_type (const char *name)
 
static int SequenceItemCmp (const void *p1, const void *p2)
 
void getExtensionMembership (Archive *fout, ExtensionInfo extinfo[], int numExtensions)
 
void processExtensionTables (Archive *fout, ExtensionInfo extinfo[], int numExtensions)
 

Variables

static const char *const SeqTypeNames []
 
static bool dosync = true
 
static Oid g_last_builtin_oid
 
static int strict_names = 0
 
static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE
 
static SimpleStringList schema_include_patterns = {NULL, NULL}
 
static SimpleOidList schema_include_oids = {NULL, NULL}
 
static SimpleStringList schema_exclude_patterns = {NULL, NULL}
 
static SimpleOidList schema_exclude_oids = {NULL, NULL}
 
static SimpleStringList table_include_patterns = {NULL, NULL}
 
static SimpleStringList table_include_patterns_and_children = {NULL, NULL}
 
static SimpleOidList table_include_oids = {NULL, NULL}
 
static SimpleStringList table_exclude_patterns = {NULL, NULL}
 
static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL}
 
static SimpleOidList table_exclude_oids = {NULL, NULL}
 
static SimpleStringList tabledata_exclude_patterns = {NULL, NULL}
 
static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL}
 
static SimpleOidList tabledata_exclude_oids = {NULL, NULL}
 
static SimpleStringList foreign_servers_include_patterns = {NULL, NULL}
 
static SimpleOidList foreign_servers_include_oids = {NULL, NULL}
 
static SimpleStringList extension_include_patterns = {NULL, NULL}
 
static SimpleOidList extension_include_oids = {NULL, NULL}
 
static SimpleStringList extension_exclude_patterns = {NULL, NULL}
 
static SimpleOidList extension_exclude_oids = {NULL, NULL}
 
static const CatalogId nilCatalogId = {0, 0}
 
static bool have_extra_float_digits = false
 
static int extra_float_digits
 
static RoleNameItemrolenames = NULL
 
static int nrolenames = 0
 
static CommentItemcomments = NULL
 
static int ncomments = 0
 
static SecLabelItemseclabels = NULL
 
static int nseclabels = 0
 
static BinaryUpgradeClassOidItembinaryUpgradeClassOids = NULL
 
static int nbinaryUpgradeClassOids = 0
 
static SequenceItemsequences = NULL
 
static int nsequences = 0
 

Macro Definition Documentation

◆ DUMP_DEFAULT_ROWS_PER_INSERT

#define DUMP_DEFAULT_ROWS_PER_INSERT   1

Definition at line 224 of file pg_dump.c.

◆ fmtQualifiedDumpable

#define fmtQualifiedDumpable (   obj)
Value:
fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
(obj)->dobj.name)
const char * fmtQualifiedId(const char *schema, const char *id)

Definition at line 236 of file pg_dump.c.

418{
419 int c;
420 const char *filename = NULL;
421 const char *format = "p";
422 TableInfo *tblinfo;
423 int numTables;
425 int numObjs;
427 int i;
428 int optindex;
429 RestoreOptions *ropt;
430 Archive *fout; /* the script file */
431 bool g_verbose = false;
432 const char *dumpencoding = NULL;
433 const char *dumpsnapshot = NULL;
434 char *use_role = NULL;
435 int numWorkers = 1;
436 int plainText = 0;
439 pg_compress_specification compression_spec = {0};
440 char *compression_detail = NULL;
441 char *compression_algorithm_str = "none";
442 char *error_detail = NULL;
443 bool user_compression_defined = false;
445 bool data_only = false;
446 bool schema_only = false;
447 bool statistics_only = false;
448 bool with_statistics = false;
449 bool no_data = false;
450 bool no_schema = false;
451 bool no_statistics = false;
452
453 static DumpOptions dopt;
454
455 static struct option long_options[] = {
456 {"data-only", no_argument, NULL, 'a'},
457 {"blobs", no_argument, NULL, 'b'},
458 {"large-objects", no_argument, NULL, 'b'},
459 {"no-blobs", no_argument, NULL, 'B'},
460 {"no-large-objects", no_argument, NULL, 'B'},
461 {"clean", no_argument, NULL, 'c'},
462 {"create", no_argument, NULL, 'C'},
463 {"dbname", required_argument, NULL, 'd'},
464 {"extension", required_argument, NULL, 'e'},
465 {"file", required_argument, NULL, 'f'},
466 {"format", required_argument, NULL, 'F'},
467 {"host", required_argument, NULL, 'h'},
468 {"jobs", 1, NULL, 'j'},
469 {"no-reconnect", no_argument, NULL, 'R'},
470 {"no-owner", no_argument, NULL, 'O'},
471 {"port", required_argument, NULL, 'p'},
472 {"schema", required_argument, NULL, 'n'},
473 {"exclude-schema", required_argument, NULL, 'N'},
474 {"schema-only", no_argument, NULL, 's'},
475 {"superuser", required_argument, NULL, 'S'},
476 {"table", required_argument, NULL, 't'},
477 {"exclude-table", required_argument, NULL, 'T'},
478 {"no-password", no_argument, NULL, 'w'},
479 {"password", no_argument, NULL, 'W'},
480 {"username", required_argument, NULL, 'U'},
481 {"verbose", no_argument, NULL, 'v'},
482 {"no-privileges", no_argument, NULL, 'x'},
483 {"no-acl", no_argument, NULL, 'x'},
484 {"compress", required_argument, NULL, 'Z'},
485 {"encoding", required_argument, NULL, 'E'},
486 {"help", no_argument, NULL, '?'},
487 {"version", no_argument, NULL, 'V'},
488
489 /*
490 * the following options don't have an equivalent short option letter
491 */
492 {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
493 {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
494 {"column-inserts", no_argument, &dopt.column_inserts, 1},
495 {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
496 {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
497 {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
498 {"exclude-table-data", required_argument, NULL, 4},
499 {"extra-float-digits", required_argument, NULL, 8},
500 {"if-exists", no_argument, &dopt.if_exists, 1},
501 {"inserts", no_argument, NULL, 9},
502 {"lock-wait-timeout", required_argument, NULL, 2},
503 {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
504 {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
505 {"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
506 {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
507 {"role", required_argument, NULL, 3},
508 {"section", required_argument, NULL, 5},
509 {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
510 {"snapshot", required_argument, NULL, 6},
511 {"statistics", no_argument, NULL, 22},
512 {"statistics-only", no_argument, NULL, 18},
513 {"strict-names", no_argument, &strict_names, 1},
514 {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
515 {"no-comments", no_argument, &dopt.no_comments, 1},
516 {"no-data", no_argument, NULL, 19},
517 {"no-policies", no_argument, &dopt.no_policies, 1},
518 {"no-publications", no_argument, &dopt.no_publications, 1},
519 {"no-schema", no_argument, NULL, 20},
520 {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
521 {"no-statistics", no_argument, NULL, 21},
522 {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
523 {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
524 {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
525 {"no-sync", no_argument, NULL, 7},
526 {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
527 {"rows-per-insert", required_argument, NULL, 10},
528 {"include-foreign-data", required_argument, NULL, 11},
529 {"table-and-children", required_argument, NULL, 12},
530 {"exclude-table-and-children", required_argument, NULL, 13},
531 {"exclude-table-data-and-children", required_argument, NULL, 14},
532 {"sync-method", required_argument, NULL, 15},
533 {"filter", required_argument, NULL, 16},
534 {"exclude-extension", required_argument, NULL, 17},
535 {"sequence-data", no_argument, &dopt.sequence_data, 1},
536 {"restrict-key", required_argument, NULL, 25},
537
538 {NULL, 0, NULL, 0}
539 };
540
541 pg_logging_init(argv[0]);
543 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
544
545 /*
546 * Initialize what we need for parallel execution, especially for thread
547 * support on Windows.
548 */
550
551 progname = get_progname(argv[0]);
552
553 if (argc > 1)
554 {
555 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
556 {
557 help(progname);
558 exit_nicely(0);
559 }
560 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
561 {
562 puts("pg_dump (PostgreSQL) " PG_VERSION);
563 exit_nicely(0);
564 }
565 }
566
567 InitDumpOptions(&dopt);
568
569 while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
570 long_options, &optindex)) != -1)
571 {
572 switch (c)
573 {
574 case 'a': /* Dump data only */
575 data_only = true;
576 break;
577
578 case 'b': /* Dump LOs */
579 dopt.outputLOs = true;
580 break;
581
582 case 'B': /* Don't dump LOs */
583 dopt.dontOutputLOs = true;
584 break;
585
586 case 'c': /* clean (i.e., drop) schema prior to create */
587 dopt.outputClean = 1;
588 break;
589
590 case 'C': /* Create DB */
591 dopt.outputCreateDB = 1;
592 break;
593
594 case 'd': /* database name */
596 break;
597
598 case 'e': /* include extension(s) */
600 dopt.include_everything = false;
601 break;
602
603 case 'E': /* Dump encoding */
605 break;
606
607 case 'f':
609 break;
610
611 case 'F':
613 break;
614
615 case 'h': /* server host */
617 break;
618
619 case 'j': /* number of dump jobs */
620 if (!option_parse_int(optarg, "-j/--jobs", 1,
622 &numWorkers))
623 exit_nicely(1);
624 break;
625
626 case 'n': /* include schema(s) */
628 dopt.include_everything = false;
629 break;
630
631 case 'N': /* exclude schema(s) */
633 break;
634
635 case 'O': /* Don't reconnect to match owner */
636 dopt.outputNoOwner = 1;
637 break;
638
639 case 'p': /* server port */
641 break;
642
643 case 'R':
644 /* no-op, still accepted for backwards compatibility */
645 break;
646
647 case 's': /* dump schema only */
648 schema_only = true;
649 break;
650
651 case 'S': /* Username for superuser in plain text output */
653 break;
654
655 case 't': /* include table(s) */
657 dopt.include_everything = false;
658 break;
659
660 case 'T': /* exclude table(s) */
662 break;
663
664 case 'U':
666 break;
667
668 case 'v': /* verbose */
669 g_verbose = true;
671 break;
672
673 case 'w':
675 break;
676
677 case 'W':
679 break;
680
681 case 'x': /* skip ACL dump */
682 dopt.aclsSkip = true;
683 break;
684
685 case 'Z': /* Compression */
689 break;
690
691 case 0:
692 /* This covers the long options. */
693 break;
694
695 case 2: /* lock-wait-timeout */
697 break;
698
699 case 3: /* SET ROLE */
700 use_role = pg_strdup(optarg);
701 break;
702
703 case 4: /* exclude table(s) data */
705 break;
706
707 case 5: /* section */
709 break;
710
711 case 6: /* snapshot */
713 break;
714
715 case 7: /* no-sync */
716 dosync = false;
717 break;
718
719 case 8:
721 if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
723 exit_nicely(1);
724 break;
725
726 case 9: /* inserts */
727
728 /*
729 * dump_inserts also stores --rows-per-insert, careful not to
730 * overwrite that.
731 */
732 if (dopt.dump_inserts == 0)
734 break;
735
736 case 10: /* rows per insert */
737 if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
738 &dopt.dump_inserts))
739 exit_nicely(1);
740 break;
741
742 case 11: /* include foreign data */
744 optarg);
745 break;
746
747 case 12: /* include table(s) and their children */
749 optarg);
750 dopt.include_everything = false;
751 break;
752
753 case 13: /* exclude table(s) and their children */
755 optarg);
756 break;
757
758 case 14: /* exclude data of table(s) and children */
760 optarg);
761 break;
762
763 case 15:
765 exit_nicely(1);
766 break;
767
768 case 16: /* read object filters from file */
770 break;
771
772 case 17: /* exclude extension(s) */
774 optarg);
775 break;
776
777 case 18:
778 statistics_only = true;
779 break;
780
781 case 19:
782 no_data = true;
783 break;
784
785 case 20:
786 no_schema = true;
787 break;
788
789 case 21:
790 no_statistics = true;
791 break;
792
793 case 22:
794 with_statistics = true;
795 break;
796
797 case 25:
799 break;
800
801 default:
802 /* getopt_long already emitted a complaint */
803 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
804 exit_nicely(1);
805 }
806 }
807
808 /*
809 * Non-option argument specifies database name as long as it wasn't
810 * already specified with -d / --dbname
811 */
812 if (optind < argc && dopt.cparams.dbname == NULL)
813 dopt.cparams.dbname = argv[optind++];
814
815 /* Complain if any arguments remain */
816 if (optind < argc)
817 {
818 pg_log_error("too many command-line arguments (first is \"%s\")",
819 argv[optind]);
820 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
821 exit_nicely(1);
822 }
823
824 /* --column-inserts implies --inserts */
825 if (dopt.column_inserts && dopt.dump_inserts == 0)
827
828 /* *-only options are incompatible with each other */
829 check_mut_excl_opts(data_only, "-a/--data-only",
830 schema_only, "-s/--schema-only",
831 statistics_only, "--statistics-only");
832
833 /* --no-* and *-only for same thing are incompatible */
834 check_mut_excl_opts(data_only, "-a/--data-only",
835 no_data, "--no-data");
836 check_mut_excl_opts(schema_only, "-s/--schema-only",
837 no_schema, "--no-schema");
838 check_mut_excl_opts(statistics_only, "--statistics-only",
839 no_statistics, "--no-statistics");
840
841 /* --statistics and --no-statistics are incompatible */
842 check_mut_excl_opts(with_statistics, "--statistics",
843 no_statistics, "--no-statistics");
844
845 /* --statistics is incompatible with *-only (except --statistics-only) */
846 check_mut_excl_opts(with_statistics, "--statistics",
847 data_only, "-a/--data-only",
848 schema_only, "-s/--schema-only");
849
850 /* --include-foreign-data is incompatible with --schema-only */
852 schema_only, "-s/--schema-only");
853
854 if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
855 pg_fatal("option %s is not supported with parallel backup",
856 "--include-foreign-data");
857
858 /* --clean is incompatible with --data-only */
859 check_mut_excl_opts(dopt.outputClean, "-c/--clean",
860 data_only, "-a/--data-only");
861
862 if (dopt.if_exists && !dopt.outputClean)
863 pg_fatal("option %s requires option %s",
864 "--if-exists", "-c/--clean");
865
866 /*
867 * Set derivative flags. Ambiguous or nonsensical combinations, e.g.
868 * "--schema-only --no-schema", will have already caused an error in one
869 * of the checks above.
870 */
871 dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
872 data_only) && !no_data;
873 dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
875 dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
877
878
879 /*
880 * --inserts are already implied above if --column-inserts or
881 * --rows-per-insert were specified.
882 */
883 if (dopt.do_nothing && dopt.dump_inserts == 0)
884 pg_fatal("option %s requires option %s, %s, or %s",
885 "--on-conflict-do-nothing",
886 "--inserts", "--rows-per-insert", "--column-inserts");
887
888 /* Identify archive format to emit */
890
891 /* archiveFormat specific setup */
892 if (archiveFormat == archNull)
893 {
894 plainText = 1;
895
896 /*
897 * If you don't provide a restrict key, one will be appointed for you.
898 */
899 if (!dopt.restrict_key)
901 if (!dopt.restrict_key)
902 pg_fatal("could not generate restrict key");
904 pg_fatal("invalid restrict key");
905 }
906 else if (dopt.restrict_key)
907 pg_fatal("option %s can only be used with %s",
908 "--restrict-key", "--format=plain");
909
910 /*
911 * Custom and directory formats are compressed by default with gzip when
912 * available, not the others. If gzip is not available, no compression is
913 * done by default.
914 */
917 {
918#ifdef HAVE_LIBZ
920#else
922#endif
923 }
924
925 /*
926 * Compression options
927 */
930 pg_fatal("unrecognized compression algorithm: \"%s\"",
932
934 &compression_spec);
936 if (error_detail != NULL)
937 pg_fatal("invalid compression specification: %s",
939
940 error_detail = supports_compression(compression_spec);
941 if (error_detail != NULL)
942 pg_fatal("%s", error_detail);
943
944 /*
945 * Disable support for zstd workers for now - these are based on
946 * threading, and it's unclear how it interacts with parallel dumps on
947 * platforms where that relies on threads too (e.g. Windows).
948 */
949 if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
950 pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
951 "workers");
952
953 /*
954 * If emitting an archive format, we always want to emit a DATABASE item,
955 * in case --create is specified at pg_restore time.
956 */
957 if (!plainText)
958 dopt.outputCreateDB = 1;
959
960 /* Parallel backup only in the directory archive format so far */
961 if (archiveFormat != archDirectory && numWorkers > 1)
962 pg_fatal("parallel backup only supported by the directory format");
963
964 /* Open the output file */
965 fout = CreateArchive(filename, archiveFormat, compression_spec,
967
968 /* Make dump options accessible right away */
969 SetArchiveOptions(fout, &dopt, NULL);
970
971 /* Register the cleanup hook */
973
974 /* Let the archiver know how noisy to be */
976
977
978 /*
979 * We allow the server to be back to 9.2, and up to any minor release of
980 * our own major version. (See also version check in pg_dumpall.c.)
981 */
982 fout->minRemoteVersion = 90200;
983 fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
984
985 fout->numWorkers = numWorkers;
986
987 /*
988 * Open the database using the Archiver, so it knows about it. Errors mean
989 * death.
990 */
991 ConnectDatabaseAhx(fout, &dopt.cparams, false);
993
994 /*
995 * On hot standbys, never try to dump unlogged table data, since it will
996 * just throw an error.
997 */
998 if (fout->isStandby)
999 dopt.no_unlogged_table_data = true;
1000
1001 /*
1002 * Find the last built-in OID, if needed (prior to 8.1)
1003 *
1004 * With 8.1 and above, we can just use FirstNormalObjectId - 1.
1005 */
1007
1008 pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1009
1010 /* Expand schema selection patterns into OID lists */
1012 {
1015 strict_names);
1017 pg_fatal("no matching schemas were found");
1018 }
1021 false);
1022 /* non-matching exclusion patterns aren't an error */
1023
1024 /* Expand table selection patterns into OID lists */
1027 strict_names, false);
1030 strict_names, true);
1034 pg_fatal("no matching tables were found");
1035
1038 false, false);
1041 false, true);
1042
1045 false, false);
1048 false, true);
1049
1052
1053 /* non-matching exclusion patterns aren't an error */
1054
1055 /* Expand extension selection patterns into OID lists */
1057 {
1060 strict_names);
1062 pg_fatal("no matching extensions were found");
1063 }
1066 false);
1067 /* non-matching exclusion patterns aren't an error */
1068
1069 /*
1070 * Dumping LOs is the default for dumps where an inclusion switch is not
1071 * used (an "include everything" dump). -B can be used to exclude LOs
1072 * from those dumps. -b can be used to include LOs even when an inclusion
1073 * switch is used.
1074 *
1075 * -s means "schema only" and LOs are data, not schema, so we never
1076 * include LOs when -s is used.
1077 */
1078 if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1079 dopt.outputLOs = true;
1080
1081 /*
1082 * Collect role names so we can map object owner OIDs to names.
1083 */
1085
1086 /*
1087 * Now scan the database and create DumpableObject structs for all the
1088 * objects we intend to dump.
1089 */
1090 tblinfo = getSchemaData(fout, &numTables);
1091
1092 if (dopt.dumpData)
1093 {
1094 getTableData(&dopt, tblinfo, numTables, 0);
1096 if (!dopt.dumpSchema)
1098 }
1099
1100 if (!dopt.dumpData && dopt.sequence_data)
1101 getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1102
1103 /*
1104 * For binary upgrade mode, dump the pg_shdepend rows for large objects
1105 * and maybe even pg_largeobject_metadata (see comment below for details).
1106 * This is faster to restore than the equivalent set of large object
1107 * commands.
1108 */
1109 if (dopt.binary_upgrade)
1110 {
1112
1115
1116 /*
1117 * Only dump large object shdepend rows for this database.
1118 */
1119 shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass "
1120 "AND dbid = (SELECT oid FROM pg_database "
1121 " WHERE datname = current_database())";
1122
1123 /*
1124 * For binary upgrades from v16 and newer versions, we can copy
1125 * pg_largeobject_metadata's files from the old cluster, so we don't
1126 * need to dump its contents. pg_upgrade can't copy/link the files
1127 * from older versions because aclitem (needed by
1128 * pg_largeobject_metadata.lomacl) changed its storage format in v16.
1129 */
1130 if (fout->remoteVersion < 160000)
1131 {
1133
1136 }
1137 }
1138
1139 /*
1140 * In binary-upgrade mode, we do not have to worry about the actual LO
1141 * data or the associated metadata that resides in the pg_largeobject and
1142 * pg_largeobject_metadata tables, respectively.
1143 *
1144 * However, we do need to collect LO information as there may be comments
1145 * or other information on LOs that we do need to dump out.
1146 */
1147 if (dopt.outputLOs || dopt.binary_upgrade)
1148 getLOs(fout);
1149
1150 /*
1151 * Collect dependency data to assist in ordering the objects.
1152 */
1154
1155 /*
1156 * Collect ACLs, comments, and security labels, if wanted.
1157 */
1158 if (!dopt.aclsSkip)
1160 if (!dopt.no_comments)
1162 if (!dopt.no_security_labels)
1164
1165 /* For binary upgrade mode, collect required pg_class information. */
1166 if (dopt.binary_upgrade)
1168
1169 /* Collect sequence information. */
1171
1172 /* Lastly, create dummy objects to represent the section boundaries */
1174
1175 /* Get pointers to all the known DumpableObjects */
1177
1178 /*
1179 * Add dummy dependencies to enforce the dump section ordering.
1180 */
1182
1183 /*
1184 * Sort the objects into a safe dump order (no forward references).
1185 *
1186 * We rely on dependency information to help us determine a safe order, so
1187 * the initial sort is mostly for cosmetic purposes: we sort by name to
1188 * ensure that logically identical schemas will dump identically.
1189 */
1191
1193 boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1194
1195 /*
1196 * Create archive TOC entries for all the objects to be dumped, in a safe
1197 * order.
1198 */
1199
1200 /*
1201 * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1202 */
1206
1207 /* The database items are always next, unless we don't want them at all */
1208 if (dopt.outputCreateDB)
1210
1211 /* Now the rearrangeable objects. */
1212 for (i = 0; i < numObjs; i++)
1214
1215 /*
1216 * Set up options info to ensure we dump what we want.
1217 */
1218 ropt = NewRestoreOptions();
1219 ropt->filename = filename;
1220
1221 /* if you change this list, see dumpOptionsFromRestoreOptions */
1222 ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1223 ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1224 ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1227 ropt->dropSchema = dopt.outputClean;
1228 ropt->dumpData = dopt.dumpData;
1229 ropt->dumpSchema = dopt.dumpSchema;
1230 ropt->dumpStatistics = dopt.dumpStatistics;
1231 ropt->if_exists = dopt.if_exists;
1232 ropt->column_inserts = dopt.column_inserts;
1233 ropt->dumpSections = dopt.dumpSections;
1234 ropt->aclsSkip = dopt.aclsSkip;
1235 ropt->superuser = dopt.outputSuperuser;
1236 ropt->createDB = dopt.outputCreateDB;
1237 ropt->noOwner = dopt.outputNoOwner;
1238 ropt->noTableAm = dopt.outputNoTableAm;
1239 ropt->noTablespace = dopt.outputNoTablespaces;
1241 ropt->use_setsessauth = dopt.use_setsessauth;
1243 ropt->dump_inserts = dopt.dump_inserts;
1244 ropt->no_comments = dopt.no_comments;
1245 ropt->no_policies = dopt.no_policies;
1246 ropt->no_publications = dopt.no_publications;
1249 ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1252 ropt->sequence_data = dopt.sequence_data;
1253 ropt->binary_upgrade = dopt.binary_upgrade;
1254 ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : NULL;
1255
1256 ropt->compression_spec = compression_spec;
1257
1258 ropt->suppressDumpWarnings = true; /* We've already shown them */
1259
1260 SetArchiveOptions(fout, &dopt, ropt);
1261
1262 /* Mark which entries should be output */
1264
1265 /*
1266 * The archive's TOC entries are now marked as to which ones will actually
1267 * be output, so we can set up their dependency lists properly. This isn't
1268 * necessary for plain-text output, though.
1269 */
1270 if (!plainText)
1272
1273 /*
1274 * And finally we can do the actual output.
1275 *
1276 * Note: for non-plain-text output formats, the output file is written
1277 * inside CloseArchive(). This is, um, bizarre; but not worth changing
1278 * right now.
1279 */
1280 if (plainText)
1281 RestoreArchive(fout, false);
1282
1284
1285 exit_nicely(0);
1286}
1287
1288
1289static void
1290help(const char *progname)
1291{
1292 printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1293 printf(_("Usage:\n"));
1294 printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1295
1296 printf(_("\nGeneral options:\n"));
1297 printf(_(" -f, --file=FILENAME output file or directory name\n"));
1298 printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1299 " plain text (default))\n"));
1300 printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1301 printf(_(" -v, --verbose verbose mode\n"));
1302 printf(_(" -V, --version output version information, then exit\n"));
1303 printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1304 " compress as specified\n"));
1305 printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1306 printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1307 printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1308 printf(_(" -?, --help show this help, then exit\n"));
1309
1310 printf(_("\nOptions controlling the output content:\n"));
1311 printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1312 printf(_(" -b, --large-objects include large objects in dump\n"));
1313 printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1314 printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1315 printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1316 printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1317 printf(_(" -C, --create include commands to create database in dump\n"));
1318 printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1319 printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1320 printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1321 printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1322 printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1323 " plain-text format\n"));
1324 printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1325 printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1326 printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1327 printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1328 printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1329 printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1330 printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1331 printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1332 printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1333 printf(_(" --enable-row-security enable row security (dump only content user has\n"
1334 " access to)\n"));
1335 printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1336 printf(_(" --exclude-table-and-children=PATTERN\n"
1337 " do NOT dump the specified table(s), including\n"
1338 " child and partition tables\n"));
1339 printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1340 printf(_(" --exclude-table-data-and-children=PATTERN\n"
1341 " do NOT dump data for the specified table(s),\n"
1342 " including child and partition tables\n"));
1343 printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1344 printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1345 " based on expressions in FILENAME\n"));
1346 printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1347 printf(_(" --include-foreign-data=PATTERN\n"
1348 " include data of foreign tables on foreign\n"
1349 " servers matching PATTERN\n"));
1350 printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1351 printf(_(" --load-via-partition-root load partitions via the root table\n"));
1352 printf(_(" --no-comments do not dump comment commands\n"));
1353 printf(_(" --no-data do not dump data\n"));
1354 printf(_(" --no-policies do not dump row security policies\n"));
1355 printf(_(" --no-publications do not dump publications\n"));
1356 printf(_(" --no-schema do not dump schema\n"));
1357 printf(_(" --no-security-labels do not dump security label assignments\n"));
1358 printf(_(" --no-statistics do not dump statistics\n"));
1359 printf(_(" --no-subscriptions do not dump subscriptions\n"));
1360 printf(_(" --no-table-access-method do not dump table access methods\n"));
1361 printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1362 printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1363 printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1364 printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1365 printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1366 printf(_(" --restrict-key=RESTRICT_KEY use provided string as psql \\restrict key\n"));
1367 printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1368 printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1369 printf(_(" --sequence-data include sequence data in dump\n"));
1370 printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1371 printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1372 printf(_(" --statistics dump the statistics\n"));
1373 printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1374 printf(_(" --strict-names require table and/or schema include patterns to\n"
1375 " match at least one entity each\n"));
1376 printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1377 " child and partition tables\n"));
1378 printf(_(" --use-set-session-authorization\n"
1379 " use SET SESSION AUTHORIZATION commands instead of\n"
1380 " ALTER OWNER commands to set ownership\n"));
1381
1382 printf(_("\nConnection options:\n"));
1383 printf(_(" -d, --dbname=DBNAME database to dump\n"));
1384 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1385 printf(_(" -p, --port=PORT database server port number\n"));
1386 printf(_(" -U, --username=NAME connect as specified database user\n"));
1387 printf(_(" -w, --no-password never prompt for password\n"));
1388 printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1389 printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1390
1391 printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1392 "variable value is used.\n\n"));
1393 printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1394 printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1395}
1396
1397static void
1398setup_connection(Archive *AH, const char *dumpencoding,
1399 const char *dumpsnapshot, char *use_role)
1400{
1401 DumpOptions *dopt = AH->dopt;
1402 PGconn *conn = GetConnection(AH);
1403
1405
1406 /*
1407 * Set the client encoding if requested.
1408 */
1409 if (dumpencoding)
1410 {
1412 pg_fatal("invalid client encoding \"%s\" specified",
1413 dumpencoding);
1414 }
1415
1416 /*
1417 * Force standard_conforming_strings on, just in case we are dumping from
1418 * an old server that has it disabled. Without this, literals in views,
1419 * expressions, etc, would be incorrect for modern servers.
1420 */
1421 ExecuteSqlStatement(AH, "SET standard_conforming_strings = on");
1422
1423 /*
1424 * And reflect that to AH->std_strings. You might think that we should
1425 * just delete that variable and the code that checks it, but that would
1426 * be problematic for pg_restore, which at least for now should still cope
1427 * with archives containing the other setting (cf. processStdStringsEntry
1428 * in pg_backup_archiver.c).
1429 */
1430 AH->std_strings = true;
1431
1432 /*
1433 * Get the active encoding, so we know how to escape strings.
1434 */
1437
1438 /*
1439 * Set the role if requested. In a parallel dump worker, we'll be passed
1440 * use_role == NULL, but AH->use_role is already set (if user specified it
1441 * originally) and we should use that.
1442 */
1443 if (!use_role && AH->use_role)
1444 use_role = AH->use_role;
1445
1446 /* Set the role if requested */
1447 if (use_role)
1448 {
1450
1451 appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1452 ExecuteSqlStatement(AH, query->data);
1453 destroyPQExpBuffer(query);
1454
1455 /* save it for possible later use by parallel workers */
1456 if (!AH->use_role)
1457 AH->use_role = pg_strdup(use_role);
1458 }
1459
1460 /* Set the datestyle to ISO to ensure the dump's portability */
1461 ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1462
1463 /* Likewise, avoid using sql_standard intervalstyle */
1464 ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1465
1466 /*
1467 * Use an explicitly specified extra_float_digits if it has been provided.
1468 * Otherwise, set extra_float_digits so that we can dump float data
1469 * exactly (given correctly implemented float I/O code, anyway).
1470 */
1472 {
1474
1475 appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1477 ExecuteSqlStatement(AH, q->data);
1479 }
1480 else
1481 ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1482
1483 /*
1484 * Disable synchronized scanning, to prevent unpredictable changes in row
1485 * ordering across a dump and reload.
1486 */
1487 ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1488
1489 /*
1490 * Disable timeouts if supported.
1491 */
1492 ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1493 if (AH->remoteVersion >= 90300)
1494 ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1495 if (AH->remoteVersion >= 90600)
1496 ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1497 if (AH->remoteVersion >= 170000)
1498 ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1499
1500 /*
1501 * Quote all identifiers, if requested.
1502 */
1504 ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1505
1506 /*
1507 * Adjust row-security mode, if supported.
1508 */
1509 if (AH->remoteVersion >= 90500)
1510 {
1511 if (dopt->enable_row_security)
1512 ExecuteSqlStatement(AH, "SET row_security = on");
1513 else
1514 ExecuteSqlStatement(AH, "SET row_security = off");
1515 }
1516
1517 /*
1518 * For security reasons, we restrict the expansion of non-system views and
1519 * access to foreign tables during the pg_dump process. This restriction
1520 * is adjusted when dumping foreign table data.
1521 */
1522 set_restrict_relation_kind(AH, "view, foreign-table");
1523
1524 /*
1525 * Initialize prepared-query state to "nothing prepared". We do this here
1526 * so that a parallel dump worker will have its own state.
1527 */
1529
1530 /*
1531 * Start transaction-snapshot mode transaction to dump consistent data.
1532 */
1533 ExecuteSqlStatement(AH, "BEGIN");
1534
1535 /*
1536 * To support the combination of serializable_deferrable with the jobs
1537 * option we use REPEATABLE READ for the worker connections that are
1538 * passed a snapshot. As long as the snapshot is acquired in a
1539 * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1540 * REPEATABLE READ transaction provides the appropriate integrity
1541 * guarantees. This is a kluge, but safe for back-patching.
1542 */
1543 if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1545 "SET TRANSACTION ISOLATION LEVEL "
1546 "SERIALIZABLE, READ ONLY, DEFERRABLE");
1547 else
1549 "SET TRANSACTION ISOLATION LEVEL "
1550 "REPEATABLE READ, READ ONLY");
1551
1552 /*
1553 * If user specified a snapshot to use, select that. In a parallel dump
1554 * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1555 * is already set (if the server can handle it) and we should use that.
1556 */
1557 if (dumpsnapshot)
1559
1560 if (AH->sync_snapshot_id)
1561 {
1563
1564 appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1566 ExecuteSqlStatement(AH, query->data);
1567 destroyPQExpBuffer(query);
1568 }
1569 else if (AH->numWorkers > 1)
1570 {
1571 if (AH->isStandby && AH->remoteVersion < 100000)
1572 pg_fatal("parallel dumps from standby servers are not supported by this server version");
1574 }
1575}
1576
1577/* Set up connection for a parallel worker process */
1578static void
1580{
1581 /*
1582 * We want to re-select all the same values the leader connection is
1583 * using. We'll have inherited directly-usable values in
1584 * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1585 * inherited encoding value back to a string to pass to setup_connection.
1586 */
1589 NULL,
1590 NULL);
1591}
1592
1593static char *
1595{
1596 char *query = "SELECT pg_catalog.pg_export_snapshot()";
1597 char *result;
1598 PGresult *res;
1599
1600 res = ExecuteSqlQueryForSingleRow(fout, query);
1601 result = pg_strdup(PQgetvalue(res, 0, 0));
1602 PQclear(res);
1603
1604 return result;
1605}
1606
1607static ArchiveFormat
1609{
1611
1613
1614 if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1615 {
1616 /* This is used by pg_dumpall, and is not documented */
1619 }
1620 else if (pg_strcasecmp(format, "c") == 0)
1622 else if (pg_strcasecmp(format, "custom") == 0)
1624 else if (pg_strcasecmp(format, "d") == 0)
1626 else if (pg_strcasecmp(format, "directory") == 0)
1628 else if (pg_strcasecmp(format, "p") == 0)
1630 else if (pg_strcasecmp(format, "plain") == 0)
1632 else if (pg_strcasecmp(format, "t") == 0)
1634 else if (pg_strcasecmp(format, "tar") == 0)
1636 else
1637 pg_fatal("invalid output format \"%s\" specified", format);
1638 return archiveFormat;
1639}
1640
1641/*
1642 * Find the OIDs of all schemas matching the given list of patterns,
1643 * and append them to the given OID list.
1644 */
1645static void
1648 SimpleOidList *oids,
1649 bool strict_names)
1650{
1651 PQExpBuffer query;
1652 PGresult *res;
1654 int i;
1655
1656 if (patterns->head == NULL)
1657 return; /* nothing to do */
1658
1659 query = createPQExpBuffer();
1660
1661 /*
1662 * The loop below runs multiple SELECTs might sometimes result in
1663 * duplicate entries in the OID list, but we don't care.
1664 */
1665
1666 for (cell = patterns->head; cell; cell = cell->next)
1667 {
1669 int dotcnt;
1670
1672 "SELECT oid FROM pg_catalog.pg_namespace n\n");
1674 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1675 false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1676 &dotcnt);
1677 if (dotcnt > 1)
1678 pg_fatal("improper qualified name (too many dotted names): %s",
1679 cell->val);
1680 else if (dotcnt == 1)
1683
1684 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1685 if (strict_names && PQntuples(res) == 0)
1686 pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1687
1688 for (i = 0; i < PQntuples(res); i++)
1689 {
1690 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1691 }
1692
1693 PQclear(res);
1694 resetPQExpBuffer(query);
1695 }
1696
1697 destroyPQExpBuffer(query);
1698}
1699
1700/*
1701 * Find the OIDs of all extensions matching the given list of patterns,
1702 * and append them to the given OID list.
1703 */
1704static void
1707 SimpleOidList *oids,
1708 bool strict_names)
1709{
1710 PQExpBuffer query;
1711 PGresult *res;
1713 int i;
1714
1715 if (patterns->head == NULL)
1716 return; /* nothing to do */
1717
1718 query = createPQExpBuffer();
1719
1720 /*
1721 * The loop below runs multiple SELECTs might sometimes result in
1722 * duplicate entries in the OID list, but we don't care.
1723 */
1724 for (cell = patterns->head; cell; cell = cell->next)
1725 {
1726 int dotcnt;
1727
1729 "SELECT oid FROM pg_catalog.pg_extension e\n");
1730 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1731 false, NULL, "e.extname", NULL, NULL, NULL,
1732 &dotcnt);
1733 if (dotcnt > 0)
1734 pg_fatal("improper qualified name (too many dotted names): %s",
1735 cell->val);
1736
1737 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1738 if (strict_names && PQntuples(res) == 0)
1739 pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1740
1741 for (i = 0; i < PQntuples(res); i++)
1742 {
1743 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1744 }
1745
1746 PQclear(res);
1747 resetPQExpBuffer(query);
1748 }
1749
1750 destroyPQExpBuffer(query);
1751}
1752
1753/*
1754 * Find the OIDs of all foreign servers matching the given list of patterns,
1755 * and append them to the given OID list.
1756 */
1757static void
1760 SimpleOidList *oids)
1761{
1762 PQExpBuffer query;
1763 PGresult *res;
1765 int i;
1766
1767 if (patterns->head == NULL)
1768 return; /* nothing to do */
1769
1770 query = createPQExpBuffer();
1771
1772 /*
1773 * The loop below runs multiple SELECTs might sometimes result in
1774 * duplicate entries in the OID list, but we don't care.
1775 */
1776
1777 for (cell = patterns->head; cell; cell = cell->next)
1778 {
1779 int dotcnt;
1780
1782 "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1783 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1784 false, NULL, "s.srvname", NULL, NULL, NULL,
1785 &dotcnt);
1786 if (dotcnt > 0)
1787 pg_fatal("improper qualified name (too many dotted names): %s",
1788 cell->val);
1789
1790 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1791 if (PQntuples(res) == 0)
1792 pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1793
1794 for (i = 0; i < PQntuples(res); i++)
1795 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1796
1797 PQclear(res);
1798 resetPQExpBuffer(query);
1799 }
1800
1801 destroyPQExpBuffer(query);
1802}
1803
1804/*
1805 * Find the OIDs of all tables matching the given list of patterns,
1806 * and append them to the given OID list. See also expand_dbname_patterns()
1807 * in pg_dumpall.c
1808 */
1809static void
1813{
1814 PQExpBuffer query;
1815 PGresult *res;
1817 int i;
1818
1819 if (patterns->head == NULL)
1820 return; /* nothing to do */
1821
1822 query = createPQExpBuffer();
1823
1824 /*
1825 * this might sometimes result in duplicate entries in the OID list, but
1826 * we don't care.
1827 */
1828
1829 for (cell = patterns->head; cell; cell = cell->next)
1830 {
1832 int dotcnt;
1833
1834 /*
1835 * Query must remain ABSOLUTELY devoid of unqualified names. This
1836 * would be unnecessary given a pg_table_is_visible() variant taking a
1837 * search_path argument.
1838 *
1839 * For with_child_tables, we start with the basic query's results and
1840 * recursively search the inheritance tree to add child tables.
1841 */
1843 {
1844 appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1845 }
1846
1847 appendPQExpBuffer(query,
1848 "SELECT c.oid"
1849 "\nFROM pg_catalog.pg_class c"
1850 "\n LEFT JOIN pg_catalog.pg_namespace n"
1851 "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1852 "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1853 "\n (array['%c', '%c', '%c', '%c', '%c', '%c', '%c'])\n",
1858 processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1859 false, "n.nspname", "c.relname", NULL,
1860 "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1861 &dotcnt);
1862 if (dotcnt > 2)
1863 pg_fatal("improper relation name (too many dotted names): %s",
1864 cell->val);
1865 else if (dotcnt == 2)
1868
1870 {
1871 appendPQExpBufferStr(query, "UNION"
1872 "\nSELECT i.inhrelid"
1873 "\nFROM partition_tree p"
1874 "\n JOIN pg_catalog.pg_inherits i"
1875 "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1876 "\n)"
1877 "\nSELECT relid FROM partition_tree");
1878 }
1879
1880 ExecuteSqlStatement(fout, "RESET search_path");
1881 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1884 if (strict_names && PQntuples(res) == 0)
1885 pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1886
1887 for (i = 0; i < PQntuples(res); i++)
1888 {
1889 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1890 }
1891
1892 PQclear(res);
1893 resetPQExpBuffer(query);
1894 }
1895
1896 destroyPQExpBuffer(query);
1897}
1898
1899/*
1900 * Verifies that the connected database name matches the given database name,
1901 * and if not, dies with an error about the given pattern.
1902 *
1903 * The 'dbname' argument should be a literal name parsed from 'pattern'.
1904 */
1905static void
1906prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1907{
1908 const char *db;
1909
1910 db = PQdb(conn);
1911 if (db == NULL)
1912 pg_fatal("You are currently not connected to a database.");
1913
1914 if (strcmp(db, dbname) != 0)
1915 pg_fatal("cross-database references are not implemented: %s",
1916 pattern);
1917}
1918
1919/*
1920 * checkExtensionMembership
1921 * Determine whether object is an extension member, and if so,
1922 * record an appropriate dependency and set the object's dump flag.
1923 *
1924 * It's important to call this for each object that could be an extension
1925 * member. Generally, we integrate this with determining the object's
1926 * to-be-dumped-ness, since extension membership overrides other rules for that.
1927 *
1928 * Returns true if object is an extension member, else false.
1929 */
1930static bool
1932{
1934
1935 if (ext == NULL)
1936 return false;
1937
1938 dobj->ext_member = true;
1939
1940 /* Record dependency so that getDependencies needn't deal with that */
1941 addObjectDependency(dobj, ext->dobj.dumpId);
1942
1943 /*
1944 * In 9.6 and above, mark the member object to have any non-initial ACLs
1945 * dumped. (Any initial ACLs will be removed later, using data from
1946 * pg_init_privs, so that we'll dump only the delta from the extension's
1947 * initial setup.)
1948 *
1949 * Prior to 9.6, we do not include any extension member components.
1950 *
1951 * In binary upgrades, we still dump all components of the members
1952 * individually, since the idea is to exactly reproduce the database
1953 * contents rather than replace the extension contents with something
1954 * different.
1955 *
1956 * Note: it might be interesting someday to implement storage and delta
1957 * dumping of extension members' RLS policies and/or security labels.
1958 * However there is a pitfall for RLS policies: trying to dump them
1959 * requires getting a lock on their tables, and the calling user might not
1960 * have privileges for that. We need no lock to examine a table's ACLs,
1961 * so the current feature doesn't have a problem of that sort.
1962 */
1963 if (fout->dopt->binary_upgrade)
1964 dobj->dump = ext->dobj.dump;
1965 else
1966 {
1967 if (fout->remoteVersion < 90600)
1968 dobj->dump = DUMP_COMPONENT_NONE;
1969 else
1970 dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1971 }
1972
1973 return true;
1974}
1975
1976/*
1977 * selectDumpableNamespace: policy-setting subroutine
1978 * Mark a namespace as to be dumped or not
1979 */
1980static void
1982{
1983 /*
1984 * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1985 * and (for --clean) a DROP SCHEMA statement. (In the absence of
1986 * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1987 */
1988 nsinfo->create = true;
1989
1990 /*
1991 * If specific tables are being dumped, do not dump any complete
1992 * namespaces. If specific namespaces are being dumped, dump just those
1993 * namespaces. Otherwise, dump all non-system namespaces.
1994 */
1996 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1997 else if (schema_include_oids.head != NULL)
1998 nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
2000 nsinfo->dobj.catId.oid) ?
2002 else if (fout->remoteVersion >= 90600 &&
2003 strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
2004 {
2005 /*
2006 * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
2007 * they are interesting (and not the original ACLs which were set at
2008 * initdb time, see pg_init_privs).
2009 */
2010 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
2011 }
2012 else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
2013 strcmp(nsinfo->dobj.name, "information_schema") == 0)
2014 {
2015 /* Other system schemas don't get dumped */
2016 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2017 }
2018 else if (strcmp(nsinfo->dobj.name, "public") == 0)
2019 {
2020 /*
2021 * The public schema is a strange beast that sits in a sort of
2022 * no-mans-land between being a system object and a user object.
2023 * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2024 * a comment and an indication of ownership. If the owner is the
2025 * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2026 * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2027 */
2028 nsinfo->create = false;
2029 nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2030 if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2031 nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2032 nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
2033
2034 /*
2035 * Also, make like it has a comment even if it doesn't; this is so
2036 * that we'll emit a command to drop the comment, if appropriate.
2037 * (Without this, we'd not call dumpCommentExtended for it.)
2038 */
2039 nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
2040 }
2041 else
2042 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2043
2044 /*
2045 * In any case, a namespace can be excluded by an exclusion switch
2046 */
2047 if (nsinfo->dobj.dump_contains &&
2049 nsinfo->dobj.catId.oid))
2050 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2051
2052 /*
2053 * If the schema belongs to an extension, allow extension membership to
2054 * override the dump decision for the schema itself. However, this does
2055 * not change dump_contains, so this won't change what we do with objects
2056 * within the schema. (If they belong to the extension, they'll get
2057 * suppressed by it, otherwise not.)
2058 */
2060}
2061
2062/*
2063 * selectDumpableTable: policy-setting subroutine
2064 * Mark a table as to be dumped or not
2065 */
2066static void
2068{
2070 return; /* extension membership overrides all else */
2071
2072 /*
2073 * If specific tables are being dumped, dump just those tables; else, dump
2074 * according to the parent namespace's dump flag.
2075 */
2078 tbinfo->dobj.catId.oid) ?
2080 else
2081 tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2082
2083 /*
2084 * In any case, a table can be excluded by an exclusion switch
2085 */
2086 if (tbinfo->dobj.dump &&
2088 tbinfo->dobj.catId.oid))
2089 tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2090}
2091
2092/*
2093 * selectDumpableType: policy-setting subroutine
2094 * Mark a type as to be dumped or not
2095 *
2096 * If it's a table's rowtype or an autogenerated array type, we also apply a
2097 * special type code to facilitate sorting into the desired order. (We don't
2098 * want to consider those to be ordinary types because that would bring tables
2099 * up into the datatype part of the dump order.) We still set the object's
2100 * dump flag; that's not going to cause the dummy type to be dumped, but we
2101 * need it so that casts involving such types will be dumped correctly -- see
2102 * dumpCast. This means the flag should be set the same as for the underlying
2103 * object (the table or base type).
2104 */
2105static void
2107{
2108 /* skip complex types, except for standalone composite types */
2109 if (OidIsValid(tyinfo->typrelid) &&
2110 tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2111 {
2112 TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2113
2114 tyinfo->dobj.objType = DO_DUMMY_TYPE;
2115 if (tytable != NULL)
2116 tyinfo->dobj.dump = tytable->dobj.dump;
2117 else
2118 tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2119 return;
2120 }
2121
2122 /* skip auto-generated array and multirange types */
2123 if (tyinfo->isArray || tyinfo->isMultirange)
2124 {
2125 tyinfo->dobj.objType = DO_DUMMY_TYPE;
2126
2127 /*
2128 * Fall through to set the dump flag; we assume that the subsequent
2129 * rules will do the same thing as they would for the array's base
2130 * type or multirange's range type. (We cannot reliably look up the
2131 * base type here, since getTypes may not have processed it yet.)
2132 */
2133 }
2134
2136 return; /* extension membership overrides all else */
2137
2138 /* Dump based on if the contents of the namespace are being dumped */
2139 tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2140}
2141
2142/*
2143 * selectDumpableDefaultACL: policy-setting subroutine
2144 * Mark a default ACL as to be dumped or not
2145 *
2146 * For per-schema default ACLs, dump if the schema is to be dumped.
2147 * Otherwise dump if we are dumping "everything". Note that dumpSchema
2148 * and aclsSkip are checked separately.
2149 */
2150static void
2152{
2153 /* Default ACLs can't be extension members */
2154
2155 if (dinfo->dobj.namespace)
2156 /* default ACLs are considered part of the namespace */
2157 dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2158 else
2159 dinfo->dobj.dump = dopt->include_everything ?
2161}
2162
2163/*
2164 * selectDumpableCast: policy-setting subroutine
2165 * Mark a cast as to be dumped or not
2166 *
2167 * Casts do not belong to any particular namespace (since they haven't got
2168 * names), nor do they have identifiable owners. To distinguish user-defined
2169 * casts from built-in ones, we must resort to checking whether the cast's
2170 * OID is in the range reserved for initdb.
2171 */
2172static void
2174{
2175 if (checkExtensionMembership(&cast->dobj, fout))
2176 return; /* extension membership overrides all else */
2177
2178 /*
2179 * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2180 * support ACLs currently.
2181 */
2182 if (cast->dobj.catId.oid <= g_last_builtin_oid)
2183 cast->dobj.dump = DUMP_COMPONENT_NONE;
2184 else
2185 cast->dobj.dump = fout->dopt->include_everything ?
2187}
2188
2189/*
2190 * selectDumpableProcLang: policy-setting subroutine
2191 * Mark a procedural language as to be dumped or not
2192 *
2193 * Procedural languages do not belong to any particular namespace. To
2194 * identify built-in languages, we must resort to checking whether the
2195 * language's OID is in the range reserved for initdb.
2196 */
2197static void
2199{
2200 if (checkExtensionMembership(&plang->dobj, fout))
2201 return; /* extension membership overrides all else */
2202
2203 /*
2204 * Only include procedural languages when we are dumping everything.
2205 *
2206 * For from-initdb procedural languages, only include ACLs, as we do for
2207 * the pg_catalog namespace. We need this because procedural languages do
2208 * not live in any namespace.
2209 */
2211 plang->dobj.dump = DUMP_COMPONENT_NONE;
2212 else
2213 {
2214 if (plang->dobj.catId.oid <= g_last_builtin_oid)
2215 plang->dobj.dump = fout->remoteVersion < 90600 ?
2217 else
2218 plang->dobj.dump = DUMP_COMPONENT_ALL;
2219 }
2220}
2221
2222/*
2223 * selectDumpableAccessMethod: policy-setting subroutine
2224 * Mark an access method as to be dumped or not
2225 *
2226 * Access methods do not belong to any particular namespace. To identify
2227 * built-in access methods, we must resort to checking whether the
2228 * method's OID is in the range reserved for initdb.
2229 */
2230static void
2232{
2233 /* see getAccessMethods() comment about v9.6. */
2234 if (fout->remoteVersion < 90600)
2235 {
2236 method->dobj.dump = DUMP_COMPONENT_NONE;
2237 return;
2238 }
2239
2240 if (checkExtensionMembership(&method->dobj, fout))
2241 return; /* extension membership overrides all else */
2242
2243 /*
2244 * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2245 * they do not support ACLs currently.
2246 */
2247 if (method->dobj.catId.oid <= g_last_builtin_oid)
2248 method->dobj.dump = DUMP_COMPONENT_NONE;
2249 else
2250 method->dobj.dump = fout->dopt->include_everything ?
2252}
2253
2254/*
2255 * selectDumpableExtension: policy-setting subroutine
2256 * Mark an extension as to be dumped or not
2257 *
2258 * Built-in extensions should be skipped except for checking ACLs, since we
2259 * assume those will already be installed in the target database. We identify
2260 * such extensions by their having OIDs in the range reserved for initdb.
2261 * We dump all user-added extensions by default. No extensions are dumped
2262 * if include_everything is false (i.e., a --schema or --table switch was
2263 * given), except if --extension specifies a list of extensions to dump.
2264 */
2265static void
2267{
2268 /*
2269 * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2270 * change permissions on their member objects, if they wish to, and have
2271 * those changes preserved.
2272 */
2273 if (extinfo->dobj.catId.oid <= g_last_builtin_oid)
2274 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2275 else
2276 {
2277 /* check if there is a list of extensions to dump */
2279 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2281 extinfo->dobj.catId.oid) ?
2283 else
2284 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2285 dopt->include_everything ?
2287
2288 /* check that the extension is not explicitly excluded */
2289 if (extinfo->dobj.dump &&
2291 extinfo->dobj.catId.oid))
2292 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2293 }
2294}
2295
2296/*
2297 * selectDumpablePublicationObject: policy-setting subroutine
2298 * Mark a publication object as to be dumped or not
2299 *
2300 * A publication can have schemas and tables which have schemas, but those are
2301 * ignored in decision making, because publications are only dumped when we are
2302 * dumping everything.
2303 */
2304static void
2306{
2307 if (checkExtensionMembership(dobj, fout))
2308 return; /* extension membership overrides all else */
2309
2310 dobj->dump = fout->dopt->include_everything ?
2312}
2313
2314/*
2315 * selectDumpableStatisticsObject: policy-setting subroutine
2316 * Mark an extended statistics object as to be dumped or not
2317 *
2318 * We dump an extended statistics object if the schema it's in and the table
2319 * it's for are being dumped. (This'll need more thought if statistics
2320 * objects ever support cross-table stats.)
2321 */
2322static void
2324{
2325 if (checkExtensionMembership(&sobj->dobj, fout))
2326 return; /* extension membership overrides all else */
2327
2328 sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2329 if (sobj->stattable == NULL ||
2330 !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2331 sobj->dobj.dump = DUMP_COMPONENT_NONE;
2332}
2333
2334/*
2335 * selectDumpableObject: policy-setting subroutine
2336 * Mark a generic dumpable object as to be dumped or not
2337 *
2338 * Use this only for object types without a special-case routine above.
2339 */
2340static void
2342{
2343 if (checkExtensionMembership(dobj, fout))
2344 return; /* extension membership overrides all else */
2345
2346 /*
2347 * Default policy is to dump if parent namespace is dumpable, or for
2348 * non-namespace-associated items, dump if we're dumping "everything".
2349 */
2350 if (dobj->namespace)
2351 dobj->dump = dobj->namespace->dobj.dump_contains;
2352 else
2353 dobj->dump = fout->dopt->include_everything ?
2355}
2356
2357/*
2358 * Dump a table's contents for loading using the COPY command
2359 * - this routine is called by the Archiver when it wants the table
2360 * to be dumped.
2361 */
2362static int
2364{
2365 const TableDataInfo *tdinfo = dcontext;
2366 const TableInfo *tbinfo = tdinfo->tdtable;
2367 const char *classname = tbinfo->dobj.name;
2369
2370 /*
2371 * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2372 * which uses it already.
2373 */
2376 PGresult *res;
2377 int ret;
2378 char *copybuf;
2379 const char *column_list;
2380
2381 pg_log_info("dumping contents of table \"%s.%s\"",
2382 tbinfo->dobj.namespace->dobj.name, classname);
2383
2384 /*
2385 * Specify the column list explicitly so that we have no possibility of
2386 * retrieving data in the wrong column order. (The default column
2387 * ordering of COPY will not be what we want in certain corner cases
2388 * involving ADD COLUMN and inheritance.)
2389 */
2391
2392 /*
2393 * Use COPY (SELECT ...) TO when dumping a foreign table's data, when a
2394 * filter condition was specified, and when in binary upgrade mode and
2395 * dumping an old pg_largeobject_metadata defined WITH OIDS. For other
2396 * cases a simple COPY suffices.
2397 */
2398 if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
2399 (fout->dopt->binary_upgrade && fout->remoteVersion < 120000 &&
2400 tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId))
2401 {
2402 /* Temporary allows to access to foreign tables to dump data */
2403 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2405
2406 appendPQExpBufferStr(q, "COPY (SELECT ");
2407 /* klugery to get rid of parens in column list */
2408 if (strlen(column_list) > 2)
2409 {
2411 q->data[q->len - 1] = ' ';
2412 }
2413 else
2414 appendPQExpBufferStr(q, "* ");
2415
2416 appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2418 tdinfo->filtercond ? tdinfo->filtercond : "");
2419 }
2420 else
2421 {
2422 appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2424 column_list);
2425 }
2427 PQclear(res);
2429
2430 for (;;)
2431 {
2432 ret = PQgetCopyData(conn, &copybuf, 0);
2433
2434 if (ret < 0)
2435 break; /* done or error */
2436
2437 if (copybuf)
2438 {
2439 WriteData(fout, copybuf, ret);
2441 }
2442
2443 /* ----------
2444 * THROTTLE:
2445 *
2446 * There was considerable discussion in late July, 2000 regarding
2447 * slowing down pg_dump when backing up large tables. Users with both
2448 * slow & fast (multi-processor) machines experienced performance
2449 * degradation when doing a backup.
2450 *
2451 * Initial attempts based on sleeping for a number of ms for each ms
2452 * of work were deemed too complex, then a simple 'sleep in each loop'
2453 * implementation was suggested. The latter failed because the loop
2454 * was too tight. Finally, the following was implemented:
2455 *
2456 * If throttle is non-zero, then
2457 * See how long since the last sleep.
2458 * Work out how long to sleep (based on ratio).
2459 * If sleep is more than 100ms, then
2460 * sleep
2461 * reset timer
2462 * EndIf
2463 * EndIf
2464 *
2465 * where the throttle value was the number of ms to sleep per ms of
2466 * work. The calculation was done in each loop.
2467 *
2468 * Most of the hard work is done in the backend, and this solution
2469 * still did not work particularly well: on slow machines, the ratio
2470 * was 50:1, and on medium paced machines, 1:1, and on fast
2471 * multi-processor machines, it had little or no effect, for reasons
2472 * that were unclear.
2473 *
2474 * Further discussion ensued, and the proposal was dropped.
2475 *
2476 * For those people who want this feature, it can be implemented using
2477 * gettimeofday in each loop, calculating the time since last sleep,
2478 * multiplying that by the sleep ratio, then if the result is more
2479 * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2480 * function to sleep for a subsecond period ie.
2481 *
2482 * select(0, NULL, NULL, NULL, &tvi);
2483 *
2484 * This will return after the interval specified in the structure tvi.
2485 * Finally, call gettimeofday again to save the 'last sleep time'.
2486 * ----------
2487 */
2488 }
2489 archprintf(fout, "\\.\n\n\n");
2490
2491 if (ret == -2)
2492 {
2493 /* copy data transfer failed */
2494 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2495 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2496 pg_log_error_detail("Command was: %s", q->data);
2497 exit_nicely(1);
2498 }
2499
2500 /* Check command status and return to normal libpq state */
2501 res = PQgetResult(conn);
2502 if (PQresultStatus(res) != PGRES_COMMAND_OK)
2503 {
2504 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2505 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2506 pg_log_error_detail("Command was: %s", q->data);
2507 exit_nicely(1);
2508 }
2509 PQclear(res);
2510
2511 /* Do this to ensure we've pumped libpq back to idle state */
2512 if (PQgetResult(conn) != NULL)
2513 pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2514 classname);
2515
2517
2518 /* Revert back the setting */
2519 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2520 set_restrict_relation_kind(fout, "view, foreign-table");
2521
2522 return 1;
2523}
2524
2525/*
2526 * Dump table data using INSERT commands.
2527 *
2528 * Caution: when we restore from an archive file direct to database, the
2529 * INSERT commands emitted by this function have to be parsed by
2530 * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2531 * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2532 */
2533static int
2535{
2536 const TableDataInfo *tdinfo = dcontext;
2537 const TableInfo *tbinfo = tdinfo->tdtable;
2538 DumpOptions *dopt = fout->dopt;
2541 char *attgenerated;
2542 PGresult *res;
2543 int nfields,
2544 i;
2545 int rows_per_statement = dopt->dump_inserts;
2546 int rows_this_statement = 0;
2547
2548 /* Temporary allows to access to foreign tables to dump data */
2549 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2551
2552 /*
2553 * If we're going to emit INSERTs with column names, the most efficient
2554 * way to deal with generated columns is to exclude them entirely. For
2555 * INSERTs without column names, we have to emit DEFAULT rather than the
2556 * actual column value --- but we can save a few cycles by fetching nulls
2557 * rather than the uninteresting-to-us value.
2558 */
2559 attgenerated = pg_malloc_array(char, tbinfo->numatts);
2560 appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2561 nfields = 0;
2562 for (i = 0; i < tbinfo->numatts; i++)
2563 {
2564 if (tbinfo->attisdropped[i])
2565 continue;
2566 if (tbinfo->attgenerated[i] && dopt->column_inserts)
2567 continue;
2568 if (nfields > 0)
2569 appendPQExpBufferStr(q, ", ");
2570 if (tbinfo->attgenerated[i])
2571 appendPQExpBufferStr(q, "NULL");
2572 else
2573 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2574 attgenerated[nfields] = tbinfo->attgenerated[i];
2575 nfields++;
2576 }
2577 /* Servers before 9.4 will complain about zero-column SELECT */
2578 if (nfields == 0)
2579 appendPQExpBufferStr(q, "NULL");
2580 appendPQExpBuffer(q, " FROM ONLY %s",
2582 if (tdinfo->filtercond)
2583 appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2584
2586
2587 while (1)
2588 {
2589 res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2591
2592 /* cross-check field count, allowing for dummy NULL if any */
2593 if (nfields != PQnfields(res) &&
2594 !(nfields == 0 && PQnfields(res) == 1))
2595 pg_fatal("wrong number of fields retrieved from table \"%s\"",
2596 tbinfo->dobj.name);
2597
2598 /*
2599 * First time through, we build as much of the INSERT statement as
2600 * possible in "insertStmt", which we can then just print for each
2601 * statement. If the table happens to have zero dumpable columns then
2602 * this will be a complete statement, otherwise it will end in
2603 * "VALUES" and be ready to have the row's column values printed.
2604 */
2605 if (insertStmt == NULL)
2606 {
2607 const TableInfo *targettab;
2608
2610
2611 /*
2612 * When load-via-partition-root is set or forced, get the root
2613 * table name for the partition table, so that we can reload data
2614 * through the root table.
2615 */
2616 if (tbinfo->ispartition &&
2617 (dopt->load_via_partition_root ||
2620 else
2621 targettab = tbinfo;
2622
2623 appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2625
2626 /* corner case for zero-column table */
2627 if (nfields == 0)
2628 {
2629 appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2630 }
2631 else
2632 {
2633 /* append the list of column names if required */
2634 if (dopt->column_inserts)
2635 {
2637 for (int field = 0; field < nfields; field++)
2638 {
2639 if (field > 0)
2642 fmtId(PQfname(res, field)));
2643 }
2645 }
2646
2647 if (tbinfo->needs_override)
2648 appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2649
2651 }
2652 }
2653
2654 for (int tuple = 0; tuple < PQntuples(res); tuple++)
2655 {
2656 /* Write the INSERT if not in the middle of a multi-row INSERT. */
2657 if (rows_this_statement == 0)
2658 archputs(insertStmt->data, fout);
2659
2660 /*
2661 * If it is zero-column table then we've already written the
2662 * complete statement, which will mean we've disobeyed
2663 * --rows-per-insert when it's set greater than 1. We do support
2664 * a way to make this multi-row with: SELECT UNION ALL SELECT
2665 * UNION ALL ... but that's non-standard so we should avoid it
2666 * given that using INSERTs is mostly only ever needed for
2667 * cross-database exports.
2668 */
2669 if (nfields == 0)
2670 continue;
2671
2672 /* Emit a row heading */
2673 if (rows_per_statement == 1)
2674 archputs(" (", fout);
2675 else if (rows_this_statement > 0)
2676 archputs(",\n\t(", fout);
2677 else
2678 archputs("\n\t(", fout);
2679
2680 for (int field = 0; field < nfields; field++)
2681 {
2682 if (field > 0)
2683 archputs(", ", fout);
2684 if (attgenerated[field])
2685 {
2686 archputs("DEFAULT", fout);
2687 continue;
2688 }
2689 if (PQgetisnull(res, tuple, field))
2690 {
2691 archputs("NULL", fout);
2692 continue;
2693 }
2694
2695 /* XXX This code is partially duplicated in ruleutils.c */
2696 switch (PQftype(res, field))
2697 {
2698 case INT2OID:
2699 case INT4OID:
2700 case INT8OID:
2701 case OIDOID:
2702 case FLOAT4OID:
2703 case FLOAT8OID:
2704 case NUMERICOID:
2705 {
2706 /*
2707 * These types are printed without quotes unless
2708 * they contain values that aren't accepted by the
2709 * scanner unquoted (e.g., 'NaN'). Note that
2710 * strtod() and friends might accept NaN, so we
2711 * can't use that to test.
2712 *
2713 * In reality we only need to defend against
2714 * infinity and NaN, so we need not get too crazy
2715 * about pattern matching here.
2716 */
2717 const char *s = PQgetvalue(res, tuple, field);
2718
2719 if (strspn(s, "0123456789 +-eE.") == strlen(s))
2720 archputs(s, fout);
2721 else
2722 archprintf(fout, "'%s'", s);
2723 }
2724 break;
2725
2726 case BITOID:
2727 case VARBITOID:
2728 archprintf(fout, "B'%s'",
2729 PQgetvalue(res, tuple, field));
2730 break;
2731
2732 case BOOLOID:
2733 if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2734 archputs("true", fout);
2735 else
2736 archputs("false", fout);
2737 break;
2738
2739 default:
2740 /* All other types are printed as string literals. */
2743 PQgetvalue(res, tuple, field),
2744 fout);
2745 archputs(q->data, fout);
2746 break;
2747 }
2748 }
2749
2750 /* Terminate the row ... */
2751 archputs(")", fout);
2752
2753 /* ... and the statement, if the target no. of rows is reached */
2755 {
2756 if (dopt->do_nothing)
2757 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2758 else
2759 archputs(";\n", fout);
2760 /* Reset the row counter */
2762 }
2763 }
2764
2765 if (PQntuples(res) <= 0)
2766 {
2767 PQclear(res);
2768 break;
2769 }
2770 PQclear(res);
2771 }
2772
2773 /* Terminate any statements that didn't make the row count. */
2774 if (rows_this_statement > 0)
2775 {
2776 if (dopt->do_nothing)
2777 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2778 else
2779 archputs(";\n", fout);
2780 }
2781
2782 archputs("\n\n", fout);
2783
2784 ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2785
2787 if (insertStmt != NULL)
2789 free(attgenerated);
2790
2791 /* Revert back the setting */
2792 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2793 set_restrict_relation_kind(fout, "view, foreign-table");
2794
2795 return 1;
2796}
2797
2798/*
2799 * getRootTableInfo:
2800 * get the root TableInfo for the given partition table.
2801 */
2802static TableInfo *
2804{
2806
2807 Assert(tbinfo->ispartition);
2808 Assert(tbinfo->numParents == 1);
2809
2810 parentTbinfo = tbinfo->parents[0];
2811 while (parentTbinfo->ispartition)
2812 {
2813 Assert(parentTbinfo->numParents == 1);
2814 parentTbinfo = parentTbinfo->parents[0];
2815 }
2816
2817 return parentTbinfo;
2818}
2819
2820/*
2821 * forcePartitionRootLoad
2822 * Check if we must force load_via_partition_root for this partition.
2823 *
2824 * This is required if any level of ancestral partitioned table has an
2825 * unsafe partitioning scheme.
2826 */
2827static bool
2829{
2831
2832 Assert(tbinfo->ispartition);
2833 Assert(tbinfo->numParents == 1);
2834
2835 parentTbinfo = tbinfo->parents[0];
2836 if (parentTbinfo->unsafe_partitions)
2837 return true;
2838 while (parentTbinfo->ispartition)
2839 {
2840 Assert(parentTbinfo->numParents == 1);
2841 parentTbinfo = parentTbinfo->parents[0];
2842 if (parentTbinfo->unsafe_partitions)
2843 return true;
2844 }
2845
2846 return false;
2847}
2848
2849/*
2850 * dumpTableData -
2851 * dump the contents of a single table
2852 *
2853 * Actually, this just makes an ArchiveEntry for the table contents.
2854 */
2855static void
2857{
2858 DumpOptions *dopt = fout->dopt;
2859 const TableInfo *tbinfo = tdinfo->tdtable;
2862 DataDumperPtr dumpFn;
2863 char *tdDefn = NULL;
2864 char *copyStmt;
2865 const char *copyFrom;
2866
2867 /* We had better have loaded per-column details about this table */
2868 Assert(tbinfo->interesting);
2869
2870 /*
2871 * When load-via-partition-root is set or forced, get the root table name
2872 * for the partition table, so that we can reload data through the root
2873 * table. Then construct a comment to be inserted into the TOC entry's
2874 * defn field, so that such cases can be identified reliably.
2875 */
2876 if (tbinfo->ispartition &&
2877 (dopt->load_via_partition_root ||
2879 {
2880 const TableInfo *parentTbinfo;
2881 char *sanitized;
2882
2886 printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2887 sanitized);
2888 free(sanitized);
2889 tdDefn = pg_strdup(copyBuf->data);
2890 }
2891 else
2893
2894 if (dopt->dump_inserts == 0)
2895 {
2896 /* Dump/restore using COPY */
2897 dumpFn = dumpTableData_copy;
2898 /* must use 2 steps here 'cause fmtId is nonreentrant */
2899 printfPQExpBuffer(copyBuf, "COPY %s ",
2900 copyFrom);
2901 appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2903 copyStmt = copyBuf->data;
2904 }
2905 else
2906 {
2907 /* Restore using INSERT */
2908 dumpFn = dumpTableData_insert;
2909 copyStmt = NULL;
2910 }
2911
2912 /*
2913 * Note: although the TableDataInfo is a full DumpableObject, we treat its
2914 * dependency on its table as "special" and pass it to ArchiveEntry now.
2915 * See comments for BuildArchiveDependencies.
2916 */
2917 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2918 {
2919 TocEntry *te;
2920
2921 te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2922 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2923 .namespace = tbinfo->dobj.namespace->dobj.name,
2924 .owner = tbinfo->rolname,
2925 .description = "TABLE DATA",
2926 .section = SECTION_DATA,
2927 .createStmt = tdDefn,
2928 .copyStmt = copyStmt,
2929 .deps = &(tbinfo->dobj.dumpId),
2930 .nDeps = 1,
2931 .dumpFn = dumpFn,
2932 .dumpArg = tdinfo));
2933
2934 /*
2935 * Set the TocEntry's dataLength in case we are doing a parallel dump
2936 * and want to order dump jobs by table size. We choose to measure
2937 * dataLength in table pages (including TOAST pages) during dump, so
2938 * no scaling is needed.
2939 *
2940 * However, relpages is declared as "integer" in pg_class, and hence
2941 * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2942 * Cast so that we get the right interpretation of table sizes
2943 * exceeding INT_MAX pages.
2944 */
2945 te->dataLength = (BlockNumber) tbinfo->relpages;
2946 te->dataLength += (BlockNumber) tbinfo->toastpages;
2947
2948 /*
2949 * If pgoff_t is only 32 bits wide, the above refinement is useless,
2950 * and instead we'd better worry about integer overflow. Clamp to
2951 * INT_MAX if the correct result exceeds that.
2952 */
2953 if (sizeof(te->dataLength) == 4 &&
2954 (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2955 te->dataLength < 0))
2956 te->dataLength = INT_MAX;
2957 }
2958
2961}
2962
2963/*
2964 * refreshMatViewData -
2965 * load or refresh the contents of a single materialized view
2966 *
2967 * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2968 * statement.
2969 */
2970static void
2972{
2973 TableInfo *tbinfo = tdinfo->tdtable;
2974 PQExpBuffer q;
2975
2976 /* If the materialized view is not flagged as populated, skip this. */
2977 if (!tbinfo->relispopulated)
2978 return;
2979
2980 q = createPQExpBuffer();
2981
2982 appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2984
2985 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2987 tdinfo->dobj.catId, /* catalog ID */
2988 tdinfo->dobj.dumpId, /* dump ID */
2989 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2990 .namespace = tbinfo->dobj.namespace->dobj.name,
2991 .owner = tbinfo->rolname,
2992 .description = "MATERIALIZED VIEW DATA",
2993 .section = SECTION_POST_DATA,
2994 .createStmt = q->data,
2995 .deps = tdinfo->dobj.dependencies,
2996 .nDeps = tdinfo->dobj.nDeps));
2997
2999}
3000
3001/*
3002 * getTableData -
3003 * set up dumpable objects representing the contents of tables
3004 */
3005static void
3006getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
3007{
3008 int i;
3009
3010 for (i = 0; i < numTables; i++)
3011 {
3012 if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
3013 (!relkind || tblinfo[i].relkind == relkind))
3014 makeTableDataInfo(dopt, &(tblinfo[i]));
3015 }
3016}
3017
3018/*
3019 * Make a dumpable object for the data of this specific table
3020 *
3021 * Note: we make a TableDataInfo if and only if we are going to dump the
3022 * table data; the "dump" field in such objects isn't very interesting.
3023 */
3024static void
3026{
3028
3029 /*
3030 * Nothing to do if we already decided to dump the table. This will
3031 * happen for "config" tables.
3032 */
3033 if (tbinfo->dataObj != NULL)
3034 return;
3035
3036 /* Skip property graphs (no data to dump) */
3037 if (tbinfo->relkind == RELKIND_PROPGRAPH)
3038 return;
3039 /* Skip VIEWs (no data to dump) */
3040 if (tbinfo->relkind == RELKIND_VIEW)
3041 return;
3042 /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3043 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3046 tbinfo->foreign_server)))
3047 return;
3048 /* Skip partitioned tables (data in partitions) */
3049 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3050 return;
3051
3052 /* Don't dump data in unlogged tables, if so requested */
3053 if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3055 return;
3056
3057 /* Check that the data is not explicitly excluded */
3059 tbinfo->dobj.catId.oid))
3060 return;
3061
3062 /* OK, let's dump it */
3064
3065 if (tbinfo->relkind == RELKIND_MATVIEW)
3066 tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3067 else if (tbinfo->relkind == RELKIND_SEQUENCE)
3068 tdinfo->dobj.objType = DO_SEQUENCE_SET;
3069 else
3070 tdinfo->dobj.objType = DO_TABLE_DATA;
3071
3072 /*
3073 * Note: use tableoid 0 so that this object won't be mistaken for
3074 * something that pg_depend entries apply to.
3075 */
3076 tdinfo->dobj.catId.tableoid = 0;
3077 tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3078 AssignDumpId(&tdinfo->dobj);
3079 tdinfo->dobj.name = tbinfo->dobj.name;
3080 tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3081 tdinfo->tdtable = tbinfo;
3082 tdinfo->filtercond = NULL; /* might get set later */
3083 addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3084
3085 /* A TableDataInfo contains data, of course */
3086 tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3087
3088 tbinfo->dataObj = tdinfo;
3089
3090 /*
3091 * Materialized view statistics must be restored after the data, because
3092 * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3093 *
3094 * The dependency is added here because the statistics objects are created
3095 * first.
3096 */
3097 if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3098 {
3099 tbinfo->stats->section = SECTION_POST_DATA;
3100 addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3101 }
3102
3103 /* Make sure that we'll collect per-column info for this table. */
3104 tbinfo->interesting = true;
3105}
3106
3107/*
3108 * The refresh for a materialized view must be dependent on the refresh for
3109 * any materialized view that this one is dependent on.
3110 *
3111 * This must be called after all the objects are created, but before they are
3112 * sorted.
3113 */
3114static void
3116{
3117 PQExpBuffer query;
3118 PGresult *res;
3119 int ntups,
3120 i;
3121 int i_classid,
3122 i_objid,
3123 i_refobjid;
3124
3125 /* No Mat Views before 9.3. */
3126 if (fout->remoteVersion < 90300)
3127 return;
3128
3129 query = createPQExpBuffer();
3130
3131 appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3132 "( "
3133 "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3134 "FROM pg_depend d1 "
3135 "JOIN pg_class c1 ON c1.oid = d1.objid "
3136 "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3137 " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3138 "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3139 "AND d2.objid = r1.oid "
3140 "AND d2.refobjid <> d1.objid "
3141 "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3142 "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3144 "WHERE d1.classid = 'pg_class'::regclass "
3145 "UNION "
3146 "SELECT w.objid, d3.refobjid, c3.relkind "
3147 "FROM w "
3148 "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3149 "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3150 "AND d3.objid = r3.oid "
3151 "AND d3.refobjid <> w.refobjid "
3152 "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3153 "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3155 ") "
3156 "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3157 "FROM w "
3158 "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3159
3160 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3161
3162 ntups = PQntuples(res);
3163
3164 i_classid = PQfnumber(res, "classid");
3165 i_objid = PQfnumber(res, "objid");
3166 i_refobjid = PQfnumber(res, "refobjid");
3167
3168 for (i = 0; i < ntups; i++)
3169 {
3170 CatalogId objId;
3172 DumpableObject *dobj;
3176
3177 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3178 objId.oid = atooid(PQgetvalue(res, i, i_objid));
3179 refobjId.tableoid = objId.tableoid;
3180 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3181
3182 dobj = findObjectByCatalogId(objId);
3183 if (dobj == NULL)
3184 continue;
3185
3186 Assert(dobj->objType == DO_TABLE);
3187 tbinfo = (TableInfo *) dobj;
3188 Assert(tbinfo->relkind == RELKIND_MATVIEW);
3189 dobj = (DumpableObject *) tbinfo->dataObj;
3190 if (dobj == NULL)
3191 continue;
3193
3195 if (refdobj == NULL)
3196 continue;
3197
3198 Assert(refdobj->objType == DO_TABLE);
3200 Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3201 refdobj = (DumpableObject *) reftbinfo->dataObj;
3202 if (refdobj == NULL)
3203 continue;
3204 Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3205
3206 addObjectDependency(dobj, refdobj->dumpId);
3207
3208 if (!reftbinfo->relispopulated)
3209 tbinfo->relispopulated = false;
3210 }
3211
3212 PQclear(res);
3213
3214 destroyPQExpBuffer(query);
3215}
3216
3217/*
3218 * getTableDataFKConstraints -
3219 * add dump-order dependencies reflecting foreign key constraints
3220 *
3221 * This code is executed only in a data-only dump --- in schema+data dumps
3222 * we handle foreign key issues by not creating the FK constraints until
3223 * after the data is loaded. In a data-only dump, however, we want to
3224 * order the table data objects in such a way that a table's referenced
3225 * tables are restored first. (In the presence of circular references or
3226 * self-references this may be impossible; we'll detect and complain about
3227 * that during the dependency sorting step.)
3228 */
3229static void
3231{
3233 int numObjs;
3234 int i;
3235
3236 /* Search through all the dumpable objects for FK constraints */
3238 for (i = 0; i < numObjs; i++)
3239 {
3240 if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3241 {
3244
3245 /* Not interesting unless both tables are to be dumped */
3246 if (cinfo->contable == NULL ||
3247 cinfo->contable->dataObj == NULL)
3248 continue;
3249 ftable = findTableByOid(cinfo->confrelid);
3250 if (ftable == NULL ||
3251 ftable->dataObj == NULL)
3252 continue;
3253
3254 /*
3255 * Okay, make referencing table's TABLE_DATA object depend on the
3256 * referenced table's TABLE_DATA object.
3257 */
3258 addObjectDependency(&cinfo->contable->dataObj->dobj,
3259 ftable->dataObj->dobj.dumpId);
3260 }
3261 }
3262 free(dobjs);
3263}
3264
3265
3266/*
3267 * dumpDatabase:
3268 * dump the database definition
3269 */
3270static void
3272{
3273 DumpOptions *dopt = fout->dopt;
3279 PGresult *res;
3280 int i_tableoid,
3281 i_oid,
3282 i_datname,
3283 i_datdba,
3284 i_encoding,
3286 i_collate,
3287 i_ctype,
3291 i_minmxid,
3292 i_datacl,
3301 const char *datname,
3302 *dba,
3303 *encoding,
3305 *collate,
3306 *ctype,
3307 *locale,
3308 *icurules,
3310 *datconnlimit,
3311 *tablespace;
3312 uint32 frozenxid,
3313 minmxid;
3314 char *qdatname;
3315
3316 pg_log_info("saving database definition");
3317
3318 /*
3319 * Fetch the database-level properties for this database.
3320 */
3321 appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3322 "datdba, "
3323 "pg_encoding_to_char(encoding) AS encoding, "
3324 "datcollate, datctype, datfrozenxid, "
3325 "datacl, acldefault('d', datdba) AS acldefault, "
3326 "datistemplate, datconnlimit, ");
3327 if (fout->remoteVersion >= 90300)
3328 appendPQExpBufferStr(dbQry, "datminmxid, ");
3329 else
3330 appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3331 if (fout->remoteVersion >= 170000)
3332 appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3333 else if (fout->remoteVersion >= 150000)
3334 appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3335 else
3336 appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3337 if (fout->remoteVersion >= 160000)
3338 appendPQExpBufferStr(dbQry, "daticurules, ");
3339 else
3340 appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3342 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3343 "shobj_description(oid, 'pg_database') AS description "
3344 "FROM pg_database "
3345 "WHERE datname = current_database()");
3346
3348
3349 i_tableoid = PQfnumber(res, "tableoid");
3350 i_oid = PQfnumber(res, "oid");
3351 i_datname = PQfnumber(res, "datname");
3352 i_datdba = PQfnumber(res, "datdba");
3353 i_encoding = PQfnumber(res, "encoding");
3354 i_datlocprovider = PQfnumber(res, "datlocprovider");
3355 i_collate = PQfnumber(res, "datcollate");
3356 i_ctype = PQfnumber(res, "datctype");
3357 i_datlocale = PQfnumber(res, "datlocale");
3358 i_daticurules = PQfnumber(res, "daticurules");
3359 i_frozenxid = PQfnumber(res, "datfrozenxid");
3360 i_minmxid = PQfnumber(res, "datminmxid");
3361 i_datacl = PQfnumber(res, "datacl");
3362 i_acldefault = PQfnumber(res, "acldefault");
3363 i_datistemplate = PQfnumber(res, "datistemplate");
3364 i_datconnlimit = PQfnumber(res, "datconnlimit");
3365 i_datcollversion = PQfnumber(res, "datcollversion");
3366 i_tablespace = PQfnumber(res, "tablespace");
3367
3368 dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3369 dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3370 datname = PQgetvalue(res, 0, i_datname);
3371 dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3372 encoding = PQgetvalue(res, 0, i_encoding);
3374 collate = PQgetvalue(res, 0, i_collate);
3375 ctype = PQgetvalue(res, 0, i_ctype);
3376 if (!PQgetisnull(res, 0, i_datlocale))
3377 locale = PQgetvalue(res, 0, i_datlocale);
3378 else
3379 locale = NULL;
3380 if (!PQgetisnull(res, 0, i_daticurules))
3382 else
3383 icurules = NULL;
3384 frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3385 minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3386 dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3387 dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3391
3393
3394 /*
3395 * Prepare the CREATE DATABASE command. We must specify OID (if we want
3396 * to preserve that), as well as the encoding, locale, and tablespace
3397 * since those can't be altered later. Other DB properties are left to
3398 * the DATABASE PROPERTIES entry, so that they can be applied after
3399 * reconnecting to the target DB.
3400 *
3401 * For binary upgrade, we use the FILE_COPY strategy because testing has
3402 * shown it to be faster. When the server is in binary upgrade mode, it
3403 * will also skip the checkpoints this strategy ordinarily performs.
3404 */
3405 if (dopt->binary_upgrade)
3406 {
3408 "CREATE DATABASE %s WITH TEMPLATE = template0 "
3409 "OID = %u STRATEGY = FILE_COPY",
3410 qdatname, dbCatId.oid);
3411 }
3412 else
3413 {
3414 appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3415 qdatname);
3416 }
3417 if (strlen(encoding) > 0)
3418 {
3419 appendPQExpBufferStr(creaQry, " ENCODING = ");
3421 }
3422
3423 appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3424 if (datlocprovider[0] == 'b')
3425 appendPQExpBufferStr(creaQry, "builtin");
3426 else if (datlocprovider[0] == 'c')
3428 else if (datlocprovider[0] == 'i')
3430 else
3431 pg_fatal("unrecognized locale provider: %s",
3433
3434 if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3435 {
3436 appendPQExpBufferStr(creaQry, " LOCALE = ");
3438 }
3439 else
3440 {
3441 if (strlen(collate) > 0)
3442 {
3443 appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3445 }
3446 if (strlen(ctype) > 0)
3447 {
3448 appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3450 }
3451 }
3452 if (locale)
3453 {
3454 if (datlocprovider[0] == 'b')
3455 appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3456 else
3457 appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3458
3460 }
3461
3462 if (icurules)
3463 {
3464 appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3466 }
3467
3468 /*
3469 * For binary upgrade, carry over the collation version. For normal
3470 * dump/restore, omit the version, so that it is computed upon restore.
3471 */
3472 if (dopt->binary_upgrade)
3473 {
3474 if (!PQgetisnull(res, 0, i_datcollversion))
3475 {
3476 appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3479 fout);
3480 }
3481 }
3482
3483 /*
3484 * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3485 * thing; the decision whether to specify a tablespace should be left till
3486 * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3487 * label the DATABASE entry with the tablespace and let the normal
3488 * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3489 * attention to default_tablespace, so that won't work.
3490 */
3491 if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3492 !dopt->outputNoTablespaces)
3493 appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3494 fmtId(tablespace));
3496
3497 appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3498 qdatname);
3499
3501
3503 dbCatId, /* catalog ID */
3504 dbDumpId, /* dump ID */
3505 ARCHIVE_OPTS(.tag = datname,
3506 .owner = dba,
3507 .description = "DATABASE",
3508 .section = SECTION_PRE_DATA,
3509 .createStmt = creaQry->data,
3510 .dropStmt = delQry->data));
3511
3512 /* Compute correct tag for archive entry */
3513 appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3514
3515 /* Dump DB comment if any */
3516 {
3517 /*
3518 * 8.2 and up keep comments on shared objects in a shared table, so we
3519 * cannot use the dumpComment() code used for other database objects.
3520 * Be careful that the ArchiveEntry parameters match that function.
3521 */
3522 char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3523
3524 if (comment && *comment && !dopt->no_comments)
3525 {
3527
3528 /*
3529 * Generates warning when loaded into a differently-named
3530 * database.
3531 */
3532 appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3535
3537 ARCHIVE_OPTS(.tag = labelq->data,
3538 .owner = dba,
3539 .description = "COMMENT",
3540 .section = SECTION_NONE,
3541 .createStmt = dbQry->data,
3542 .deps = &dbDumpId,
3543 .nDeps = 1));
3544 }
3545 }
3546
3547 /* Dump DB security label, if enabled */
3548 if (!dopt->no_security_labels)
3549 {
3550 PGresult *shres;
3552
3554
3555 buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3559 if (seclabelQry->len > 0)
3561 ARCHIVE_OPTS(.tag = labelq->data,
3562 .owner = dba,
3563 .description = "SECURITY LABEL",
3564 .section = SECTION_NONE,
3565 .createStmt = seclabelQry->data,
3566 .deps = &dbDumpId,
3567 .nDeps = 1));
3569 PQclear(shres);
3570 }
3571
3572 /*
3573 * Dump ACL if any. Note that we do not support initial privileges
3574 * (pg_init_privs) on databases.
3575 */
3576 dbdacl.privtype = 0;
3577 dbdacl.initprivs = NULL;
3578
3579 dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3580 qdatname, NULL, NULL,
3581 NULL, dba, &dbdacl);
3582
3583 /*
3584 * Now construct a DATABASE PROPERTIES archive entry to restore any
3585 * non-default database-level properties. (The reason this must be
3586 * separate is that we cannot put any additional commands into the TOC
3587 * entry that has CREATE DATABASE. pg_restore would execute such a group
3588 * in an implicit transaction block, and the backend won't allow CREATE
3589 * DATABASE in that context.)
3590 */
3593
3594 if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3595 appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3597
3598 if (strcmp(datistemplate, "t") == 0)
3599 {
3600 appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3601 qdatname);
3602
3603 /*
3604 * The backend won't accept DROP DATABASE on a template database. We
3605 * can deal with that by removing the template marking before the DROP
3606 * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3607 * since no such command is currently supported, fake it with a direct
3608 * UPDATE on pg_database.
3609 */
3610 appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3611 "SET datistemplate = false WHERE datname = ");
3614 }
3615
3616 /*
3617 * We do not restore pg_database.dathasloginevt because it is set
3618 * automatically on login event trigger creation.
3619 */
3620
3621 /* Add database-specific SET options */
3623
3624 /*
3625 * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3626 * entry, too, for lack of a better place.
3627 */
3628 if (dopt->binary_upgrade)
3629 {
3630 appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3631 appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3632 "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3633 "WHERE datname = ",
3634 frozenxid, minmxid);
3637 }
3638
3639 if (creaQry->len > 0)
3641 ARCHIVE_OPTS(.tag = datname,
3642 .owner = dba,
3643 .description = "DATABASE PROPERTIES",
3644 .section = SECTION_PRE_DATA,
3645 .createStmt = creaQry->data,
3646 .dropStmt = delQry->data,
3647 .deps = &dbDumpId));
3648
3649 /*
3650 * pg_largeobject comes from the old system intact, so set its
3651 * relfrozenxids, relminmxids and relfilenode.
3652 *
3653 * pg_largeobject_metadata also comes from the old system intact for
3654 * upgrades from v16 and newer, so set its relfrozenxids, relminmxids, and
3655 * relfilenode, too. pg_upgrade can't copy/link the files from older
3656 * versions because aclitem (needed by pg_largeobject_metadata.lomacl)
3657 * changed its storage format in v16.
3658 */
3659 if (dopt->binary_upgrade)
3660 {
3667 int ii_relfrozenxid,
3669 ii_oid,
3671
3672 if (fout->remoteVersion >= 90300)
3673 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3674 "FROM pg_catalog.pg_class\n"
3675 "WHERE oid IN (%u, %u, %u, %u);\n",
3678 else
3679 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3680 "FROM pg_catalog.pg_class\n"
3681 "WHERE oid IN (%u, %u);\n",
3683
3685
3686 ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3687 ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3688 ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3689 ii_oid = PQfnumber(lo_res, "oid");
3690
3691 appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3692 appendPQExpBufferStr(lomHorizonQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
3693 appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3694 appendPQExpBufferStr(lomOutQry, "\n-- For binary upgrade, preserve pg_largeobject_metadata and index relfilenodes\n");
3695 for (int i = 0; i < PQntuples(lo_res); ++i)
3696 {
3697 Oid oid;
3698 RelFileNumber relfilenumber;
3701
3702 oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3703 relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3704
3705 if (oid == LargeObjectRelationId ||
3707 {
3709 outQry = loOutQry;
3710 }
3711 else
3712 {
3714 outQry = lomOutQry;
3715 }
3716
3717 appendPQExpBuffer(horizonQry, "UPDATE pg_catalog.pg_class\n"
3718 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3719 "WHERE oid = %u;\n",
3723
3724 if (oid == LargeObjectRelationId ||
3727 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3728 relfilenumber);
3729 else if (oid == LargeObjectLOidPNIndexId ||
3732 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3733 relfilenumber);
3734 }
3735
3737 "TRUNCATE pg_catalog.pg_largeobject;\n");
3739 "TRUNCATE pg_catalog.pg_largeobject_metadata;\n");
3740
3743
3745 ARCHIVE_OPTS(.tag = "pg_largeobject",
3746 .description = "pg_largeobject",
3747 .section = SECTION_PRE_DATA,
3748 .createStmt = loOutQry->data));
3749
3750 if (fout->remoteVersion >= 160000)
3752 ARCHIVE_OPTS(.tag = "pg_largeobject_metadata",
3753 .description = "pg_largeobject_metadata",
3754 .section = SECTION_PRE_DATA,
3755 .createStmt = lomOutQry->data));
3756
3757 PQclear(lo_res);
3758
3764 }
3765
3766 PQclear(res);
3767
3768 free(qdatname);
3773}
3774
3775/*
3776 * Collect any database-specific or role-and-database-specific SET options
3777 * for this database, and append them to outbuf.
3778 */
3779static void
3781 const char *dbname, Oid dboid)
3782{
3783 PGconn *conn = GetConnection(AH);
3785 PGresult *res;
3786
3787 /* First collect database-specific options */
3788 printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3789 "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3790 dboid);
3791
3792 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3793
3794 for (int i = 0; i < PQntuples(res); i++)
3796 "DATABASE", dbname, NULL, NULL,
3797 outbuf);
3798
3799 PQclear(res);
3800
3801 /* Now look for role-and-database-specific options */
3802 printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3803 "FROM pg_db_role_setting s, pg_roles r "
3804 "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3805 dboid);
3806
3807 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3808
3809 for (int i = 0; i < PQntuples(res); i++)
3811 "ROLE", PQgetvalue(res, i, 0),
3812 "DATABASE", dbname,
3813 outbuf);
3814
3815 PQclear(res);
3816
3818}
3819
3820/*
3821 * dumpEncoding: put the correct encoding into the archive
3822 */
3823static void
3825{
3826 const char *encname = pg_encoding_to_char(AH->encoding);
3828
3829 pg_log_info("saving encoding = %s", encname);
3830
3831 appendPQExpBufferStr(qry, "SET client_encoding = ");
3833 appendPQExpBufferStr(qry, ";\n");
3834
3836 ARCHIVE_OPTS(.tag = "ENCODING",
3837 .description = "ENCODING",
3838 .section = SECTION_PRE_DATA,
3839 .createStmt = qry->data));
3840
3841 destroyPQExpBuffer(qry);
3842}
3843
3844
3845/*
3846 * dumpStdStrings: put the correct escape string behavior into the archive
3847 */
3848static void
3850{
3851 const char *stdstrings = AH->std_strings ? "on" : "off";
3853
3854 pg_log_info("saving \"standard_conforming_strings = %s\"",
3855 stdstrings);
3856
3857 appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3858 stdstrings);
3859
3861 ARCHIVE_OPTS(.tag = "STDSTRINGS",
3862 .description = "STDSTRINGS",
3863 .section = SECTION_PRE_DATA,
3864 .createStmt = qry->data));
3865
3866 destroyPQExpBuffer(qry);
3867}
3868
3869/*
3870 * dumpSearchPath: record the active search_path in the archive
3871 */
3872static void
3874{
3877 PGresult *res;
3878 char **schemanames = NULL;
3879 int nschemanames = 0;
3880 int i;
3881
3882 /*
3883 * We use the result of current_schemas(), not the search_path GUC,
3884 * because that might contain wildcards such as "$user", which won't
3885 * necessarily have the same value during restore. Also, this way avoids
3886 * listing schemas that may appear in search_path but not actually exist,
3887 * which seems like a prudent exclusion.
3888 */
3890 "SELECT pg_catalog.current_schemas(false)");
3891
3892 if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3893 pg_fatal("could not parse result of current_schemas()");
3894
3895 /*
3896 * We use set_config(), not a simple "SET search_path" command, because
3897 * the latter has less-clean behavior if the search path is empty. While
3898 * that's likely to get fixed at some point, it seems like a good idea to
3899 * be as backwards-compatible as possible in what we put into archives.
3900 */
3901 for (i = 0; i < nschemanames; i++)
3902 {
3903 if (i > 0)
3904 appendPQExpBufferStr(path, ", ");
3906 }
3907
3908 appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3909 appendStringLiteralAH(qry, path->data, AH);
3910 appendPQExpBufferStr(qry, ", false);\n");
3911
3912 pg_log_info("saving \"search_path = %s\"", path->data);
3913
3915 ARCHIVE_OPTS(.tag = "SEARCHPATH",
3916 .description = "SEARCHPATH",
3917 .section = SECTION_PRE_DATA,
3918 .createStmt = qry->data));
3919
3920 /* Also save it in AH->searchpath, in case we're doing plain text dump */
3921 AH->searchpath = pg_strdup(qry->data);
3922
3924 PQclear(res);
3925 destroyPQExpBuffer(qry);
3926 destroyPQExpBuffer(path);
3927}
3928
3929
3930/*
3931 * getLOs:
3932 * Collect schema-level data about large objects
3933 */
3934static void
3936{
3937 DumpOptions *dopt = fout->dopt;
3939 PGresult *res;
3940 int ntups;
3941 int i;
3942 int n;
3943 int i_oid;
3944 int i_lomowner;
3945 int i_lomacl;
3946 int i_acldefault;
3947
3948 pg_log_info("reading large objects");
3949
3950 /*
3951 * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3952 * with the same owner/ACL appear together.
3953 */
3955 "SELECT oid, lomowner, lomacl, "
3956 "acldefault('L', lomowner) AS acldefault "
3957 "FROM pg_largeobject_metadata ");
3958
3959 /*
3960 * For binary upgrades, we transfer pg_largeobject_metadata via COPY or by
3961 * copying/linking its files from the old cluster. On such upgrades, we
3962 * only need to consider large objects that have comments or security
3963 * labels, since we still restore those objects via COMMENT/SECURITY LABEL
3964 * commands.
3965 */
3966 if (dopt->binary_upgrade)
3968 "WHERE oid IN "
3969 "(SELECT objoid FROM pg_description "
3970 "WHERE classoid = " CppAsString2(LargeObjectRelationId) " "
3971 "UNION SELECT objoid FROM pg_seclabel "
3972 "WHERE classoid = " CppAsString2(LargeObjectRelationId) ") ");
3973
3975 "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3976
3978
3979 i_oid = PQfnumber(res, "oid");
3980 i_lomowner = PQfnumber(res, "lomowner");
3981 i_lomacl = PQfnumber(res, "lomacl");
3982 i_acldefault = PQfnumber(res, "acldefault");
3983
3984 ntups = PQntuples(res);
3985
3986 /*
3987 * Group the blobs into suitably-sized groups that have the same owner and
3988 * ACL setting, and build a metadata and a data DumpableObject for each
3989 * group. (If we supported initprivs for blobs, we'd have to insist that
3990 * groups also share initprivs settings, since the DumpableObject only has
3991 * room for one.) i is the index of the first tuple in the current group,
3992 * and n is the number of tuples we include in the group.
3993 */
3994 for (i = 0; i < ntups; i += n)
3995 {
3996 Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3997 char *thisowner = PQgetvalue(res, i, i_lomowner);
3998 char *thisacl = PQgetvalue(res, i, i_lomacl);
3999 LoInfo *loinfo;
4001 char namebuf[64];
4002
4003 /* Scan to find first tuple not to be included in group */
4004 n = 1;
4005 while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
4006 {
4007 if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
4008 strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
4009 break;
4010 n++;
4011 }
4012
4013 /* Build the metadata DumpableObject */
4014 loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
4015
4017 loinfo->dobj.catId.tableoid = LargeObjectRelationId;
4018 loinfo->dobj.catId.oid = thisoid;
4019 AssignDumpId(&loinfo->dobj);
4020
4021 if (n > 1)
4022 snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
4023 atooid(PQgetvalue(res, i + n - 1, i_oid)));
4024 else
4025 snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
4026 loinfo->dobj.name = pg_strdup(namebuf);
4027 loinfo->dacl.acl = pg_strdup(thisacl);
4028 loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
4029 loinfo->dacl.privtype = 0;
4030 loinfo->dacl.initprivs = NULL;
4031 loinfo->rolname = getRoleName(thisowner);
4032 loinfo->numlos = n;
4033 loinfo->looids[0] = thisoid;
4034 /* Collect OIDs of the remaining blobs in this group */
4035 for (int k = 1; k < n; k++)
4036 {
4038
4039 loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
4040
4041 /* Make sure we can look up loinfo by any of the blobs' OIDs */
4042 extraID.tableoid = LargeObjectRelationId;
4043 extraID.oid = loinfo->looids[k];
4045 }
4046
4047 /* LOs have data */
4048 loinfo->dobj.components |= DUMP_COMPONENT_DATA;
4049
4050 /* Mark whether LO group has a non-empty ACL */
4051 if (!PQgetisnull(res, i, i_lomacl))
4052 loinfo->dobj.components |= DUMP_COMPONENT_ACL;
4053
4054 /*
4055 * In binary upgrade mode, pg_largeobject and pg_largeobject_metadata
4056 * are transferred via COPY or by copying/linking the files from the
4057 * old cluster. Thus, we do not need to dump LO data, definitions, or
4058 * ACLs.
4059 */
4060 if (dopt->binary_upgrade)
4062
4063 /*
4064 * Create a "BLOBS" data item for the group, too. This is just a
4065 * placeholder for sorting; it carries no data now.
4066 */
4068 lodata->objType = DO_LARGE_OBJECT_DATA;
4069 lodata->catId = nilCatalogId;
4071 lodata->name = pg_strdup(namebuf);
4072 lodata->components |= DUMP_COMPONENT_DATA;
4073 /* Set up explicit dependency from data to metadata */
4074 lodata->dependencies = pg_malloc_object(DumpId);
4075 lodata->dependencies[0] = loinfo->dobj.dumpId;
4076 lodata->nDeps = lodata->allocDeps = 1;
4077 }
4078
4079 PQclear(res);
4081}
4082
4083/*
4084 * dumpLO
4085 *
4086 * dump the definition (metadata) of the given large object group
4087 */
4088static void
4090{
4092
4093 /*
4094 * The "definition" is just a newline-separated list of OIDs. We need to
4095 * put something into the dropStmt too, but it can just be a comment.
4096 */
4097 for (int i = 0; i < loinfo->numlos; i++)
4098 appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4099
4100 if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4101 ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4102 ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4103 .owner = loinfo->rolname,
4104 .description = "BLOB METADATA",
4105 .section = SECTION_DATA,
4106 .createStmt = cquery->data,
4107 .dropStmt = "-- dummy"));
4108
4109 /*
4110 * Dump per-blob comments and seclabels if any. We assume these are rare
4111 * enough that it's okay to generate retail TOC entries for them.
4112 */
4113 if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4115 {
4116 for (int i = 0; i < loinfo->numlos; i++)
4117 {
4118 CatalogId catId;
4119 char namebuf[32];
4120
4121 /* Build identifying info for this blob */
4122 catId.tableoid = loinfo->dobj.catId.tableoid;
4123 catId.oid = loinfo->looids[i];
4124 snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4125
4126 if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4127 dumpComment(fout, "LARGE OBJECT", namebuf,
4128 NULL, loinfo->rolname,
4129 catId, 0, loinfo->dobj.dumpId);
4130
4131 if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4132 dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4133 NULL, loinfo->rolname,
4134 catId, 0, loinfo->dobj.dumpId);
4135 }
4136 }
4137
4138 /*
4139 * Dump the ACLs if any (remember that all blobs in the group will have
4140 * the same ACL). If there's just one blob, dump a simple ACL entry; if
4141 * there's more, make a "LARGE OBJECTS" entry that really contains only
4142 * the ACL for the first blob. _printTocEntry() will be cued by the tag
4143 * string to emit a mutated version for each blob.
4144 */
4145 if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4146 {
4147 char namebuf[32];
4148
4149 /* Build identifying info for the first blob */
4150 snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4151
4152 if (loinfo->numlos > 1)
4153 {
4154 char tagbuf[64];
4155
4156 snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4157 loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4158
4159 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4160 "LARGE OBJECT", namebuf, NULL, NULL,
4161 tagbuf, loinfo->rolname, &loinfo->dacl);
4162 }
4163 else
4164 {
4165 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4166 "LARGE OBJECT", namebuf, NULL, NULL,
4167 NULL, loinfo->rolname, &loinfo->dacl);
4168 }
4169 }
4170
4172}
4173
4174/*
4175 * dumpLOs:
4176 * dump the data contents of the large objects in the given group
4177 */
4178static int
4179dumpLOs(Archive *fout, const void *arg)
4180{
4181 const LoInfo *loinfo = (const LoInfo *) arg;
4183 char buf[LOBBUFSIZE];
4184
4185 pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4186
4187 for (int i = 0; i < loinfo->numlos; i++)
4188 {
4189 Oid loOid = loinfo->looids[i];
4190 int loFd;
4191 int cnt;
4192
4193 /* Open the LO */
4194 loFd = lo_open(conn, loOid, INV_READ);
4195 if (loFd == -1)
4196 pg_fatal("could not open large object %u: %s",
4198
4199 StartLO(fout, loOid);
4200
4201 /* Now read it in chunks, sending data to archive */
4202 do
4203 {
4204 cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4205 if (cnt < 0)
4206 pg_fatal("error reading large object %u: %s",
4208
4209 WriteData(fout, buf, cnt);
4210 } while (cnt > 0);
4211
4212 lo_close(conn, loFd);
4213
4214 EndLO(fout, loOid);
4215 }
4216
4217 return 1;
4218}
4219
4220/*
4221 * getPolicies
4222 * get information about all RLS policies on dumpable tables.
4223 */
4224void
4225getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4226{
4227 DumpOptions *dopt = fout->dopt;
4228 PQExpBuffer query;
4230 PGresult *res;
4232 int i_oid;
4233 int i_tableoid;
4234 int i_polrelid;
4235 int i_polname;
4236 int i_polcmd;
4237 int i_polpermissive;
4238 int i_polroles;
4239 int i_polqual;
4240 int i_polwithcheck;
4241 int i,
4242 j,
4243 ntups;
4244
4245 /* No policies before 9.5 */
4246 if (fout->remoteVersion < 90500)
4247 return;
4248
4249 /* Skip if --no-policies was specified */
4250 if (dopt->no_policies)
4251 return;
4252
4253 query = createPQExpBuffer();
4255
4256 /*
4257 * Identify tables of interest, and check which ones have RLS enabled.
4258 */
4260 for (i = 0; i < numTables; i++)
4261 {
4262 TableInfo *tbinfo = &tblinfo[i];
4263
4264 /* Ignore row security on tables not to be dumped */
4265 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4266 continue;
4267
4268 /* It can't have RLS or policies if it's not a table */
4269 if (tbinfo->relkind != RELKIND_RELATION &&
4271 continue;
4272
4273 /* Add it to the list of table OIDs to be probed below */
4274 if (tbloids->len > 1) /* do we have more than the '{'? */
4276 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4277
4278 /* Is RLS enabled? (That's separate from whether it has policies) */
4279 if (tbinfo->rowsec)
4280 {
4281 tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4282
4283 /*
4284 * We represent RLS being enabled on a table by creating a
4285 * PolicyInfo object with null polname.
4286 *
4287 * Note: use tableoid 0 so that this object won't be mistaken for
4288 * something that pg_depend entries apply to.
4289 */
4291 polinfo->dobj.objType = DO_POLICY;
4292 polinfo->dobj.catId.tableoid = 0;
4293 polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4294 AssignDumpId(&polinfo->dobj);
4295 polinfo->dobj.namespace = tbinfo->dobj.namespace;
4296 polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4297 polinfo->poltable = tbinfo;
4298 polinfo->polname = NULL;
4299 polinfo->polcmd = '\0';
4300 polinfo->polpermissive = 0;
4301 polinfo->polroles = NULL;
4302 polinfo->polqual = NULL;
4303 polinfo->polwithcheck = NULL;
4304 }
4305 }
4307
4308 /*
4309 * Now, read all RLS policies belonging to the tables of interest, and
4310 * create PolicyInfo objects for them. (Note that we must filter the
4311 * results server-side not locally, because we dare not apply pg_get_expr
4312 * to tables we don't have lock on.)
4313 */
4314 pg_log_info("reading row-level security policies");
4315
4316 printfPQExpBuffer(query,
4317 "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4318 if (fout->remoteVersion >= 100000)
4319 appendPQExpBufferStr(query, "pol.polpermissive, ");
4320 else
4321 appendPQExpBufferStr(query, "'t' as polpermissive, ");
4322 appendPQExpBuffer(query,
4323 "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4324 " pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
4325 "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4326 "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4327 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4328 "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4329 tbloids->data);
4330
4331 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4332
4333 ntups = PQntuples(res);
4334 if (ntups > 0)
4335 {
4336 i_oid = PQfnumber(res, "oid");
4337 i_tableoid = PQfnumber(res, "tableoid");
4338 i_polrelid = PQfnumber(res, "polrelid");
4339 i_polname = PQfnumber(res, "polname");
4340 i_polcmd = PQfnumber(res, "polcmd");
4341 i_polpermissive = PQfnumber(res, "polpermissive");
4342 i_polroles = PQfnumber(res, "polroles");
4343 i_polqual = PQfnumber(res, "polqual");
4344 i_polwithcheck = PQfnumber(res, "polwithcheck");
4345
4347
4348 for (j = 0; j < ntups; j++)
4349 {
4352
4353 tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4354
4355 polinfo[j].dobj.objType = DO_POLICY;
4356 polinfo[j].dobj.catId.tableoid =
4357 atooid(PQgetvalue(res, j, i_tableoid));
4358 polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4359 AssignDumpId(&polinfo[j].dobj);
4360 polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4361 polinfo[j].poltable = tbinfo;
4362 polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4363 polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4364
4365 polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4366 polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4367
4368 if (PQgetisnull(res, j, i_polroles))
4369 polinfo[j].polroles = NULL;
4370 else
4371 polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4372
4373 if (PQgetisnull(res, j, i_polqual))
4374 polinfo[j].polqual = NULL;
4375 else
4376 polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4377
4378 if (PQgetisnull(res, j, i_polwithcheck))
4379 polinfo[j].polwithcheck = NULL;
4380 else
4381 polinfo[j].polwithcheck
4383 }
4384 }
4385
4386 PQclear(res);
4387
4388 destroyPQExpBuffer(query);
4390}
4391
4392/*
4393 * dumpPolicy
4394 * dump the definition of the given policy
4395 */
4396static void
4398{
4399 DumpOptions *dopt = fout->dopt;
4400 TableInfo *tbinfo = polinfo->poltable;
4401 PQExpBuffer query;
4404 char *qtabname;
4405 const char *cmd;
4406 char *tag;
4407
4408 /* Do nothing if not dumping schema */
4409 if (!dopt->dumpSchema)
4410 return;
4411
4412 /*
4413 * If polname is NULL, then this record is just indicating that ROW LEVEL
4414 * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4415 * ROW LEVEL SECURITY.
4416 */
4417 if (polinfo->polname == NULL)
4418 {
4419 query = createPQExpBuffer();
4420
4421 appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4423
4424 /*
4425 * We must emit the ROW SECURITY object's dependency on its table
4426 * explicitly, because it will not match anything in pg_depend (unlike
4427 * the case for other PolicyInfo objects).
4428 */
4429 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4430 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4431 ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4432 .namespace = polinfo->dobj.namespace->dobj.name,
4433 .owner = tbinfo->rolname,
4434 .description = "ROW SECURITY",
4435 .section = SECTION_POST_DATA,
4436 .createStmt = query->data,
4437 .deps = &(tbinfo->dobj.dumpId),
4438 .nDeps = 1));
4439
4440 destroyPQExpBuffer(query);
4441 return;
4442 }
4443
4444 if (polinfo->polcmd == '*')
4445 cmd = "";
4446 else if (polinfo->polcmd == 'r')
4447 cmd = " FOR SELECT";
4448 else if (polinfo->polcmd == 'a')
4449 cmd = " FOR INSERT";
4450 else if (polinfo->polcmd == 'w')
4451 cmd = " FOR UPDATE";
4452 else if (polinfo->polcmd == 'd')
4453 cmd = " FOR DELETE";
4454 else
4455 pg_fatal("unexpected policy command type: %c",
4456 polinfo->polcmd);
4457
4458 query = createPQExpBuffer();
4461
4462 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4463
4464 appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4465
4466 appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4467 !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4468
4469 if (polinfo->polroles != NULL)
4470 appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4471
4472 if (polinfo->polqual != NULL)
4473 appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4474
4475 if (polinfo->polwithcheck != NULL)
4476 appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4477
4478 appendPQExpBufferStr(query, ";\n");
4479
4480 appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4482
4483 appendPQExpBuffer(polprefix, "POLICY %s ON",
4484 fmtId(polinfo->polname));
4485
4486 tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4487
4488 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4489 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4490 ARCHIVE_OPTS(.tag = tag,
4491 .namespace = polinfo->dobj.namespace->dobj.name,
4492 .owner = tbinfo->rolname,
4493 .description = "POLICY",
4494 .section = SECTION_POST_DATA,
4495 .createStmt = query->data,
4496 .dropStmt = delqry->data));
4497
4498 if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4500 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4501 polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4502
4503 free(tag);
4504 destroyPQExpBuffer(query);
4507 free(qtabname);
4508}
4509
4510/*
4511 * getPublications
4512 * get information about publications
4513 */
4514void
4516{
4517 DumpOptions *dopt = fout->dopt;
4518 PQExpBuffer query;
4519 PGresult *res;
4521 int i_tableoid;
4522 int i_oid;
4523 int i_pubname;
4524 int i_pubowner;
4525 int i_puballtables;
4527 int i_pubinsert;
4528 int i_pubupdate;
4529 int i_pubdelete;
4530 int i_pubtruncate;
4531 int i_pubviaroot;
4532 int i_pubgencols;
4533 int i,
4534 ntups;
4535
4536 if (dopt->no_publications || fout->remoteVersion < 100000)
4537 return;
4538
4539 query = createPQExpBuffer();
4540
4541 /* Get the publications. */
4542 appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4543 "p.pubowner, p.puballtables, p.pubinsert, "
4544 "p.pubupdate, p.pubdelete, ");
4545
4546 if (fout->remoteVersion >= 110000)
4547 appendPQExpBufferStr(query, "p.pubtruncate, ");
4548 else
4549 appendPQExpBufferStr(query, "false AS pubtruncate, ");
4550
4551 if (fout->remoteVersion >= 130000)
4552 appendPQExpBufferStr(query, "p.pubviaroot, ");
4553 else
4554 appendPQExpBufferStr(query, "false AS pubviaroot, ");
4555
4556 if (fout->remoteVersion >= 180000)
4557 appendPQExpBufferStr(query, "p.pubgencols, ");
4558 else
4559 appendPQExpBuffer(query, "'%c' AS pubgencols, ", PUBLISH_GENCOLS_NONE);
4560
4561 if (fout->remoteVersion >= 190000)
4562 appendPQExpBufferStr(query, "p.puballsequences ");
4563 else
4564 appendPQExpBufferStr(query, "false AS puballsequences ");
4565
4566 appendPQExpBufferStr(query, "FROM pg_publication p");
4567
4568 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4569
4570 ntups = PQntuples(res);
4571
4572 if (ntups == 0)
4573 goto cleanup;
4574
4575 i_tableoid = PQfnumber(res, "tableoid");
4576 i_oid = PQfnumber(res, "oid");
4577 i_pubname = PQfnumber(res, "pubname");
4578 i_pubowner = PQfnumber(res, "pubowner");
4579 i_puballtables = PQfnumber(res, "puballtables");
4580 i_puballsequences = PQfnumber(res, "puballsequences");
4581 i_pubinsert = PQfnumber(res, "pubinsert");
4582 i_pubupdate = PQfnumber(res, "pubupdate");
4583 i_pubdelete = PQfnumber(res, "pubdelete");
4584 i_pubtruncate = PQfnumber(res, "pubtruncate");
4585 i_pubviaroot = PQfnumber(res, "pubviaroot");
4586 i_pubgencols = PQfnumber(res, "pubgencols");
4587
4589
4590 for (i = 0; i < ntups; i++)
4591 {
4592 pubinfo[i].dobj.objType = DO_PUBLICATION;
4593 pubinfo[i].dobj.catId.tableoid =
4594 atooid(PQgetvalue(res, i, i_tableoid));
4595 pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4596 AssignDumpId(&pubinfo[i].dobj);
4597 pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4598 pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4599 pubinfo[i].puballtables =
4600 (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4601 pubinfo[i].puballsequences =
4602 (strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
4603 pubinfo[i].pubinsert =
4604 (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4605 pubinfo[i].pubupdate =
4606 (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4607 pubinfo[i].pubdelete =
4608 (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4609 pubinfo[i].pubtruncate =
4610 (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4611 pubinfo[i].pubviaroot =
4612 (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4613 pubinfo[i].pubgencols_type =
4614 *(PQgetvalue(res, i, i_pubgencols));
4615 pubinfo[i].except_tables = (SimplePtrList)
4616 {
4617 NULL, NULL
4618 };
4619
4620 /* Decide whether we want to dump it */
4622
4623 /*
4624 * Get the list of tables for publications specified in the EXCEPT
4625 * TABLE clause.
4626 *
4627 * Although individual table entries in EXCEPT list could be stored in
4628 * PublicationRelInfo, dumpPublicationTable cannot be used to emit
4629 * them, because there is no ALTER PUBLICATION ... ADD command to add
4630 * individual table entries to the EXCEPT list.
4631 *
4632 * Therefore, the approach is to dump the complete EXCEPT list in a
4633 * single CREATE PUBLICATION statement. PublicationInfo is used to
4634 * collect this information, which is then emitted by
4635 * dumpPublication().
4636 */
4637 if (fout->remoteVersion >= 190000)
4638 {
4639 int ntbls;
4641
4642 resetPQExpBuffer(query);
4643 appendPQExpBuffer(query,
4644 "SELECT prrelid\n"
4645 "FROM pg_catalog.pg_publication_rel\n"
4646 "WHERE prpubid = %u AND prexcept",
4647 pubinfo[i].dobj.catId.oid);
4648
4650
4652
4653 for (int j = 0; j < ntbls; j++)
4654 {
4655 Oid prrelid;
4657
4659
4661
4662 if (tbinfo != NULL)
4663 simple_ptr_list_append(&pubinfo[i].except_tables, tbinfo);
4664 }
4665
4667 }
4668 }
4669
4670cleanup:
4671 PQclear(res);
4672
4673 destroyPQExpBuffer(query);
4674}
4675
4676/*
4677 * dumpPublication
4678 * dump the definition of the given publication
4679 */
4680static void
4682{
4683 DumpOptions *dopt = fout->dopt;
4685 PQExpBuffer query;
4686 char *qpubname;
4687 bool first = true;
4688
4689 /* Do nothing if not dumping schema */
4690 if (!dopt->dumpSchema)
4691 return;
4692
4694 query = createPQExpBuffer();
4695
4696 qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4697
4698 appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4699 qpubname);
4700
4701 appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4702 qpubname);
4703
4704 if (pubinfo->puballtables)
4705 {
4706 int n_except = 0;
4707
4708 appendPQExpBufferStr(query, " FOR ALL TABLES");
4709
4710 /* Include EXCEPT (TABLE) clause if there are except_tables. */
4711 for (SimplePtrListCell *cell = pubinfo->except_tables.head; cell; cell = cell->next)
4712 {
4713 TableInfo *tbinfo = (TableInfo *) cell->ptr;
4714
4715 if (++n_except == 1)
4716 appendPQExpBufferStr(query, " EXCEPT (");
4717 else
4718 appendPQExpBufferStr(query, ", ");
4719 appendPQExpBuffer(query, "TABLE ONLY %s", fmtQualifiedDumpable(tbinfo));
4720 }
4721 if (n_except > 0)
4722 appendPQExpBufferChar(query, ')');
4723
4724 if (pubinfo->puballsequences)
4725 appendPQExpBufferStr(query, ", ALL SEQUENCES");
4726 }
4727 else if (pubinfo->puballsequences)
4728 appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
4729
4730 appendPQExpBufferStr(query, " WITH (publish = '");
4731 if (pubinfo->pubinsert)
4732 {
4733 appendPQExpBufferStr(query, "insert");
4734 first = false;
4735 }
4736
4737 if (pubinfo->pubupdate)
4738 {
4739 if (!first)
4740 appendPQExpBufferStr(query, ", ");
4741
4742 appendPQExpBufferStr(query, "update");
4743 first = false;
4744 }
4745
4746 if (pubinfo->pubdelete)
4747 {
4748 if (!first)
4749 appendPQExpBufferStr(query, ", ");
4750
4751 appendPQExpBufferStr(query, "delete");
4752 first = false;
4753 }
4754
4755 if (pubinfo->pubtruncate)
4756 {
4757 if (!first)
4758 appendPQExpBufferStr(query, ", ");
4759
4760 appendPQExpBufferStr(query, "truncate");
4761 first = false;
4762 }
4763
4764 appendPQExpBufferChar(query, '\'');
4765
4766 if (pubinfo->pubviaroot)
4767 appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4768
4769 if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4770 appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4771
4772 appendPQExpBufferStr(query, ");\n");
4773
4774 if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4775 ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4776 ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4777 .owner = pubinfo->rolname,
4778 .description = "PUBLICATION",
4779 .section = SECTION_POST_DATA,
4780 .createStmt = query->data,
4781 .dropStmt = delq->data));
4782
4783 if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4784 dumpComment(fout, "PUBLICATION", qpubname,
4785 NULL, pubinfo->rolname,
4786 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4787
4788 if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4789 dumpSecLabel(fout, "PUBLICATION", qpubname,
4790 NULL, pubinfo->rolname,
4791 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4792
4794 destroyPQExpBuffer(query);
4795 free(qpubname);
4796}
4797
4798/*
4799 * getPublicationNamespaces
4800 * get information about publication membership for dumpable schemas.
4801 */
4802void
4804{
4805 PQExpBuffer query;
4806 PGresult *res;
4808 DumpOptions *dopt = fout->dopt;
4809 int i_tableoid;
4810 int i_oid;
4811 int i_pnpubid;
4812 int i_pnnspid;
4813 int i,
4814 j,
4815 ntups;
4816
4817 if (dopt->no_publications || fout->remoteVersion < 150000)
4818 return;
4819
4820 query = createPQExpBuffer();
4821
4822 /* Collect all publication membership info. */
4824 "SELECT tableoid, oid, pnpubid, pnnspid "
4825 "FROM pg_catalog.pg_publication_namespace");
4826 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4827
4828 ntups = PQntuples(res);
4829
4830 i_tableoid = PQfnumber(res, "tableoid");
4831 i_oid = PQfnumber(res, "oid");
4832 i_pnpubid = PQfnumber(res, "pnpubid");
4833 i_pnnspid = PQfnumber(res, "pnnspid");
4834
4835 /* this allocation may be more than we need */
4837 j = 0;
4838
4839 for (i = 0; i < ntups; i++)
4840 {
4845
4846 /*
4847 * Ignore any entries for which we aren't interested in either the
4848 * publication or the rel.
4849 */
4851 if (pubinfo == NULL)
4852 continue;
4854 if (nspinfo == NULL)
4855 continue;
4856
4857 /* OK, make a DumpableObject for this relationship */
4859 pubsinfo[j].dobj.catId.tableoid =
4860 atooid(PQgetvalue(res, i, i_tableoid));
4861 pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4862 AssignDumpId(&pubsinfo[j].dobj);
4863 pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4864 pubsinfo[j].dobj.name = nspinfo->dobj.name;
4865 pubsinfo[j].publication = pubinfo;
4866 pubsinfo[j].pubschema = nspinfo;
4867
4868 /* Decide whether we want to dump it */
4870
4871 j++;
4872 }
4873
4874 PQclear(res);
4875 destroyPQExpBuffer(query);
4876}
4877
4878/*
4879 * getPublicationTables
4880 * get information about publication membership for dumpable tables.
4881 */
4882void
4884{
4885 PQExpBuffer query;
4886 PGresult *res;
4888 DumpOptions *dopt = fout->dopt;
4889 int i_tableoid;
4890 int i_oid;
4891 int i_prpubid;
4892 int i_prrelid;
4893 int i_prrelqual;
4894 int i_prattrs;
4895 int i,
4896 j,
4897 ntups;
4898
4899 if (dopt->no_publications || fout->remoteVersion < 100000)
4900 return;
4901
4902 query = createPQExpBuffer();
4903
4904 /* Collect all publication membership info. */
4905 if (fout->remoteVersion >= 150000)
4906 {
4908 "SELECT tableoid, oid, prpubid, prrelid, "
4909 "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4910 "(CASE\n"
4911 " WHEN pr.prattrs IS NOT NULL THEN\n"
4912 " (SELECT array_agg(attname)\n"
4913 " FROM\n"
4914 " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4915 " pg_catalog.pg_attribute\n"
4916 " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4917 " ELSE NULL END) prattrs "
4918 "FROM pg_catalog.pg_publication_rel pr");
4919 if (fout->remoteVersion >= 190000)
4920 appendPQExpBufferStr(query, " WHERE NOT pr.prexcept");
4921 }
4922 else
4924 "SELECT tableoid, oid, prpubid, prrelid, "
4925 "NULL AS prrelqual, NULL AS prattrs "
4926 "FROM pg_catalog.pg_publication_rel");
4927 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4928
4929 ntups = PQntuples(res);
4930
4931 i_tableoid = PQfnumber(res, "tableoid");
4932 i_oid = PQfnumber(res, "oid");
4933 i_prpubid = PQfnumber(res, "prpubid");
4934 i_prrelid = PQfnumber(res, "prrelid");
4935 i_prrelqual = PQfnumber(res, "prrelqual");
4936 i_prattrs = PQfnumber(res, "prattrs");
4937
4938 /* this allocation may be more than we need */
4940 j = 0;
4941
4942 for (i = 0; i < ntups; i++)
4943 {
4948
4949 /*
4950 * Ignore any entries for which we aren't interested in either the
4951 * publication or the rel.
4952 */
4954 if (pubinfo == NULL)
4955 continue;
4957 if (tbinfo == NULL)
4958 continue;
4959
4960 /* OK, make a DumpableObject for this relationship */
4961 pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4962 pubrinfo[j].dobj.catId.tableoid =
4963 atooid(PQgetvalue(res, i, i_tableoid));
4964 pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4965 AssignDumpId(&pubrinfo[j].dobj);
4966 pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4967 pubrinfo[j].dobj.name = tbinfo->dobj.name;
4968 pubrinfo[j].publication = pubinfo;
4969 pubrinfo[j].pubtable = tbinfo;
4970 if (PQgetisnull(res, i, i_prrelqual))
4971 pubrinfo[j].pubrelqual = NULL;
4972 else
4973 pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4974
4975 if (!PQgetisnull(res, i, i_prattrs))
4976 {
4977 char **attnames;
4978 int nattnames;
4980
4981 if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4982 &attnames, &nattnames))
4983 pg_fatal("could not parse %s array", "prattrs");
4985 for (int k = 0; k < nattnames; k++)
4986 {
4987 if (k > 0)
4989
4990 appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4991 }
4992 pubrinfo[j].pubrattrs = attribs->data;
4993 free(attribs); /* but not attribs->data */
4994 free(attnames);
4995 }
4996 else
4997 pubrinfo[j].pubrattrs = NULL;
4998
4999 /* Decide whether we want to dump it */
5001
5002 j++;
5003 }
5004
5005 PQclear(res);
5006 destroyPQExpBuffer(query);
5007}
5008
5009/*
5010 * dumpPublicationNamespace
5011 * dump the definition of the given publication schema mapping.
5012 */
5013static void
5015{
5016 DumpOptions *dopt = fout->dopt;
5017 NamespaceInfo *schemainfo = pubsinfo->pubschema;
5018 PublicationInfo *pubinfo = pubsinfo->publication;
5019 PQExpBuffer query;
5020 char *tag;
5021
5022 /* Do nothing if not dumping schema */
5023 if (!dopt->dumpSchema)
5024 return;
5025
5026 tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
5027
5028 query = createPQExpBuffer();
5029
5030 appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
5031 appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
5032
5033 /*
5034 * There is no point in creating drop query as the drop is done by schema
5035 * drop.
5036 */
5037 if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5038 ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
5039 ARCHIVE_OPTS(.tag = tag,
5040 .namespace = schemainfo->dobj.name,
5041 .owner = pubinfo->rolname,
5042 .description = "PUBLICATION TABLES IN SCHEMA",
5043 .section = SECTION_POST_DATA,
5044 .createStmt = query->data));
5045
5046 /* These objects can't currently have comments or seclabels */
5047
5048 free(tag);
5049 destroyPQExpBuffer(query);
5050}
5051
5052/*
5053 * dumpPublicationTable
5054 * dump the definition of the given publication table mapping
5055 */
5056static void
5058{
5059 DumpOptions *dopt = fout->dopt;
5060 PublicationInfo *pubinfo = pubrinfo->publication;
5061 TableInfo *tbinfo = pubrinfo->pubtable;
5062 PQExpBuffer query;
5063 char *tag;
5064
5065 /* Do nothing if not dumping schema */
5066 if (!dopt->dumpSchema)
5067 return;
5068
5069 tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
5070
5071 query = createPQExpBuffer();
5072
5073 appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
5074 fmtId(pubinfo->dobj.name));
5075 appendPQExpBuffer(query, " %s",
5077
5078 if (pubrinfo->pubrattrs)
5079 appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5080
5081 if (pubrinfo->pubrelqual)
5082 {
5083 /*
5084 * It's necessary to add parentheses around the expression because
5085 * pg_get_expr won't supply the parentheses for things like WHERE
5086 * TRUE.
5087 */
5088 appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5089 }
5090 appendPQExpBufferStr(query, ";\n");
5091
5092 /*
5093 * There is no point in creating a drop query as the drop is done by table
5094 * drop. (If you think to change this, see also _printTocEntry().)
5095 * Although this object doesn't really have ownership as such, set the
5096 * owner field anyway to ensure that the command is run by the correct
5097 * role at restore time.
5098 */
5099 if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5100 ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5101 ARCHIVE_OPTS(.tag = tag,
5102 .namespace = tbinfo->dobj.namespace->dobj.name,
5103 .owner = pubinfo->rolname,
5104 .description = "PUBLICATION TABLE",
5105 .section = SECTION_POST_DATA,
5106 .createStmt = query->data));
5107
5108 /* These objects can't currently have comments or seclabels */
5109
5110 free(tag);
5111 destroyPQExpBuffer(query);
5112}
5113
5114/*
5115 * Is the currently connected user a superuser?
5116 */
5117static bool
5119{
5121 const char *val;
5122
5123 val = PQparameterStatus(AH->connection, "is_superuser");
5124
5125 if (val && strcmp(val, "on") == 0)
5126 return true;
5127
5128 return false;
5129}
5130
5131/*
5132 * Set the given value to restrict_nonsystem_relation_kind value. Since
5133 * restrict_nonsystem_relation_kind is introduced in minor version releases,
5134 * the setting query is effective only where available.
5135 */
5136static void
5138{
5140 PGresult *res;
5141
5142 appendPQExpBuffer(query,
5143 "SELECT set_config(name, '%s', false) "
5144 "FROM pg_settings "
5145 "WHERE name = 'restrict_nonsystem_relation_kind'",
5146 value);
5147 res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5148
5149 PQclear(res);
5150 destroyPQExpBuffer(query);
5151}
5152
5153/*
5154 * getSubscriptions
5155 * get information about subscriptions
5156 */
5157void
5159{
5160 DumpOptions *dopt = fout->dopt;
5161 PQExpBuffer query;
5162 PGresult *res;
5163 SubscriptionInfo *subinfo;
5164 int i_tableoid;
5165 int i_oid;
5166 int i_subname;
5167 int i_subowner;
5168 int i_subbinary;
5169 int i_substream;
5173 int i_subrunasowner;
5174 int i_subservername;
5175 int i_subconninfo;
5176 int i_subslotname;
5177 int i_subsynccommit;
5180 int i_suborigin;
5182 int i_subenabled;
5183 int i_subfailover;
5186 int i,
5187 ntups;
5188
5189 if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5190 return;
5191
5192 if (!is_superuser(fout))
5193 {
5194 int n;
5195
5196 res = ExecuteSqlQuery(fout,
5197 "SELECT count(*) FROM pg_subscription "
5198 "WHERE subdbid = (SELECT oid FROM pg_database"
5199 " WHERE datname = current_database())",
5201 n = atoi(PQgetvalue(res, 0, 0));
5202 if (n > 0)
5203 pg_log_warning("subscriptions not dumped because current user is not a superuser");
5204 PQclear(res);
5205 return;
5206 }
5207
5208 query = createPQExpBuffer();
5209
5210 /* Get the subscriptions in current database. */
5212 "SELECT s.tableoid, s.oid, s.subname,\n"
5213 " s.subowner,\n"
5214 " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5215 " s.subpublications,\n");
5216
5217 if (fout->remoteVersion >= 140000)
5218 appendPQExpBufferStr(query, " s.subbinary,\n");
5219 else
5220 appendPQExpBufferStr(query, " false AS subbinary,\n");
5221
5222 if (fout->remoteVersion >= 140000)
5223 appendPQExpBufferStr(query, " s.substream,\n");
5224 else
5225 appendPQExpBufferStr(query, " 'f' AS substream,\n");
5226
5227 if (fout->remoteVersion >= 150000)
5229 " s.subtwophasestate,\n"
5230 " s.subdisableonerr,\n");
5231 else
5232 appendPQExpBuffer(query,
5233 " '%c' AS subtwophasestate,\n"
5234 " false AS subdisableonerr,\n",
5236
5237 if (fout->remoteVersion >= 160000)
5239 " s.subpasswordrequired,\n"
5240 " s.subrunasowner,\n"
5241 " s.suborigin,\n");
5242 else
5243 appendPQExpBuffer(query,
5244 " 't' AS subpasswordrequired,\n"
5245 " 't' AS subrunasowner,\n"
5246 " '%s' AS suborigin,\n",
5248
5249 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5250 appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5251 " s.subenabled,\n");
5252 else
5253 appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5254 " false AS subenabled,\n");
5255
5256 if (fout->remoteVersion >= 170000)
5258 " s.subfailover,\n");
5259 else
5261 " false AS subfailover,\n");
5262
5263 if (fout->remoteVersion >= 190000)
5265 " s.subretaindeadtuples,\n");
5266 else
5268 " false AS subretaindeadtuples,\n");
5269
5270 if (fout->remoteVersion >= 190000)
5272 " s.submaxretention,\n");
5273 else
5274 appendPQExpBufferStr(query, " 0 AS submaxretention,\n");
5275
5276 if (fout->remoteVersion >= 190000)
5278 " s.subwalrcvtimeout,\n");
5279 else
5281 " '-1' AS subwalrcvtimeout,\n");
5282
5283 if (fout->remoteVersion >= 190000)
5284 appendPQExpBufferStr(query, " fs.srvname AS subservername\n");
5285 else
5286 appendPQExpBufferStr(query, " NULL AS subservername\n");
5287
5289 "FROM pg_subscription s\n");
5290
5291 if (fout->remoteVersion >= 190000)
5293 "LEFT JOIN pg_catalog.pg_foreign_server fs \n"
5294 " ON fs.oid = s.subserver \n");
5295
5296 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5298 "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5299 " ON o.external_id = 'pg_' || s.oid::text \n");
5300
5302 "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5303 " WHERE datname = current_database())");
5304
5305 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5306
5307 ntups = PQntuples(res);
5308
5309 /*
5310 * Get subscription fields. We don't include subskiplsn in the dump as
5311 * after restoring the dump this value may no longer be relevant.
5312 */
5313 i_tableoid = PQfnumber(res, "tableoid");
5314 i_oid = PQfnumber(res, "oid");
5315 i_subname = PQfnumber(res, "subname");
5316 i_subowner = PQfnumber(res, "subowner");
5317 i_subenabled = PQfnumber(res, "subenabled");
5318 i_subbinary = PQfnumber(res, "subbinary");
5319 i_substream = PQfnumber(res, "substream");
5320 i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5321 i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5322 i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5323 i_subrunasowner = PQfnumber(res, "subrunasowner");
5324 i_subfailover = PQfnumber(res, "subfailover");
5325 i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5326 i_submaxretention = PQfnumber(res, "submaxretention");
5327 i_subservername = PQfnumber(res, "subservername");
5328 i_subconninfo = PQfnumber(res, "subconninfo");
5329 i_subslotname = PQfnumber(res, "subslotname");
5330 i_subsynccommit = PQfnumber(res, "subsynccommit");
5331 i_subwalrcvtimeout = PQfnumber(res, "subwalrcvtimeout");
5332 i_subpublications = PQfnumber(res, "subpublications");
5333 i_suborigin = PQfnumber(res, "suborigin");
5334 i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5335
5336 subinfo = pg_malloc_array(SubscriptionInfo, ntups);
5337
5338 for (i = 0; i < ntups; i++)
5339 {
5340 subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5341 subinfo[i].dobj.catId.tableoid =
5342 atooid(PQgetvalue(res, i, i_tableoid));
5343 subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5344 AssignDumpId(&subinfo[i].dobj);
5345 subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5346 subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5347
5348 subinfo[i].subenabled =
5349 (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5350 if (PQgetisnull(res, i, i_subservername))
5351 subinfo[i].subservername = NULL;
5352 else
5354 subinfo[i].subbinary =
5355 (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5356 subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5357 subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5358 subinfo[i].subdisableonerr =
5359 (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5360 subinfo[i].subpasswordrequired =
5361 (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5362 subinfo[i].subrunasowner =
5363 (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5364 subinfo[i].subfailover =
5365 (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5366 subinfo[i].subretaindeadtuples =
5367 (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5368 subinfo[i].submaxretention =
5370 if (PQgetisnull(res, i, i_subconninfo))
5371 subinfo[i].subconninfo = NULL;
5372 else
5373 subinfo[i].subconninfo =
5375 if (PQgetisnull(res, i, i_subslotname))
5376 subinfo[i].subslotname = NULL;
5377 else
5378 subinfo[i].subslotname =
5380 subinfo[i].subsynccommit =
5382 subinfo[i].subwalrcvtimeout =
5384 subinfo[i].subpublications =
5386 subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5388 subinfo[i].suboriginremotelsn = NULL;
5389 else
5390 subinfo[i].suboriginremotelsn =
5392
5393 /* Decide whether we want to dump it */
5394 selectDumpableObject(&(subinfo[i].dobj), fout);
5395 }
5396 PQclear(res);
5397
5398 destroyPQExpBuffer(query);
5399}
5400
5401/*
5402 * getSubscriptionRelations
5403 * Get information about subscription membership for dumpable relations. This
5404 * will be used only in binary-upgrade mode for PG17 or later versions.
5405 */
5406void
5408{
5409 DumpOptions *dopt = fout->dopt;
5410 SubscriptionInfo *subinfo = NULL;
5412 PGresult *res;
5413 int i_srsubid;
5414 int i_srrelid;
5415 int i_srsubstate;
5416 int i_srsublsn;
5417 int ntups;
5419
5420 if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5421 fout->remoteVersion < 170000)
5422 return;
5423
5424 res = ExecuteSqlQuery(fout,
5425 "SELECT srsubid, srrelid, srsubstate, srsublsn "
5426 "FROM pg_catalog.pg_subscription_rel "
5427 "ORDER BY srsubid",
5429 ntups = PQntuples(res);
5430 if (ntups == 0)
5431 goto cleanup;
5432
5433 /* Get pg_subscription_rel attributes */
5434 i_srsubid = PQfnumber(res, "srsubid");
5435 i_srrelid = PQfnumber(res, "srrelid");
5436 i_srsubstate = PQfnumber(res, "srsubstate");
5437 i_srsublsn = PQfnumber(res, "srsublsn");
5438
5440 for (int i = 0; i < ntups; i++)
5441 {
5443 Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5444 TableInfo *tblinfo;
5445
5446 /*
5447 * If we switched to a new subscription, check if the subscription
5448 * exists.
5449 */
5451 {
5453 if (subinfo == NULL)
5454 pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5455
5457 }
5458
5459 tblinfo = findTableByOid(relid);
5460 if (tblinfo == NULL)
5461 pg_fatal("failed sanity check, relation with OID %u not found",
5462 relid);
5463
5464 /* OK, make a DumpableObject for this relationship */
5465 subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5466 subrinfo[i].dobj.catId.tableoid = relid;
5467 subrinfo[i].dobj.catId.oid = cur_srsubid;
5468 AssignDumpId(&subrinfo[i].dobj);
5469 subrinfo[i].dobj.namespace = tblinfo->dobj.namespace;
5470 subrinfo[i].dobj.name = tblinfo->dobj.name;
5471 subrinfo[i].subinfo = subinfo;
5472 subrinfo[i].tblinfo = tblinfo;
5473 subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5474 if (PQgetisnull(res, i, i_srsublsn))
5475 subrinfo[i].srsublsn = NULL;
5476 else
5477 subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5478
5479 /* Decide whether we want to dump it */
5481 }
5482
5483cleanup:
5484 PQclear(res);
5485}
5486
5487/*
5488 * dumpSubscriptionTable
5489 * Dump the definition of the given subscription table mapping. This will be
5490 * used only in binary-upgrade mode for PG17 or later versions.
5491 */
5492static void
5494{
5495 DumpOptions *dopt = fout->dopt;
5496 SubscriptionInfo *subinfo = subrinfo->subinfo;
5497 PQExpBuffer query;
5498 char *tag;
5499
5500 /* Do nothing if not dumping schema */
5501 if (!dopt->dumpSchema)
5502 return;
5503
5505
5506 tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->tblinfo->dobj.name);
5507
5508 query = createPQExpBuffer();
5509
5510 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5511 {
5512 /*
5513 * binary_upgrade_add_sub_rel_state will add the subscription relation
5514 * to pg_subscription_rel table. This will be used only in
5515 * binary-upgrade mode.
5516 */
5518 "\n-- For binary upgrade, must preserve the subscriber table.\n");
5520 "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5521 appendStringLiteralAH(query, subinfo->dobj.name, fout);
5522 appendPQExpBuffer(query,
5523 ", %u, '%c'",
5524 subrinfo->tblinfo->dobj.catId.oid,
5525 subrinfo->srsubstate);
5526
5527 if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5528 appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5529 else
5530 appendPQExpBufferStr(query, ", NULL");
5531
5532 appendPQExpBufferStr(query, ");\n");
5533 }
5534
5535 /*
5536 * There is no point in creating a drop query as the drop is done by table
5537 * drop. (If you think to change this, see also _printTocEntry().)
5538 * Although this object doesn't really have ownership as such, set the
5539 * owner field anyway to ensure that the command is run by the correct
5540 * role at restore time.
5541 */
5542 if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5543 ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5544 ARCHIVE_OPTS(.tag = tag,
5545 .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5546 .owner = subinfo->rolname,
5547 .description = "SUBSCRIPTION TABLE",
5548 .section = SECTION_POST_DATA,
5549 .createStmt = query->data));
5550
5551 /* These objects can't currently have comments or seclabels */
5552
5553 free(tag);
5554 destroyPQExpBuffer(query);
5555}
5556
5557/*
5558 * dumpSubscription
5559 * dump the definition of the given subscription
5560 */
5561static void
5563{
5564 DumpOptions *dopt = fout->dopt;
5566 PQExpBuffer query;
5567 PQExpBuffer publications;
5568 char *qsubname;
5569 char **pubnames = NULL;
5570 int npubnames = 0;
5571 int i;
5572
5573 /* Do nothing if not dumping schema */
5574 if (!dopt->dumpSchema)
5575 return;
5576
5578 query = createPQExpBuffer();
5579
5580 qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5581
5582 appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5583 qsubname);
5584
5585 appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s ",
5586 qsubname);
5587 if (subinfo->subservername)
5588 {
5589 appendPQExpBuffer(query, "SERVER %s", fmtId(subinfo->subservername));
5590 }
5591 else
5592 {
5593 appendPQExpBufferStr(query, "CONNECTION ");
5594 appendStringLiteralAH(query, subinfo->subconninfo, fout);
5595 }
5596
5597 /* Build list of quoted publications and append them to query. */
5599 pg_fatal("could not parse %s array", "subpublications");
5600
5601 publications = createPQExpBuffer();
5602 for (i = 0; i < npubnames; i++)
5603 {
5604 if (i > 0)
5605 appendPQExpBufferStr(publications, ", ");
5606
5607 appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5608 }
5609
5610 appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5611 if (subinfo->subslotname)
5612 appendStringLiteralAH(query, subinfo->subslotname, fout);
5613 else
5614 appendPQExpBufferStr(query, "NONE");
5615
5616 if (subinfo->subbinary)
5617 appendPQExpBufferStr(query, ", binary = true");
5618
5619 if (subinfo->substream == LOGICALREP_STREAM_ON)
5620 appendPQExpBufferStr(query, ", streaming = on");
5621 else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5622 appendPQExpBufferStr(query, ", streaming = parallel");
5623 else
5624 appendPQExpBufferStr(query, ", streaming = off");
5625
5627 appendPQExpBufferStr(query, ", two_phase = on");
5628
5629 if (subinfo->subdisableonerr)
5630 appendPQExpBufferStr(query, ", disable_on_error = true");
5631
5632 if (!subinfo->subpasswordrequired)
5633 appendPQExpBufferStr(query, ", password_required = false");
5634
5635 if (subinfo->subrunasowner)
5636 appendPQExpBufferStr(query, ", run_as_owner = true");
5637
5638 if (subinfo->subfailover)
5639 appendPQExpBufferStr(query, ", failover = true");
5640
5641 if (subinfo->subretaindeadtuples)
5642 appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5643
5644 if (subinfo->submaxretention)
5645 appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5646
5647 if (strcmp(subinfo->subsynccommit, "off") != 0)
5648 appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5649
5650 if (strcmp(subinfo->subwalrcvtimeout, "-1") != 0)
5651 appendPQExpBuffer(query, ", wal_receiver_timeout = %s", fmtId(subinfo->subwalrcvtimeout));
5652
5653 if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5654 appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5655
5656 appendPQExpBufferStr(query, ");\n");
5657
5658 /*
5659 * In binary-upgrade mode, we allow the replication to continue after the
5660 * upgrade.
5661 */
5662 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5663 {
5664 if (subinfo->suboriginremotelsn)
5665 {
5666 /*
5667 * Preserve the remote_lsn for the subscriber's replication
5668 * origin. This value is required to start the replication from
5669 * the position before the upgrade. This value will be stale if
5670 * the publisher gets upgraded before the subscriber node.
5671 * However, this shouldn't be a problem as the upgrade of the
5672 * publisher ensures that all the transactions were replicated
5673 * before upgrading it.
5674 */
5676 "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5678 "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5679 appendStringLiteralAH(query, subinfo->dobj.name, fout);
5680 appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5681 }
5682
5683 if (subinfo->subenabled)
5684 {
5685 /*
5686 * Enable the subscription to allow the replication to continue
5687 * after the upgrade.
5688 */
5690 "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5691 appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5692 }
5693 }
5694
5695 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5696 ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5697 ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5698 .owner = subinfo->rolname,
5699 .description = "SUBSCRIPTION",
5700 .section = SECTION_POST_DATA,
5701 .createStmt = query->data,
5702 .dropStmt = delq->data));
5703
5704 if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5705 dumpComment(fout, "SUBSCRIPTION", qsubname,
5706 NULL, subinfo->rolname,
5707 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5708
5709 if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5710 dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5711 NULL, subinfo->rolname,
5712 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5713
5714 destroyPQExpBuffer(publications);
5715 free(pubnames);
5716
5718 destroyPQExpBuffer(query);
5719 free(qsubname);
5720}
5721
5722/*
5723 * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5724 * the object needs.
5725 */
5726static void
5728 PQExpBuffer create,
5729 const DumpableObject *dobj,
5730 const char *catalog,
5731 const char *keyword,
5732 const char *objname)
5733{
5734 if (dobj->depends_on_ext)
5735 {
5736 char *nm;
5737 PGresult *res;
5738 PQExpBuffer query;
5739 int ntups;
5740 int i_extname;
5741 int i;
5742
5743 /* dodge fmtId() non-reentrancy */
5744 nm = pg_strdup(objname);
5745
5746 query = createPQExpBuffer();
5747 appendPQExpBuffer(query,
5748 "SELECT e.extname "
5749 "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5750 "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5751 "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5752 "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5753 catalog,
5754 dobj->catId.oid);
5755 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5756 ntups = PQntuples(res);
5757 i_extname = PQfnumber(res, "extname");
5758 for (i = 0; i < ntups; i++)
5759 {
5760 appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5761 keyword, nm,
5762 fmtId(PQgetvalue(res, i, i_extname)));
5763 }
5764
5765 PQclear(res);
5766 destroyPQExpBuffer(query);
5767 pg_free(nm);
5768 }
5769}
5770
5771static Oid
5773{
5774 /*
5775 * If the old version didn't assign an array type, but the new version
5776 * does, we must select an unused type OID to assign. This currently only
5777 * happens for domains, when upgrading pre-v11 to v11 and up.
5778 *
5779 * Note: local state here is kind of ugly, but we must have some, since we
5780 * mustn't choose the same unused OID more than once.
5781 */
5783 PGresult *res;
5784 bool is_dup;
5785
5786 do
5787 {
5790 "SELECT EXISTS(SELECT 1 "
5791 "FROM pg_catalog.pg_type "
5792 "WHERE oid = '%u'::pg_catalog.oid);",
5795 is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5796 PQclear(res);
5797 } while (is_dup);
5798
5800}
5801
5802static void
5806 bool force_array_type,
5808{
5810 PGresult *res;
5814 TypeInfo *tinfo;
5815
5816 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5818 "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5819 pg_type_oid);
5820
5822 if (tinfo)
5823 pg_type_array_oid = tinfo->typarray;
5824 else
5826
5829
5831 {
5833 "\n-- For binary upgrade, must preserve pg_type array oid\n");
5835 "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5837 }
5838
5839 /*
5840 * Pre-set the multirange type oid and its own array type oid.
5841 */
5843 {
5844 if (fout->remoteVersion >= 140000)
5845 {
5847 "SELECT t.oid, t.typarray "
5848 "FROM pg_catalog.pg_type t "
5849 "JOIN pg_catalog.pg_range r "
5850 "ON t.oid = r.rngmultitypid "
5851 "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5852 pg_type_oid);
5853
5855
5856 pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5857 pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5858
5859 PQclear(res);
5860 }
5861 else
5862 {
5865 }
5866
5868 "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5870 "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5873 "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5875 "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5877 }
5878
5880}
5881
5882static void
5885 const TableInfo *tbinfo)
5886{
5887 Oid pg_type_oid = tbinfo->reltype;
5888
5891 pg_type_oid, false, false);
5892}
5893
5894/*
5895 * bsearch() comparator for BinaryUpgradeClassOidItem
5896 */
5897static int
5898BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5899{
5902
5903 return pg_cmp_u32(v1.oid, v2.oid);
5904}
5905
5906/*
5907 * collectBinaryUpgradeClassOids
5908 *
5909 * Construct a table of pg_class information required for
5910 * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5911 * lookup.
5912 */
5913static void
5915{
5916 PGresult *res;
5917 const char *query;
5918
5919 query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5920 "ct.relfilenode, i.indexrelid, cti.relfilenode "
5921 "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5922 "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5923 "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5924 "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5925 "ORDER BY c.oid;";
5926
5927 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5928
5932
5933 for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5934 {
5942 }
5943
5944 PQclear(res);
5945}
5946
5947static void
5950{
5953
5955
5956 /*
5957 * Preserve the OID and relfilenumber of the table, table's index, table's
5958 * toast table and toast table's index if any.
5959 *
5960 * One complexity is that the current table definition might not require
5961 * the creation of a TOAST table, but the old database might have a TOAST
5962 * table that was created earlier, before some wide columns were dropped.
5963 * By setting the TOAST oid we force creation of the TOAST heap and index
5964 * by the new backend, so we can copy the files during binary upgrade
5965 * without worrying about this case.
5966 */
5967 key.oid = pg_class_oid;
5971
5973 "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5974
5975 if (entry->relkind != RELKIND_INDEX &&
5977 {
5979 "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5980 pg_class_oid);
5981
5982 /*
5983 * Not every relation has storage. Also, in a pre-v12 database,
5984 * partitioned tables have a relfilenumber, which should not be
5985 * preserved when upgrading.
5986 */
5987 if (RelFileNumberIsValid(entry->relfilenumber) &&
5990 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5991 entry->relfilenumber);
5992
5993 /*
5994 * In a pre-v12 database, partitioned tables might be marked as having
5995 * toast tables, but we should ignore them if so.
5996 */
5997 if (OidIsValid(entry->toast_oid) &&
5999 {
6001 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
6002 entry->toast_oid);
6004 "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
6005 entry->toast_relfilenumber);
6006
6007 /* every toast table has an index */
6009 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
6010 entry->toast_index_oid);
6012 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
6014 }
6015 }
6016 else
6017 {
6018 /* Preserve the OID and relfilenumber of the index */
6020 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
6021 pg_class_oid);
6023 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
6024 entry->relfilenumber);
6025 }
6026
6028}
6029
6030/*
6031 * If the DumpableObject is a member of an extension, add a suitable
6032 * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
6033 *
6034 * For somewhat historical reasons, objname should already be quoted,
6035 * but not objnamespace (if any).
6036 */
6037static void
6039 const DumpableObject *dobj,
6040 const char *objtype,
6041 const char *objname,
6042 const char *objnamespace)
6043{
6045 int i;
6046
6047 if (!dobj->ext_member)
6048 return;
6049
6050 /*
6051 * Find the parent extension. We could avoid this search if we wanted to
6052 * add a link field to DumpableObject, but the space costs of that would
6053 * be considerable. We assume that member objects could only have a
6054 * direct dependency on their own extension, not any others.
6055 */
6056 for (i = 0; i < dobj->nDeps; i++)
6057 {
6059 if (extobj && extobj->objType == DO_EXTENSION)
6060 break;
6061 extobj = NULL;
6062 }
6063 if (extobj == NULL)
6064 pg_fatal("could not find parent extension for %s %s",
6065 objtype, objname);
6066
6068 "\n-- For binary upgrade, handle extension membership the hard way\n");
6069 appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
6070 fmtId(extobj->name),
6071 objtype);
6072 if (objnamespace && *objnamespace)
6074 appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
6075}
6076
6077/*
6078 * getNamespaces:
6079 * get information about all namespaces in the system catalogs
6080 */
6081void
6083{
6084 PGresult *res;
6085 int ntups;
6086 int i;
6087 PQExpBuffer query;
6089 int i_tableoid;
6090 int i_oid;
6091 int i_nspname;
6092 int i_nspowner;
6093 int i_nspacl;
6094 int i_acldefault;
6095
6096 query = createPQExpBuffer();
6097
6098 /*
6099 * we fetch all namespaces including system ones, so that every object we
6100 * read in can be linked to a containing namespace.
6101 */
6102 appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
6103 "n.nspowner, "
6104 "n.nspacl, "
6105 "acldefault('n', n.nspowner) AS acldefault "
6106 "FROM pg_namespace n");
6107
6108 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6109
6110 ntups = PQntuples(res);
6111
6113
6114 i_tableoid = PQfnumber(res, "tableoid");
6115 i_oid = PQfnumber(res, "oid");
6116 i_nspname = PQfnumber(res, "nspname");
6117 i_nspowner = PQfnumber(res, "nspowner");
6118 i_nspacl = PQfnumber(res, "nspacl");
6119 i_acldefault = PQfnumber(res, "acldefault");
6120
6121 for (i = 0; i < ntups; i++)
6122 {
6123 const char *nspowner;
6124
6125 nsinfo[i].dobj.objType = DO_NAMESPACE;
6126 nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6127 nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6128 AssignDumpId(&nsinfo[i].dobj);
6129 nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6130 nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6131 nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6132 nsinfo[i].dacl.privtype = 0;
6133 nsinfo[i].dacl.initprivs = NULL;
6134 nspowner = PQgetvalue(res, i, i_nspowner);
6135 nsinfo[i].nspowner = atooid(nspowner);
6136 nsinfo[i].rolname = getRoleName(nspowner);
6137
6138 /* Decide whether to dump this namespace */
6140
6141 /* Mark whether namespace has an ACL */
6142 if (!PQgetisnull(res, i, i_nspacl))
6143 nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6144
6145 /*
6146 * We ignore any pg_init_privs.initprivs entry for the public schema
6147 * and assume a predetermined default, for several reasons. First,
6148 * dropping and recreating the schema removes its pg_init_privs entry,
6149 * but an empty destination database starts with this ACL nonetheless.
6150 * Second, we support dump/reload of public schema ownership changes.
6151 * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6152 * initprivs continues to reflect the initial owner. Hence,
6153 * synthesize the value that nspacl will have after the restore's
6154 * ALTER SCHEMA OWNER. Third, this makes the destination database
6155 * match the source's ACL, even if the latter was an initdb-default
6156 * ACL, which changed in v15. An upgrade pulls in changes to most
6157 * system object ACLs that the DBA had not customized. We've made the
6158 * public schema depart from that, because changing its ACL so easily
6159 * breaks applications.
6160 */
6161 if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6162 {
6165
6166 /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6177
6178 nsinfo[i].dacl.privtype = 'i';
6179 nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6180 nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6181
6184 }
6185 }
6186
6187 PQclear(res);
6188 destroyPQExpBuffer(query);
6189}
6190
6191/*
6192 * findNamespace:
6193 * given a namespace OID, look up the info read by getNamespaces
6194 */
6195static NamespaceInfo *
6197{
6199
6201 if (nsinfo == NULL)
6202 pg_fatal("schema with OID %u does not exist", nsoid);
6203 return nsinfo;
6204}
6205
6206/*
6207 * getExtensions:
6208 * read all extensions in the system catalogs and return them in the
6209 * ExtensionInfo* structure
6210 *
6211 * numExtensions is set to the number of extensions read in
6212 */
6215{
6216 DumpOptions *dopt = fout->dopt;
6217 PGresult *res;
6218 int ntups;
6219 int i;
6220 PQExpBuffer query;
6222 int i_tableoid;
6223 int i_oid;
6224 int i_extname;
6225 int i_nspname;
6226 int i_extrelocatable;
6227 int i_extversion;
6228 int i_extconfig;
6229 int i_extcondition;
6230
6231 query = createPQExpBuffer();
6232
6233 appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6234 "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6235 "FROM pg_extension x "
6236 "JOIN pg_namespace n ON n.oid = x.extnamespace");
6237
6238 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6239
6240 ntups = PQntuples(res);
6241 if (ntups == 0)
6242 goto cleanup;
6243
6245
6246 i_tableoid = PQfnumber(res, "tableoid");
6247 i_oid = PQfnumber(res, "oid");
6248 i_extname = PQfnumber(res, "extname");
6249 i_nspname = PQfnumber(res, "nspname");
6250 i_extrelocatable = PQfnumber(res, "extrelocatable");
6251 i_extversion = PQfnumber(res, "extversion");
6252 i_extconfig = PQfnumber(res, "extconfig");
6253 i_extcondition = PQfnumber(res, "extcondition");
6254
6255 for (i = 0; i < ntups; i++)
6256 {
6257 extinfo[i].dobj.objType = DO_EXTENSION;
6258 extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6259 extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6260 AssignDumpId(&extinfo[i].dobj);
6261 extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6262 extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6263 extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6264 extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6265 extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6266 extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6267
6268 /* Decide whether we want to dump it */
6270 }
6271
6272cleanup:
6273 PQclear(res);
6274 destroyPQExpBuffer(query);
6275
6276 *numExtensions = ntups;
6277
6278 return extinfo;
6279}
6280
6281/*
6282 * getTypes:
6283 * get information about all types in the system catalogs
6284 *
6285 * NB: this must run after getFuncs() because we assume we can do
6286 * findFuncByOid().
6287 */
6288void
6290{
6291 PGresult *res;
6292 int ntups;
6293 int i;
6297 int i_tableoid;
6298 int i_oid;
6299 int i_typname;
6300 int i_typnamespace;
6301 int i_typacl;
6302 int i_acldefault;
6303 int i_typowner;
6304 int i_typelem;
6305 int i_typrelid;
6306 int i_typrelkind;
6307 int i_typtype;
6308 int i_typisdefined;
6309 int i_isarray;
6310 int i_typarray;
6311
6312 /*
6313 * we include even the built-in types because those may be used as array
6314 * elements by user-defined types
6315 *
6316 * we filter out the built-in types when we dump out the types
6317 *
6318 * same approach for undefined (shell) types and array types
6319 *
6320 * Note: as of 8.3 we can reliably detect whether a type is an
6321 * auto-generated array type by checking the element type's typarray.
6322 * (Before that the test is capable of generating false positives.) We
6323 * still check for name beginning with '_', though, so as to avoid the
6324 * cost of the subselect probe for all standard types. This would have to
6325 * be revisited if the backend ever allows renaming of array types.
6326 */
6327 appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6328 "typnamespace, typacl, "
6329 "acldefault('T', typowner) AS acldefault, "
6330 "typowner, "
6331 "typelem, typrelid, typarray, "
6332 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6333 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6334 "typtype, typisdefined, "
6335 "typname[0] = '_' AND typelem != 0 AND "
6336 "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6337 "FROM pg_type");
6338
6339 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6340
6341 ntups = PQntuples(res);
6342
6344
6345 i_tableoid = PQfnumber(res, "tableoid");
6346 i_oid = PQfnumber(res, "oid");
6347 i_typname = PQfnumber(res, "typname");
6348 i_typnamespace = PQfnumber(res, "typnamespace");
6349 i_typacl = PQfnumber(res, "typacl");
6350 i_acldefault = PQfnumber(res, "acldefault");
6351 i_typowner = PQfnumber(res, "typowner");
6352 i_typelem = PQfnumber(res, "typelem");
6353 i_typrelid = PQfnumber(res, "typrelid");
6354 i_typrelkind = PQfnumber(res, "typrelkind");
6355 i_typtype = PQfnumber(res, "typtype");
6356 i_typisdefined = PQfnumber(res, "typisdefined");
6357 i_isarray = PQfnumber(res, "isarray");
6358 i_typarray = PQfnumber(res, "typarray");
6359
6360 for (i = 0; i < ntups; i++)
6361 {
6362 tyinfo[i].dobj.objType = DO_TYPE;
6363 tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6364 tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6365 AssignDumpId(&tyinfo[i].dobj);
6366 tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6367 tyinfo[i].dobj.namespace =
6369 tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6370 tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6371 tyinfo[i].dacl.privtype = 0;
6372 tyinfo[i].dacl.initprivs = NULL;
6373 tyinfo[i].ftypname = NULL; /* may get filled later */
6374 tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6375 tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6376 tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6377 tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6378 tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6379 tyinfo[i].shellType = NULL;
6380
6381 if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6382 tyinfo[i].isDefined = true;
6383 else
6384 tyinfo[i].isDefined = false;
6385
6386 if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6387 tyinfo[i].isArray = true;
6388 else
6389 tyinfo[i].isArray = false;
6390
6391 tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6392
6393 if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6394 tyinfo[i].isMultirange = true;
6395 else
6396 tyinfo[i].isMultirange = false;
6397
6398 /* Decide whether we want to dump it */
6400
6401 /* Mark whether type has an ACL */
6402 if (!PQgetisnull(res, i, i_typacl))
6403 tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6404
6405 /*
6406 * If it's a domain, fetch info about its constraints, if any
6407 */
6408 tyinfo[i].nDomChecks = 0;
6409 tyinfo[i].domChecks = NULL;
6410 tyinfo[i].notnull = NULL;
6411 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6412 tyinfo[i].typtype == TYPTYPE_DOMAIN)
6414
6415 /*
6416 * If it's a base type, make a DumpableObject representing a shell
6417 * definition of the type. We will need to dump that ahead of the I/O
6418 * functions for the type. Similarly, range types need a shell
6419 * definition in case they have a canonicalize function.
6420 *
6421 * Note: the shell type doesn't have a catId. You might think it
6422 * should copy the base type's catId, but then it might capture the
6423 * pg_depend entries for the type, which we don't want.
6424 */
6425 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6426 (tyinfo[i].typtype == TYPTYPE_BASE ||
6427 tyinfo[i].typtype == TYPTYPE_RANGE))
6428 {
6430 stinfo->dobj.objType = DO_SHELL_TYPE;
6431 stinfo->dobj.catId = nilCatalogId;
6432 AssignDumpId(&stinfo->dobj);
6433 stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6434 stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6435 stinfo->baseType = &(tyinfo[i]);
6436 tyinfo[i].shellType = stinfo;
6437
6438 /*
6439 * Initially mark the shell type as not to be dumped. We'll only
6440 * dump it if the I/O or canonicalize functions need to be dumped;
6441 * this is taken care of while sorting dependencies.
6442 */
6443 stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6444 }
6445 }
6446
6447 PQclear(res);
6448
6449 destroyPQExpBuffer(query);
6450}
6451
6452/*
6453 * getOperators:
6454 * get information about all operators in the system catalogs
6455 */
6456void
6458{
6459 PGresult *res;
6460 int ntups;
6461 int i;
6464 int i_tableoid;
6465 int i_oid;
6466 int i_oprname;
6467 int i_oprnamespace;
6468 int i_oprowner;
6469 int i_oprkind;
6470 int i_oprleft;
6471 int i_oprright;
6472 int i_oprcode;
6473
6474 /*
6475 * find all operators, including builtin operators; we filter out
6476 * system-defined operators at dump-out time.
6477 */
6478
6479 appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6480 "oprnamespace, "
6481 "oprowner, "
6482 "oprkind, "
6483 "oprleft, "
6484 "oprright, "
6485 "oprcode::oid AS oprcode "
6486 "FROM pg_operator");
6487
6488 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6489
6490 ntups = PQntuples(res);
6491
6493
6494 i_tableoid = PQfnumber(res, "tableoid");
6495 i_oid = PQfnumber(res, "oid");
6496 i_oprname = PQfnumber(res, "oprname");
6497 i_oprnamespace = PQfnumber(res, "oprnamespace");
6498 i_oprowner = PQfnumber(res, "oprowner");
6499 i_oprkind = PQfnumber(res, "oprkind");
6500 i_oprleft = PQfnumber(res, "oprleft");
6501 i_oprright = PQfnumber(res, "oprright");
6502 i_oprcode = PQfnumber(res, "oprcode");
6503
6504 for (i = 0; i < ntups; i++)
6505 {
6506 oprinfo[i].dobj.objType = DO_OPERATOR;
6507 oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6508 oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6509 AssignDumpId(&oprinfo[i].dobj);
6510 oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6511 oprinfo[i].dobj.namespace =
6513 oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6514 oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6515 oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6516 oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6517 oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6518
6519 /* Decide whether we want to dump it */
6521 }
6522
6523 PQclear(res);
6524
6525 destroyPQExpBuffer(query);
6526}
6527
6528/*
6529 * getCollations:
6530 * get information about all collations in the system catalogs
6531 */
6532void
6534{
6535 PGresult *res;
6536 int ntups;
6537 int i;
6538 PQExpBuffer query;
6540 int i_tableoid;
6541 int i_oid;
6542 int i_collname;
6543 int i_collnamespace;
6544 int i_collowner;
6545 int i_collencoding;
6546
6547 query = createPQExpBuffer();
6548
6549 /*
6550 * find all collations, including builtin collations; we filter out
6551 * system-defined collations at dump-out time.
6552 */
6553
6554 appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6555 "collnamespace, "
6556 "collowner, "
6557 "collencoding "
6558 "FROM pg_collation");
6559
6560 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6561
6562 ntups = PQntuples(res);
6563
6565
6566 i_tableoid = PQfnumber(res, "tableoid");
6567 i_oid = PQfnumber(res, "oid");
6568 i_collname = PQfnumber(res, "collname");
6569 i_collnamespace = PQfnumber(res, "collnamespace");
6570 i_collowner = PQfnumber(res, "collowner");
6571 i_collencoding = PQfnumber(res, "collencoding");
6572
6573 for (i = 0; i < ntups; i++)
6574 {
6575 collinfo[i].dobj.objType = DO_COLLATION;
6576 collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6577 collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6578 AssignDumpId(&collinfo[i].dobj);
6579 collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6580 collinfo[i].dobj.namespace =
6582 collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6583 collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6584
6585 /* Decide whether we want to dump it */
6587 }
6588
6589 PQclear(res);
6590
6591 destroyPQExpBuffer(query);
6592}
6593
6594/*
6595 * getConversions:
6596 * get information about all conversions in the system catalogs
6597 */
6598void
6600{
6601 PGresult *res;
6602 int ntups;
6603 int i;
6604 PQExpBuffer query;
6606 int i_tableoid;
6607 int i_oid;
6608 int i_conname;
6609 int i_connamespace;
6610 int i_conowner;
6611
6612 query = createPQExpBuffer();
6613
6614 /*
6615 * find all conversions, including builtin conversions; we filter out
6616 * system-defined conversions at dump-out time.
6617 */
6618
6619 appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6620 "connamespace, "
6621 "conowner "
6622 "FROM pg_conversion");
6623
6624 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6625
6626 ntups = PQntuples(res);
6627
6629
6630 i_tableoid = PQfnumber(res, "tableoid");
6631 i_oid = PQfnumber(res, "oid");
6632 i_conname = PQfnumber(res, "conname");
6633 i_connamespace = PQfnumber(res, "connamespace");
6634 i_conowner = PQfnumber(res, "conowner");
6635
6636 for (i = 0; i < ntups; i++)
6637 {
6638 convinfo[i].dobj.objType = DO_CONVERSION;
6639 convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6640 convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6641 AssignDumpId(&convinfo[i].dobj);
6642 convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6643 convinfo[i].dobj.namespace =
6645 convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6646
6647 /* Decide whether we want to dump it */
6649 }
6650
6651 PQclear(res);
6652
6653 destroyPQExpBuffer(query);
6654}
6655
6656/*
6657 * getAccessMethods:
6658 * get information about all user-defined access methods
6659 */
6660void
6662{
6663 PGresult *res;
6664 int ntups;
6665 int i;
6666 PQExpBuffer query;
6668 int i_tableoid;
6669 int i_oid;
6670 int i_amname;
6671 int i_amhandler;
6672 int i_amtype;
6673
6674 query = createPQExpBuffer();
6675
6676 /*
6677 * Select all access methods from pg_am table. v9.6 introduced CREATE
6678 * ACCESS METHOD, so earlier versions usually have only built-in access
6679 * methods. v9.6 also changed the access method API, replacing dozens of
6680 * pg_am columns with amhandler. Even if a user created an access method
6681 * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6682 * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6683 * pg_am just to facilitate findAccessMethodByOid() providing the
6684 * OID-to-name mapping.
6685 */
6686 appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6687 if (fout->remoteVersion >= 90600)
6689 "amtype, "
6690 "amhandler::pg_catalog.regproc AS amhandler ");
6691 else
6693 "'i'::pg_catalog.\"char\" AS amtype, "
6694 "'-'::pg_catalog.regproc AS amhandler ");
6695 appendPQExpBufferStr(query, "FROM pg_am");
6696
6697 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6698
6699 ntups = PQntuples(res);
6700
6702
6703 i_tableoid = PQfnumber(res, "tableoid");
6704 i_oid = PQfnumber(res, "oid");
6705 i_amname = PQfnumber(res, "amname");
6706 i_amhandler = PQfnumber(res, "amhandler");
6707 i_amtype = PQfnumber(res, "amtype");
6708
6709 for (i = 0; i < ntups; i++)
6710 {
6711 aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6712 aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6713 aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6714 AssignDumpId(&aminfo[i].dobj);
6715 aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6716 aminfo[i].dobj.namespace = NULL;
6717 aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6718 aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6719
6720 /* Decide whether we want to dump it */
6722 }
6723
6724 PQclear(res);
6725
6726 destroyPQExpBuffer(query);
6727}
6728
6729
6730/*
6731 * getOpclasses:
6732 * get information about all opclasses in the system catalogs
6733 */
6734void
6736{
6737 PGresult *res;
6738 int ntups;
6739 int i;
6742 int i_tableoid;
6743 int i_oid;
6744 int i_opcmethod;
6745 int i_opcname;
6746 int i_opcnamespace;
6747 int i_opcowner;
6748
6749 /*
6750 * find all opclasses, including builtin opclasses; we filter out
6751 * system-defined opclasses at dump-out time.
6752 */
6753
6754 appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6755 "opcnamespace, "
6756 "opcowner "
6757 "FROM pg_opclass");
6758
6759 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6760
6761 ntups = PQntuples(res);
6762
6764
6765 i_tableoid = PQfnumber(res, "tableoid");
6766 i_oid = PQfnumber(res, "oid");
6767 i_opcmethod = PQfnumber(res, "opcmethod");
6768 i_opcname = PQfnumber(res, "opcname");
6769 i_opcnamespace = PQfnumber(res, "opcnamespace");
6770 i_opcowner = PQfnumber(res, "opcowner");
6771
6772 for (i = 0; i < ntups; i++)
6773 {
6774 opcinfo[i].dobj.objType = DO_OPCLASS;
6775 opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6776 opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6777 AssignDumpId(&opcinfo[i].dobj);
6778 opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6779 opcinfo[i].dobj.namespace =
6781 opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6782 opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6783
6784 /* Decide whether we want to dump it */
6786 }
6787
6788 PQclear(res);
6789
6790 destroyPQExpBuffer(query);
6791}
6792
6793/*
6794 * getOpfamilies:
6795 * get information about all opfamilies in the system catalogs
6796 */
6797void
6799{
6800 PGresult *res;
6801 int ntups;
6802 int i;
6803 PQExpBuffer query;
6805 int i_tableoid;
6806 int i_oid;
6807 int i_opfmethod;
6808 int i_opfname;
6809 int i_opfnamespace;
6810 int i_opfowner;
6811
6812 query = createPQExpBuffer();
6813
6814 /*
6815 * find all opfamilies, including builtin opfamilies; we filter out
6816 * system-defined opfamilies at dump-out time.
6817 */
6818
6819 appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6820 "opfnamespace, "
6821 "opfowner "
6822 "FROM pg_opfamily");
6823
6824 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6825
6826 ntups = PQntuples(res);
6827
6829
6830 i_tableoid = PQfnumber(res, "tableoid");
6831 i_oid = PQfnumber(res, "oid");
6832 i_opfname = PQfnumber(res, "opfname");
6833 i_opfmethod = PQfnumber(res, "opfmethod");
6834 i_opfnamespace = PQfnumber(res, "opfnamespace");
6835 i_opfowner = PQfnumber(res, "opfowner");
6836
6837 for (i = 0; i < ntups; i++)
6838 {
6839 opfinfo[i].dobj.objType = DO_OPFAMILY;
6840 opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6841 opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6842 AssignDumpId(&opfinfo[i].dobj);
6843 opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6844 opfinfo[i].dobj.namespace =
6846 opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6847 opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6848
6849 /* Decide whether we want to dump it */
6851 }
6852
6853 PQclear(res);
6854
6855 destroyPQExpBuffer(query);
6856}
6857
6858/*
6859 * getAggregates:
6860 * get information about all user-defined aggregates in the system catalogs
6861 */
6862void
6864{
6865 DumpOptions *dopt = fout->dopt;
6866 PGresult *res;
6867 int ntups;
6868 int i;
6871 int i_tableoid;
6872 int i_oid;
6873 int i_aggname;
6874 int i_aggnamespace;
6875 int i_pronargs;
6876 int i_proargtypes;
6877 int i_proowner;
6878 int i_aggacl;
6879 int i_acldefault;
6880
6881 /*
6882 * Find all interesting aggregates. See comment in getFuncs() for the
6883 * rationale behind the filtering logic.
6884 */
6885 if (fout->remoteVersion >= 90600)
6886 {
6887 const char *agg_check;
6888
6889 agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6890 : "p.proisagg");
6891
6892 appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6893 "p.proname AS aggname, "
6894 "p.pronamespace AS aggnamespace, "
6895 "p.pronargs, p.proargtypes, "
6896 "p.proowner, "
6897 "p.proacl AS aggacl, "
6898 "acldefault('f', p.proowner) AS acldefault "
6899 "FROM pg_proc p "
6900 "LEFT JOIN pg_init_privs pip ON "
6901 "(p.oid = pip.objoid "
6902 "AND pip.classoid = 'pg_proc'::regclass "
6903 "AND pip.objsubid = 0) "
6904 "WHERE %s AND ("
6905 "p.pronamespace != "
6906 "(SELECT oid FROM pg_namespace "
6907 "WHERE nspname = 'pg_catalog') OR "
6908 "p.proacl IS DISTINCT FROM pip.initprivs",
6909 agg_check);
6910 if (dopt->binary_upgrade)
6912 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6913 "classid = 'pg_proc'::regclass AND "
6914 "objid = p.oid AND "
6915 "refclassid = 'pg_extension'::regclass AND "
6916 "deptype = 'e')");
6917 appendPQExpBufferChar(query, ')');
6918 }
6919 else
6920 {
6921 appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6922 "pronamespace AS aggnamespace, "
6923 "pronargs, proargtypes, "
6924 "proowner, "
6925 "proacl AS aggacl, "
6926 "acldefault('f', proowner) AS acldefault "
6927 "FROM pg_proc p "
6928 "WHERE proisagg AND ("
6929 "pronamespace != "
6930 "(SELECT oid FROM pg_namespace "
6931 "WHERE nspname = 'pg_catalog')");
6932 if (dopt->binary_upgrade)
6934 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6935 "classid = 'pg_proc'::regclass AND "
6936 "objid = p.oid AND "
6937 "refclassid = 'pg_extension'::regclass AND "
6938 "deptype = 'e')");
6939 appendPQExpBufferChar(query, ')');
6940 }
6941
6942 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6943
6944 ntups = PQntuples(res);
6945
6947
6948 i_tableoid = PQfnumber(res, "tableoid");
6949 i_oid = PQfnumber(res, "oid");
6950 i_aggname = PQfnumber(res, "aggname");
6951 i_aggnamespace = PQfnumber(res, "aggnamespace");
6952 i_pronargs = PQfnumber(res, "pronargs");
6953 i_proargtypes = PQfnumber(res, "proargtypes");
6954 i_proowner = PQfnumber(res, "proowner");
6955 i_aggacl = PQfnumber(res, "aggacl");
6956 i_acldefault = PQfnumber(res, "acldefault");
6957
6958 for (i = 0; i < ntups; i++)
6959 {
6960 agginfo[i].aggfn.dobj.objType = DO_AGG;
6961 agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6962 agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6963 AssignDumpId(&agginfo[i].aggfn.dobj);
6964 agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6965 agginfo[i].aggfn.dobj.namespace =
6967 agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6968 agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6969 agginfo[i].aggfn.dacl.privtype = 0;
6970 agginfo[i].aggfn.dacl.initprivs = NULL;
6971 agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6972 agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6973 agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6974 agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6975 if (agginfo[i].aggfn.nargs == 0)
6976 agginfo[i].aggfn.argtypes = NULL;
6977 else
6978 {
6979 agginfo[i].aggfn.argtypes = pg_malloc_array(Oid, agginfo[i].aggfn.nargs);
6981 agginfo[i].aggfn.argtypes,
6982 agginfo[i].aggfn.nargs);
6983 }
6984 agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6985
6986 /* Decide whether we want to dump it */
6987 selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6988
6989 /* Mark whether aggregate has an ACL */
6990 if (!PQgetisnull(res, i, i_aggacl))
6991 agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6992 }
6993
6994 PQclear(res);
6995
6996 destroyPQExpBuffer(query);
6997}
6998
6999/*
7000 * getFuncs:
7001 * get information about all user-defined functions in the system catalogs
7002 */
7003void
7005{
7006 DumpOptions *dopt = fout->dopt;
7007 PGresult *res;
7008 int ntups;
7009 int i;
7011 FuncInfo *finfo;
7012 int i_tableoid;
7013 int i_oid;
7014 int i_proname;
7015 int i_pronamespace;
7016 int i_proowner;
7017 int i_prolang;
7018 int i_pronargs;
7019 int i_proargtypes;
7020 int i_prorettype;
7021 int i_proacl;
7022 int i_acldefault;
7023
7024 /*
7025 * Find all interesting functions. This is a bit complicated:
7026 *
7027 * 1. Always exclude aggregates; those are handled elsewhere.
7028 *
7029 * 2. Always exclude functions that are internally dependent on something
7030 * else, since presumably those will be created as a result of creating
7031 * the something else. This currently acts only to suppress constructor
7032 * functions for range types. Note this is OK only because the
7033 * constructors don't have any dependencies the range type doesn't have;
7034 * otherwise we might not get creation ordering correct.
7035 *
7036 * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
7037 * they're members of extensions and we are in binary-upgrade mode then
7038 * include them, since we want to dump extension members individually in
7039 * that mode. Also, if they are used by casts or transforms then we need
7040 * to gather the information about them, though they won't be dumped if
7041 * they are built-in. Also, in 9.6 and up, include functions in
7042 * pg_catalog if they have an ACL different from what's shown in
7043 * pg_init_privs (so we have to join to pg_init_privs; annoying).
7044 */
7045 if (fout->remoteVersion >= 90600)
7046 {
7047 const char *not_agg_check;
7048
7049 not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
7050 : "NOT p.proisagg");
7051
7052 appendPQExpBuffer(query,
7053 "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
7054 "p.pronargs, p.proargtypes, p.prorettype, "
7055 "p.proacl, "
7056 "acldefault('f', p.proowner) AS acldefault, "
7057 "p.pronamespace, "
7058 "p.proowner "
7059 "FROM pg_proc p "
7060 "LEFT JOIN pg_init_privs pip ON "
7061 "(p.oid = pip.objoid "
7062 "AND pip.classoid = 'pg_proc'::regclass "
7063 "AND pip.objsubid = 0) "
7064 "WHERE %s"
7065 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
7066 "WHERE classid = 'pg_proc'::regclass AND "
7067 "objid = p.oid AND deptype = 'i')"
7068 "\n AND ("
7069 "\n pronamespace != "
7070 "(SELECT oid FROM pg_namespace "
7071 "WHERE nspname = 'pg_catalog')"
7072 "\n OR EXISTS (SELECT 1 FROM pg_cast"
7073 "\n WHERE pg_cast.oid > %u "
7074 "\n AND p.oid = pg_cast.castfunc)"
7075 "\n OR EXISTS (SELECT 1 FROM pg_transform"
7076 "\n WHERE pg_transform.oid > %u AND "
7077 "\n (p.oid = pg_transform.trffromsql"
7078 "\n OR p.oid = pg_transform.trftosql))",
7082 if (dopt->binary_upgrade)
7084 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7085 "classid = 'pg_proc'::regclass AND "
7086 "objid = p.oid AND "
7087 "refclassid = 'pg_extension'::regclass AND "
7088 "deptype = 'e')");
7090 "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
7091 appendPQExpBufferChar(query, ')');
7092 }
7093 else
7094 {
7095 appendPQExpBuffer(query,
7096 "SELECT tableoid, oid, proname, prolang, "
7097 "pronargs, proargtypes, prorettype, proacl, "
7098 "acldefault('f', proowner) AS acldefault, "
7099 "pronamespace, "
7100 "proowner "
7101 "FROM pg_proc p "
7102 "WHERE NOT proisagg"
7103 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
7104 "WHERE classid = 'pg_proc'::regclass AND "
7105 "objid = p.oid AND deptype = 'i')"
7106 "\n AND ("
7107 "\n pronamespace != "
7108 "(SELECT oid FROM pg_namespace "
7109 "WHERE nspname = 'pg_catalog')"
7110 "\n OR EXISTS (SELECT 1 FROM pg_cast"
7111 "\n WHERE pg_cast.oid > '%u'::oid"
7112 "\n AND p.oid = pg_cast.castfunc)",
7114
7115 if (fout->remoteVersion >= 90500)
7116 appendPQExpBuffer(query,
7117 "\n OR EXISTS (SELECT 1 FROM pg_transform"
7118 "\n WHERE pg_transform.oid > '%u'::oid"
7119 "\n AND (p.oid = pg_transform.trffromsql"
7120 "\n OR p.oid = pg_transform.trftosql))",
7122
7123 if (dopt->binary_upgrade)
7125 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7126 "classid = 'pg_proc'::regclass AND "
7127 "objid = p.oid AND "
7128 "refclassid = 'pg_extension'::regclass AND "
7129 "deptype = 'e')");
7130 appendPQExpBufferChar(query, ')');
7131 }
7132
7133 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7134
7135 ntups = PQntuples(res);
7136
7137 finfo = pg_malloc0_array(FuncInfo, ntups);
7138
7139 i_tableoid = PQfnumber(res, "tableoid");
7140 i_oid = PQfnumber(res, "oid");
7141 i_proname = PQfnumber(res, "proname");
7142 i_pronamespace = PQfnumber(res, "pronamespace");
7143 i_proowner = PQfnumber(res, "proowner");
7144 i_prolang = PQfnumber(res, "prolang");
7145 i_pronargs = PQfnumber(res, "pronargs");
7146 i_proargtypes = PQfnumber(res, "proargtypes");
7147 i_prorettype = PQfnumber(res, "prorettype");
7148 i_proacl = PQfnumber(res, "proacl");
7149 i_acldefault = PQfnumber(res, "acldefault");
7150
7151 for (i = 0; i < ntups; i++)
7152 {
7153 finfo[i].dobj.objType = DO_FUNC;
7154 finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7155 finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7156 AssignDumpId(&finfo[i].dobj);
7157 finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7158 finfo[i].dobj.namespace =
7160 finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7162 finfo[i].dacl.privtype = 0;
7163 finfo[i].dacl.initprivs = NULL;
7164 finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7165 finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7166 finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7167 finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7168 if (finfo[i].nargs == 0)
7169 finfo[i].argtypes = NULL;
7170 else
7171 {
7172 finfo[i].argtypes = pg_malloc_array(Oid, finfo[i].nargs);
7174 finfo[i].argtypes, finfo[i].nargs);
7175 }
7176 finfo[i].postponed_def = false; /* might get set during sort */
7177
7178 /* Decide whether we want to dump it */
7179 selectDumpableObject(&(finfo[i].dobj), fout);
7180
7181 /* Mark whether function has an ACL */
7182 if (!PQgetisnull(res, i, i_proacl))
7184 }
7185
7186 PQclear(res);
7187
7188 destroyPQExpBuffer(query);
7189}
7190
7191/*
7192 * getRelationStatistics
7193 * register the statistics object as a dependent of the relation.
7194 *
7195 * reltuples is passed as a string to avoid complexities in converting from/to
7196 * floating point.
7197 */
7198static RelStatsInfo *
7200 char *reltuples, int32 relallvisible,
7201 int32 relallfrozen, char relkind,
7202 char **indAttNames, int nindAttNames)
7203{
7204 if (!fout->dopt->dumpStatistics)
7205 return NULL;
7206
7207 if ((relkind == RELKIND_RELATION) ||
7208 (relkind == RELKIND_PARTITIONED_TABLE) ||
7209 (relkind == RELKIND_INDEX) ||
7210 (relkind == RELKIND_PARTITIONED_INDEX) ||
7211 (relkind == RELKIND_MATVIEW ||
7212 relkind == RELKIND_FOREIGN_TABLE))
7213 {
7215 DumpableObject *dobj = &info->dobj;
7216
7217 dobj->objType = DO_REL_STATS;
7218 dobj->catId.tableoid = 0;
7219 dobj->catId.oid = 0;
7220 AssignDumpId(dobj);
7222 dobj->dependencies[0] = rel->dumpId;
7223 dobj->nDeps = 1;
7224 dobj->allocDeps = 1;
7226 dobj->name = pg_strdup(rel->name);
7227 dobj->namespace = rel->namespace;
7228 info->relid = rel->catId.oid;
7229 info->relpages = relpages;
7230 info->reltuples = pstrdup(reltuples);
7231 info->relallvisible = relallvisible;
7232 info->relallfrozen = relallfrozen;
7233 info->relkind = relkind;
7234 info->indAttNames = indAttNames;
7235 info->nindAttNames = nindAttNames;
7236
7237 /*
7238 * Ordinarily, stats go in SECTION_DATA for tables and
7239 * SECTION_POST_DATA for indexes.
7240 *
7241 * However, the section may be updated later for materialized view
7242 * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7243 * the stats, so the stats must be restored after the data. Also, the
7244 * materialized view definition may be postponed to SECTION_POST_DATA
7245 * (see repairMatViewBoundaryMultiLoop()).
7246 */
7247 switch (info->relkind)
7248 {
7249 case RELKIND_RELATION:
7251 case RELKIND_MATVIEW:
7253 info->section = SECTION_DATA;
7254 break;
7255 case RELKIND_INDEX:
7257 info->section = SECTION_POST_DATA;
7258 break;
7259 default:
7260 pg_fatal("cannot dump statistics for relation kind \"%c\"",
7261 info->relkind);
7262 }
7263
7264 return info;
7265 }
7266 return NULL;
7267}
7268
7269/*
7270 * getTables
7271 * read all the tables (no indexes) in the system catalogs,
7272 * and return them as an array of TableInfo structures
7273 *
7274 * *numTables is set to the number of tables read in
7275 */
7276TableInfo *
7278{
7279 DumpOptions *dopt = fout->dopt;
7280 PGresult *res;
7281 int ntups;
7282 int i;
7284 TableInfo *tblinfo;
7285 int i_reltableoid;
7286 int i_reloid;
7287 int i_relname;
7288 int i_relnamespace;
7289 int i_relkind;
7290 int i_reltype;
7291 int i_relowner;
7292 int i_relchecks;
7293 int i_relhasindex;
7294 int i_relhasrules;
7295 int i_relpages;
7296 int i_reltuples;
7297 int i_relallvisible;
7298 int i_relallfrozen;
7299 int i_toastpages;
7300 int i_owning_tab;
7301 int i_owning_col;
7302 int i_reltablespace;
7303 int i_relhasoids;
7304 int i_relhastriggers;
7305 int i_relpersistence;
7306 int i_relispopulated;
7307 int i_relreplident;
7308 int i_relrowsec;
7309 int i_relforcerowsec;
7310 int i_relfrozenxid;
7311 int i_toastfrozenxid;
7312 int i_toastoid;
7313 int i_relminmxid;
7314 int i_toastminmxid;
7315 int i_reloptions;
7316 int i_checkoption;
7318 int i_reloftype;
7319 int i_foreignserver;
7320 int i_amname;
7322 int i_relacl;
7323 int i_acldefault;
7324 int i_ispartition;
7325
7326 /*
7327 * Find all the tables and table-like objects.
7328 *
7329 * We must fetch all tables in this phase because otherwise we cannot
7330 * correctly identify inherited columns, owned sequences, etc.
7331 *
7332 * We include system catalogs, so that we can work if a user table is
7333 * defined to inherit from a system catalog (pretty weird, but...)
7334 *
7335 * Note: in this phase we should collect only a minimal amount of
7336 * information about each table, basically just enough to decide if it is
7337 * interesting. In particular, since we do not yet have lock on any user
7338 * table, we MUST NOT invoke any server-side data collection functions
7339 * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7340 * wrong answers if any concurrent DDL is happening.
7341 */
7342
7344 "SELECT c.tableoid, c.oid, c.relname, "
7345 "c.relnamespace, c.relkind, c.reltype, "
7346 "c.relowner, "
7347 "c.relchecks, "
7348 "c.relhasindex, c.relhasrules, c.relpages, "
7349 "c.reltuples, c.relallvisible, ");
7350
7351 if (fout->remoteVersion >= 180000)
7352 appendPQExpBufferStr(query, "c.relallfrozen, ");
7353 else
7354 appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7355
7357 "c.relhastriggers, c.relpersistence, "
7358 "c.reloftype, "
7359 "c.relacl, "
7360 "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7361 " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7362 "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7363 "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7364 "ELSE 0 END AS foreignserver, "
7365 "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7366 "tc.oid AS toid, "
7367 "tc.relpages AS toastpages, "
7368 "tc.reloptions AS toast_reloptions, "
7369 "d.refobjid AS owning_tab, "
7370 "d.refobjsubid AS owning_col, "
7371 "tsp.spcname AS reltablespace, ");
7372
7373 if (fout->remoteVersion >= 120000)
7375 "false AS relhasoids, ");
7376 else
7378 "c.relhasoids, ");
7379
7380 if (fout->remoteVersion >= 90300)
7382 "c.relispopulated, ");
7383 else
7385 "'t' as relispopulated, ");
7386
7387 if (fout->remoteVersion >= 90400)
7389 "c.relreplident, ");
7390 else
7392 "'d' AS relreplident, ");
7393
7394 if (fout->remoteVersion >= 90500)
7396 "c.relrowsecurity, c.relforcerowsecurity, ");
7397 else
7399 "false AS relrowsecurity, "
7400 "false AS relforcerowsecurity, ");
7401
7402 if (fout->remoteVersion >= 90300)
7404 "c.relminmxid, tc.relminmxid AS tminmxid, ");
7405 else
7407 "0 AS relminmxid, 0 AS tminmxid, ");
7408
7409 if (fout->remoteVersion >= 90300)
7411 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7412 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7413 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7414 else
7416 "c.reloptions, NULL AS checkoption, ");
7417
7418 if (fout->remoteVersion >= 90600)
7420 "am.amname, ");
7421 else
7423 "NULL AS amname, ");
7424
7425 if (fout->remoteVersion >= 90600)
7427 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7428 else
7430 "false AS is_identity_sequence, ");
7431
7432 if (fout->remoteVersion >= 100000)
7434 "c.relispartition AS ispartition ");
7435 else
7437 "false AS ispartition ");
7438
7439 /*
7440 * Left join to pg_depend to pick up dependency info linking sequences to
7441 * their owning column, if any (note this dependency is AUTO except for
7442 * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7443 * collect the spcname.
7444 */
7446 "\nFROM pg_class c\n"
7447 "LEFT JOIN pg_depend d ON "
7448 "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7449 "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7450 "d.objsubid = 0 AND "
7451 "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7452 "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7453
7454 /*
7455 * In 9.6 and up, left join to pg_am to pick up the amname.
7456 */
7457 if (fout->remoteVersion >= 90600)
7459 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7460
7461 /*
7462 * We purposefully ignore toast OIDs for partitioned tables; the reason is
7463 * that versions 10 and 11 have them, but later versions do not, so
7464 * emitting them causes the upgrade to fail.
7465 */
7467 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7468 " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7469 " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7470
7471 /*
7472 * Restrict to interesting relkinds (in particular, not indexes). Not all
7473 * relkinds are possible in older servers, but it's not worth the trouble
7474 * to emit a version-dependent list.
7475 *
7476 * Composite-type table entries won't be dumped as such, but we have to
7477 * make a DumpableObject for them so that we can track dependencies of the
7478 * composite type (pg_depend entries for columns of the composite type
7479 * link to the pg_class entry not the pg_type entry).
7480 */
7482 "WHERE c.relkind IN ("
7491 "ORDER BY c.oid");
7492
7493 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7494
7495 ntups = PQntuples(res);
7496
7497 *numTables = ntups;
7498
7499 /*
7500 * Extract data from result and lock dumpable tables. We do the locking
7501 * before anything else, to minimize the window wherein a table could
7502 * disappear under us.
7503 *
7504 * Note that we have to save info about all tables here, even when dumping
7505 * only one, because we don't yet know which tables might be inheritance
7506 * ancestors of the target table.
7507 */
7508 tblinfo = pg_malloc0_array(TableInfo, ntups);
7509
7510 i_reltableoid = PQfnumber(res, "tableoid");
7511 i_reloid = PQfnumber(res, "oid");
7512 i_relname = PQfnumber(res, "relname");
7513 i_relnamespace = PQfnumber(res, "relnamespace");
7514 i_relkind = PQfnumber(res, "relkind");
7515 i_reltype = PQfnumber(res, "reltype");
7516 i_relowner = PQfnumber(res, "relowner");
7517 i_relchecks = PQfnumber(res, "relchecks");
7518 i_relhasindex = PQfnumber(res, "relhasindex");
7519 i_relhasrules = PQfnumber(res, "relhasrules");
7520 i_relpages = PQfnumber(res, "relpages");
7521 i_reltuples = PQfnumber(res, "reltuples");
7522 i_relallvisible = PQfnumber(res, "relallvisible");
7523 i_relallfrozen = PQfnumber(res, "relallfrozen");
7524 i_toastpages = PQfnumber(res, "toastpages");
7525 i_owning_tab = PQfnumber(res, "owning_tab");
7526 i_owning_col = PQfnumber(res, "owning_col");
7527 i_reltablespace = PQfnumber(res, "reltablespace");
7528 i_relhasoids = PQfnumber(res, "relhasoids");
7529 i_relhastriggers = PQfnumber(res, "relhastriggers");
7530 i_relpersistence = PQfnumber(res, "relpersistence");
7531 i_relispopulated = PQfnumber(res, "relispopulated");
7532 i_relreplident = PQfnumber(res, "relreplident");
7533 i_relrowsec = PQfnumber(res, "relrowsecurity");
7534 i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7535 i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7536 i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7537 i_toastoid = PQfnumber(res, "toid");
7538 i_relminmxid = PQfnumber(res, "relminmxid");
7539 i_toastminmxid = PQfnumber(res, "tminmxid");
7540 i_reloptions = PQfnumber(res, "reloptions");
7541 i_checkoption = PQfnumber(res, "checkoption");
7542 i_toastreloptions = PQfnumber(res, "toast_reloptions");
7543 i_reloftype = PQfnumber(res, "reloftype");
7544 i_foreignserver = PQfnumber(res, "foreignserver");
7545 i_amname = PQfnumber(res, "amname");
7546 i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7547 i_relacl = PQfnumber(res, "relacl");
7548 i_acldefault = PQfnumber(res, "acldefault");
7549 i_ispartition = PQfnumber(res, "ispartition");
7550
7551 if (dopt->lockWaitTimeout)
7552 {
7553 /*
7554 * Arrange to fail instead of waiting forever for a table lock.
7555 *
7556 * NB: this coding assumes that the only queries issued within the
7557 * following loop are LOCK TABLEs; else the timeout may be undesirably
7558 * applied to other things too.
7559 */
7560 resetPQExpBuffer(query);
7561 appendPQExpBufferStr(query, "SET statement_timeout = ");
7563 ExecuteSqlStatement(fout, query->data);
7564 }
7565
7566 resetPQExpBuffer(query);
7567
7568 for (i = 0; i < ntups; i++)
7569 {
7570 int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7571 int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7572
7573 tblinfo[i].dobj.objType = DO_TABLE;
7574 tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7575 tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7576 AssignDumpId(&tblinfo[i].dobj);
7577 tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7578 tblinfo[i].dobj.namespace =
7580 tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7581 tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7582 tblinfo[i].dacl.privtype = 0;
7583 tblinfo[i].dacl.initprivs = NULL;
7584 tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7585 tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7586 tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7587 tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7588 tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7589 tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7590 tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7591 if (PQgetisnull(res, i, i_toastpages))
7592 tblinfo[i].toastpages = 0;
7593 else
7594 tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7595 if (PQgetisnull(res, i, i_owning_tab))
7596 {
7597 tblinfo[i].owning_tab = InvalidOid;
7598 tblinfo[i].owning_col = 0;
7599 }
7600 else
7601 {
7602 tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7603 tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7604 }
7606 tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7607 tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7608 tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7609 tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7610 tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7611 tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7612 tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7613 tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7615 tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7616 tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7617 tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7618 tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7619 if (PQgetisnull(res, i, i_checkoption))
7620 tblinfo[i].checkoption = NULL;
7621 else
7622 tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7624 tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7626 if (PQgetisnull(res, i, i_amname))
7627 tblinfo[i].amname = NULL;
7628 else
7629 tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7630 tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7631 tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7632
7633 /* other fields were zeroed above */
7634
7635 /*
7636 * Decide whether we want to dump this table.
7637 */
7638 if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7639 tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7640 else
7641 selectDumpableTable(&tblinfo[i], fout);
7642
7643 /*
7644 * Now, consider the table "interesting" if we need to dump its
7645 * definition, data or its statistics. Later on, we'll skip a lot of
7646 * data collection for uninteresting tables.
7647 *
7648 * Note: the "interesting" flag will also be set by flagInhTables for
7649 * parents of interesting tables, so that we collect necessary
7650 * inheritance info even when the parents are not themselves being
7651 * dumped. This is the main reason why we need an "interesting" flag
7652 * that's separate from the components-to-dump bitmask.
7653 */
7654 tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7658
7659 tblinfo[i].dummy_view = false; /* might get set during sort */
7660 tblinfo[i].postponed_def = false; /* might get set during sort */
7661
7662 /* Tables have data */
7664
7665 /* Mark whether table has an ACL */
7666 if (!PQgetisnull(res, i, i_relacl))
7667 tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7668 tblinfo[i].hascolumnACLs = false; /* may get set later */
7669
7670 /* Add statistics */
7671 if (tblinfo[i].interesting)
7672 {
7673 RelStatsInfo *stats;
7674
7675 stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7676 tblinfo[i].relpages,
7677 PQgetvalue(res, i, i_reltuples),
7678 relallvisible, relallfrozen,
7679 tblinfo[i].relkind, NULL, 0);
7680 if (tblinfo[i].relkind == RELKIND_MATVIEW)
7681 tblinfo[i].stats = stats;
7682 }
7683
7684 /*
7685 * Read-lock target tables to make sure they aren't DROPPED or altered
7686 * in schema before we get around to dumping them.
7687 *
7688 * Note that we don't explicitly lock parents of the target tables; we
7689 * assume our lock on the child is enough to prevent schema
7690 * alterations to parent tables.
7691 *
7692 * NOTE: it'd be kinda nice to lock other relations too, not only
7693 * plain or partitioned tables, but the backend doesn't presently
7694 * allow that.
7695 *
7696 * We only need to lock the table for certain components; see
7697 * pg_dump.h
7698 */
7699 if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7700 (tblinfo[i].relkind == RELKIND_RELATION ||
7701 tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7702 {
7703 /*
7704 * Tables are locked in batches. When dumping from a remote
7705 * server this can save a significant amount of time by reducing
7706 * the number of round trips.
7707 */
7708 if (query->len == 0)
7709 appendPQExpBuffer(query, "LOCK TABLE %s",
7710 fmtQualifiedDumpable(&tblinfo[i]));
7711 else
7712 {
7713 appendPQExpBuffer(query, ", %s",
7714 fmtQualifiedDumpable(&tblinfo[i]));
7715
7716 /* Arbitrarily end a batch when query length reaches 100K. */
7717 if (query->len >= 100000)
7718 {
7719 /* Lock another batch of tables. */
7720 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7721 ExecuteSqlStatement(fout, query->data);
7722 resetPQExpBuffer(query);
7723 }
7724 }
7725 }
7726 }
7727
7728 if (query->len != 0)
7729 {
7730 /* Lock the tables in the last batch. */
7731 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7732 ExecuteSqlStatement(fout, query->data);
7733 }
7734
7735 if (dopt->lockWaitTimeout)
7736 {
7737 ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7738 }
7739
7740 PQclear(res);
7741
7742 destroyPQExpBuffer(query);
7743
7744 return tblinfo;
7745}
7746
7747/*
7748 * getOwnedSeqs
7749 * identify owned sequences and mark them as dumpable if owning table is
7750 *
7751 * We used to do this in getTables(), but it's better to do it after the
7752 * index used by findTableByOid() has been set up.
7753 */
7754void
7756{
7757 int i;
7758
7759 /*
7760 * Force sequences that are "owned" by table columns to be dumped whenever
7761 * their owning table is being dumped.
7762 */
7763 for (i = 0; i < numTables; i++)
7764 {
7765 TableInfo *seqinfo = &tblinfo[i];
7766 TableInfo *owning_tab;
7767
7768 if (!OidIsValid(seqinfo->owning_tab))
7769 continue; /* not an owned sequence */
7770
7771 owning_tab = findTableByOid(seqinfo->owning_tab);
7772 if (owning_tab == NULL)
7773 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7774 seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7775
7776 /*
7777 * For an identity sequence, dump exactly the same components for the
7778 * sequence as for the owning table. This is important because we
7779 * treat the identity sequence as an integral part of the table. For
7780 * example, there is not any DDL command that allows creation of such
7781 * a sequence independently of the table.
7782 *
7783 * For other owned sequences such as serial sequences, we need to dump
7784 * the components that are being dumped for the table and any
7785 * components that the sequence is explicitly marked with.
7786 *
7787 * We can't simply use the set of components which are being dumped
7788 * for the table as the table might be in an extension (and only the
7789 * non-extension components, eg: ACLs if changed, security labels, and
7790 * policies, are being dumped) while the sequence is not (and
7791 * therefore the definition and other components should also be
7792 * dumped).
7793 *
7794 * If the sequence is part of the extension then it should be properly
7795 * marked by checkExtensionMembership() and this will be a no-op as
7796 * the table will be equivalently marked.
7797 */
7798 if (seqinfo->is_identity_sequence)
7799 seqinfo->dobj.dump = owning_tab->dobj.dump;
7800 else
7801 seqinfo->dobj.dump |= owning_tab->dobj.dump;
7802
7803 /* Make sure that necessary data is available if we're dumping it */
7804 if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7805 {
7806 seqinfo->interesting = true;
7807 owning_tab->interesting = true;
7808 }
7809 }
7810}
7811
7812/*
7813 * getInherits
7814 * read all the inheritance information
7815 * from the system catalogs return them in the InhInfo* structure
7816 *
7817 * numInherits is set to the number of pairs read in
7818 */
7819InhInfo *
7821{
7822 PGresult *res;
7823 int ntups;
7824 int i;
7827
7828 int i_inhrelid;
7829 int i_inhparent;
7830
7831 /* find all the inheritance information */
7832 appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7833
7834 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7835
7836 ntups = PQntuples(res);
7837
7838 *numInherits = ntups;
7839
7841
7842 i_inhrelid = PQfnumber(res, "inhrelid");
7843 i_inhparent = PQfnumber(res, "inhparent");
7844
7845 for (i = 0; i < ntups; i++)
7846 {
7847 inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7848 inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7849 }
7850
7851 PQclear(res);
7852
7853 destroyPQExpBuffer(query);
7854
7855 return inhinfo;
7856}
7857
7858/*
7859 * getPartitioningInfo
7860 * get information about partitioning
7861 *
7862 * For the most part, we only collect partitioning info about tables we
7863 * intend to dump. However, this function has to consider all partitioned
7864 * tables in the database, because we need to know about parents of partitions
7865 * we are going to dump even if the parents themselves won't be dumped.
7866 *
7867 * Specifically, what we need to know is whether each partitioned table
7868 * has an "unsafe" partitioning scheme that requires us to force
7869 * load-via-partition-root mode for its children. Currently the only case
7870 * for which we force that is hash partitioning on enum columns, since the
7871 * hash codes depend on enum value OIDs which won't be replicated across
7872 * dump-and-reload. There are other cases in which load-via-partition-root
7873 * might be necessary, but we expect users to cope with them.
7874 */
7875void
7877{
7878 PQExpBuffer query;
7879 PGresult *res;
7880 int ntups;
7881
7882 /* hash partitioning didn't exist before v11 */
7883 if (fout->remoteVersion < 110000)
7884 return;
7885 /* needn't bother if not dumping data */
7886 if (!fout->dopt->dumpData)
7887 return;
7888
7889 query = createPQExpBuffer();
7890
7891 /*
7892 * Unsafe partitioning schemes are exactly those for which hash enum_ops
7893 * appears among the partition opclasses. We needn't check partstrat.
7894 *
7895 * Note that this query may well retrieve info about tables we aren't
7896 * going to dump and hence have no lock on. That's okay since we need not
7897 * invoke any unsafe server-side functions.
7898 */
7900 "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7901 "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7902 "ON c.opcmethod = a.oid\n"
7903 "WHERE opcname = 'enum_ops' "
7904 "AND opcnamespace = 'pg_catalog'::regnamespace "
7905 "AND amname = 'hash') = ANY(partclass)");
7906
7907 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7908
7909 ntups = PQntuples(res);
7910
7911 for (int i = 0; i < ntups; i++)
7912 {
7913 Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7915
7917 if (tbinfo == NULL)
7918 pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7919 tabrelid);
7920 tbinfo->unsafe_partitions = true;
7921 }
7922
7923 PQclear(res);
7924
7925 destroyPQExpBuffer(query);
7926}
7927
7928/*
7929 * getIndexes
7930 * get information about every index on a dumpable table
7931 *
7932 * Note: index data is not returned directly to the caller, but it
7933 * does get entered into the DumpableObject tables.
7934 */
7935void
7936getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7937{
7940 PGresult *res;
7941 int ntups;
7942 int curtblindx;
7944 int i_tableoid,
7945 i_oid,
7946 i_indrelid,
7948 i_relpages,
7953 i_indexdef,
7955 i_indnatts,
7956 i_indkey,
7960 i_contype,
7961 i_conname,
7966 i_conoid,
7967 i_condef,
7973
7974 /*
7975 * We want to perform just one query against pg_index. However, we
7976 * mustn't try to select every row of the catalog and then sort it out on
7977 * the client side, because some of the server-side functions we need
7978 * would be unsafe to apply to tables we don't have lock on. Hence, we
7979 * build an array of the OIDs of tables we care about (and now have lock
7980 * on!), and use a WHERE clause to constrain which rows are selected.
7981 */
7983 for (int i = 0; i < numTables; i++)
7984 {
7985 TableInfo *tbinfo = &tblinfo[i];
7986
7987 if (!tbinfo->hasindex)
7988 continue;
7989
7990 /*
7991 * We can ignore indexes of uninteresting tables.
7992 */
7993 if (!tbinfo->interesting)
7994 continue;
7995
7996 /* OK, we need info for this table */
7997 if (tbloids->len > 1) /* do we have more than the '{'? */
7999 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8000 }
8002
8004 "SELECT t.tableoid, t.oid, i.indrelid, "
8005 "t.relname AS indexname, "
8006 "t.relpages, t.reltuples, t.relallvisible, ");
8007
8008 if (fout->remoteVersion >= 180000)
8009 appendPQExpBufferStr(query, "t.relallfrozen, ");
8010 else
8011 appendPQExpBufferStr(query, "0 AS relallfrozen, ");
8012
8014 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
8015 "i.indkey, i.indisclustered, "
8016 "c.contype, c.conname, "
8017 "c.condeferrable, c.condeferred, "
8018 "c.tableoid AS contableoid, "
8019 "c.oid AS conoid, "
8020 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
8021 "CASE WHEN i.indexprs IS NOT NULL THEN "
8022 "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
8023 " FROM pg_catalog.pg_attribute "
8024 " WHERE attrelid = i.indexrelid) "
8025 "ELSE NULL END AS indattnames, "
8026 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
8027 "t.reloptions AS indreloptions, ");
8028
8029
8030 if (fout->remoteVersion >= 90400)
8032 "i.indisreplident, ");
8033 else
8035 "false AS indisreplident, ");
8036
8037 if (fout->remoteVersion >= 110000)
8039 "inh.inhparent AS parentidx, "
8040 "i.indnkeyatts AS indnkeyatts, "
8041 "i.indnatts AS indnatts, "
8042 "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
8043 " FROM pg_catalog.pg_attribute "
8044 " WHERE attrelid = i.indexrelid AND "
8045 " attstattarget >= 0) AS indstatcols, "
8046 "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
8047 " FROM pg_catalog.pg_attribute "
8048 " WHERE attrelid = i.indexrelid AND "
8049 " attstattarget >= 0) AS indstatvals, ");
8050 else
8052 "0 AS parentidx, "
8053 "i.indnatts AS indnkeyatts, "
8054 "i.indnatts AS indnatts, "
8055 "'' AS indstatcols, "
8056 "'' AS indstatvals, ");
8057
8058 if (fout->remoteVersion >= 150000)
8060 "i.indnullsnotdistinct, ");
8061 else
8063 "false AS indnullsnotdistinct, ");
8064
8065 if (fout->remoteVersion >= 180000)
8067 "c.conperiod ");
8068 else
8070 "NULL AS conperiod ");
8071
8072 /*
8073 * The point of the messy-looking outer join is to find a constraint that
8074 * is related by an internal dependency link to the index. If we find one,
8075 * create a CONSTRAINT entry linked to the INDEX entry. We assume an
8076 * index won't have more than one internal dependency.
8077 *
8078 * Note: the check on conrelid is redundant, but useful because that
8079 * column is indexed while conindid is not.
8080 */
8081 if (fout->remoteVersion >= 110000)
8082 {
8083 appendPQExpBuffer(query,
8084 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8085 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8086 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8087 "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
8088 "LEFT JOIN pg_catalog.pg_constraint c "
8089 "ON (i.indrelid = c.conrelid AND "
8090 "i.indexrelid = c.conindid AND "
8091 "c.contype IN ('p','u','x')) "
8092 "LEFT JOIN pg_catalog.pg_inherits inh "
8093 "ON (inh.inhrelid = indexrelid) "
8094 "WHERE (i.indisvalid OR t2.relkind = 'p') "
8095 "AND i.indisready "
8096 "ORDER BY i.indrelid, indexname",
8097 tbloids->data);
8098 }
8099 else
8100 {
8101 /*
8102 * the test on indisready is necessary in 9.2, and harmless in
8103 * earlier/later versions
8104 */
8105 appendPQExpBuffer(query,
8106 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8107 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8108 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8109 "LEFT JOIN pg_catalog.pg_constraint c "
8110 "ON (i.indrelid = c.conrelid AND "
8111 "i.indexrelid = c.conindid AND "
8112 "c.contype IN ('p','u','x')) "
8113 "WHERE i.indisvalid AND i.indisready "
8114 "ORDER BY i.indrelid, indexname",
8115 tbloids->data);
8116 }
8117
8118 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8119
8120 ntups = PQntuples(res);
8121
8122 i_tableoid = PQfnumber(res, "tableoid");
8123 i_oid = PQfnumber(res, "oid");
8124 i_indrelid = PQfnumber(res, "indrelid");
8125 i_indexname = PQfnumber(res, "indexname");
8126 i_relpages = PQfnumber(res, "relpages");
8127 i_reltuples = PQfnumber(res, "reltuples");
8128 i_relallvisible = PQfnumber(res, "relallvisible");
8129 i_relallfrozen = PQfnumber(res, "relallfrozen");
8130 i_parentidx = PQfnumber(res, "parentidx");
8131 i_indexdef = PQfnumber(res, "indexdef");
8132 i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8133 i_indnatts = PQfnumber(res, "indnatts");
8134 i_indkey = PQfnumber(res, "indkey");
8135 i_indisclustered = PQfnumber(res, "indisclustered");
8136 i_indisreplident = PQfnumber(res, "indisreplident");
8137 i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8138 i_contype = PQfnumber(res, "contype");
8139 i_conname = PQfnumber(res, "conname");
8140 i_condeferrable = PQfnumber(res, "condeferrable");
8141 i_condeferred = PQfnumber(res, "condeferred");
8142 i_conperiod = PQfnumber(res, "conperiod");
8143 i_contableoid = PQfnumber(res, "contableoid");
8144 i_conoid = PQfnumber(res, "conoid");
8145 i_condef = PQfnumber(res, "condef");
8146 i_indattnames = PQfnumber(res, "indattnames");
8147 i_tablespace = PQfnumber(res, "tablespace");
8148 i_indreloptions = PQfnumber(res, "indreloptions");
8149 i_indstatcols = PQfnumber(res, "indstatcols");
8150 i_indstatvals = PQfnumber(res, "indstatvals");
8151
8153
8154 /*
8155 * Outer loop iterates once per table, not once per row. Incrementing of
8156 * j is handled by the inner loop.
8157 */
8158 curtblindx = -1;
8159 for (int j = 0; j < ntups;)
8160 {
8163 char **indAttNames = NULL;
8164 int nindAttNames = 0;
8165 int numinds;
8166
8167 /* Count rows for this table */
8168 for (numinds = 1; numinds < ntups - j; numinds++)
8169 if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8170 break;
8171
8172 /*
8173 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8174 * order.
8175 */
8176 while (++curtblindx < numTables)
8177 {
8178 tbinfo = &tblinfo[curtblindx];
8179 if (tbinfo->dobj.catId.oid == indrelid)
8180 break;
8181 }
8182 if (curtblindx >= numTables)
8183 pg_fatal("unrecognized table OID %u", indrelid);
8184 /* cross-check that we only got requested tables */
8185 if (!tbinfo->hasindex ||
8186 !tbinfo->interesting)
8187 pg_fatal("unexpected index data for table \"%s\"",
8188 tbinfo->dobj.name);
8189
8190 /* Save data for this table */
8191 tbinfo->indexes = indxinfo + j;
8192 tbinfo->numIndexes = numinds;
8193
8194 for (int c = 0; c < numinds; c++, j++)
8195 {
8196 char contype;
8197 char indexkind;
8199 int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8200 int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8201 int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8202
8203 indxinfo[j].dobj.objType = DO_INDEX;
8204 indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8205 indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8206 AssignDumpId(&indxinfo[j].dobj);
8207 indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8208 indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8209 indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8210 indxinfo[j].indextable = tbinfo;
8211 indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8212 indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8213 indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8214 indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8215 indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8216 indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8217 indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8218 indxinfo[j].indkeys = pg_malloc_array(Oid, indxinfo[j].indnattrs);
8220 indxinfo[j].indkeys, indxinfo[j].indnattrs);
8221 indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8222 indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8223 indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8224 indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8225 indxinfo[j].partattaches = (SimplePtrList)
8226 {
8227 NULL, NULL
8228 };
8229
8230 if (indxinfo[j].parentidx == 0)
8232 else
8234
8235 if (!PQgetisnull(res, j, i_indattnames))
8236 {
8238 &indAttNames, &nindAttNames))
8239 pg_fatal("could not parse %s array", "indattnames");
8240 }
8241
8242 relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8243 PQgetvalue(res, j, i_reltuples),
8244 relallvisible, relallfrozen, indexkind,
8245 indAttNames, nindAttNames);
8246
8247 contype = *(PQgetvalue(res, j, i_contype));
8248 if (contype == 'p' || contype == 'u' || contype == 'x')
8249 {
8250 /*
8251 * If we found a constraint matching the index, create an
8252 * entry for it.
8253 */
8255
8257 constrinfo->dobj.objType = DO_CONSTRAINT;
8258 constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8259 constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8260 AssignDumpId(&constrinfo->dobj);
8261 constrinfo->dobj.dump = tbinfo->dobj.dump;
8262 constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8263 constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8264 constrinfo->contable = tbinfo;
8265 constrinfo->condomain = NULL;
8266 constrinfo->contype = contype;
8267 if (contype == 'x')
8268 constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8269 else
8270 constrinfo->condef = NULL;
8271 constrinfo->confrelid = InvalidOid;
8272 constrinfo->conindex = indxinfo[j].dobj.dumpId;
8273 constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8274 constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8275 constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8276 constrinfo->conislocal = true;
8277 constrinfo->separate = true;
8278
8279 indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8280 if (relstats != NULL)
8281 addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8282 }
8283 else
8284 {
8285 /* Plain secondary index */
8286 indxinfo[j].indexconstraint = 0;
8287 }
8288 }
8289 }
8290
8291 PQclear(res);
8292
8293 destroyPQExpBuffer(query);
8295}
8296
8297/*
8298 * getExtendedStatistics
8299 * get information about extended-statistics objects.
8300 *
8301 * Note: extended statistics data is not returned directly to the caller, but
8302 * it does get entered into the DumpableObject tables.
8303 */
8304void
8306{
8307 PQExpBuffer query;
8308 PGresult *res;
8310 int ntups;
8311 int i_tableoid;
8312 int i_oid;
8313 int i_stxname;
8314 int i_stxnamespace;
8315 int i_stxowner;
8316 int i_stxrelid;
8317 int i_stattarget;
8318 int i;
8319
8320 /* Extended statistics were new in v10 */
8321 if (fout->remoteVersion < 100000)
8322 return;
8323
8324 query = createPQExpBuffer();
8325
8326 if (fout->remoteVersion < 130000)
8327 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8328 "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8329 "FROM pg_catalog.pg_statistic_ext");
8330 else
8331 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8332 "stxnamespace, stxowner, stxrelid, stxstattarget "
8333 "FROM pg_catalog.pg_statistic_ext");
8334
8335 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8336
8337 ntups = PQntuples(res);
8338
8339 i_tableoid = PQfnumber(res, "tableoid");
8340 i_oid = PQfnumber(res, "oid");
8341 i_stxname = PQfnumber(res, "stxname");
8342 i_stxnamespace = PQfnumber(res, "stxnamespace");
8343 i_stxowner = PQfnumber(res, "stxowner");
8344 i_stxrelid = PQfnumber(res, "stxrelid");
8345 i_stattarget = PQfnumber(res, "stxstattarget");
8346
8348
8349 for (i = 0; i < ntups; i++)
8350 {
8351 statsextinfo[i].dobj.objType = DO_STATSEXT;
8352 statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8353 statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8354 AssignDumpId(&statsextinfo[i].dobj);
8355 statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8356 statsextinfo[i].dobj.namespace =
8358 statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8359 statsextinfo[i].stattable =
8361 if (PQgetisnull(res, i, i_stattarget))
8362 statsextinfo[i].stattarget = -1;
8363 else
8364 statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8365
8366 /* Decide whether we want to dump it */
8368
8369 if (fout->dopt->dumpStatistics)
8370 statsextinfo[i].dobj.components |= DUMP_COMPONENT_STATISTICS;
8371 }
8372
8373 PQclear(res);
8374 destroyPQExpBuffer(query);
8375}
8376
8377/*
8378 * getConstraints
8379 *
8380 * Get info about constraints on dumpable tables.
8381 *
8382 * Currently handles foreign keys only.
8383 * Unique and primary key constraints are handled with indexes,
8384 * while check constraints are processed in getTableAttrs().
8385 */
8386void
8388{
8391 PGresult *res;
8392 int ntups;
8393 int curtblindx;
8396 int i_contableoid,
8397 i_conoid,
8398 i_conrelid,
8399 i_conname,
8401 i_conindid,
8402 i_condef;
8403
8404 /*
8405 * We want to perform just one query against pg_constraint. However, we
8406 * mustn't try to select every row of the catalog and then sort it out on
8407 * the client side, because some of the server-side functions we need
8408 * would be unsafe to apply to tables we don't have lock on. Hence, we
8409 * build an array of the OIDs of tables we care about (and now have lock
8410 * on!), and use a WHERE clause to constrain which rows are selected.
8411 */
8413 for (int i = 0; i < numTables; i++)
8414 {
8415 TableInfo *tinfo = &tblinfo[i];
8416
8417 if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8418 continue;
8419
8420 /* OK, we need info for this table */
8421 if (tbloids->len > 1) /* do we have more than the '{'? */
8423 appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8424 }
8426
8428 "SELECT c.tableoid, c.oid, "
8429 "conrelid, conname, confrelid, ");
8430 if (fout->remoteVersion >= 110000)
8431 appendPQExpBufferStr(query, "conindid, ");
8432 else
8433 appendPQExpBufferStr(query, "0 AS conindid, ");
8434 appendPQExpBuffer(query,
8435 "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8436 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8437 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8438 "WHERE contype = 'f' ",
8439 tbloids->data);
8440 if (fout->remoteVersion >= 110000)
8442 "AND conparentid = 0 ");
8444 "ORDER BY conrelid, conname");
8445
8446 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8447
8448 ntups = PQntuples(res);
8449
8450 i_contableoid = PQfnumber(res, "tableoid");
8451 i_conoid = PQfnumber(res, "oid");
8452 i_conrelid = PQfnumber(res, "conrelid");
8453 i_conname = PQfnumber(res, "conname");
8454 i_confrelid = PQfnumber(res, "confrelid");
8455 i_conindid = PQfnumber(res, "conindid");
8456 i_condef = PQfnumber(res, "condef");
8457
8459
8460 curtblindx = -1;
8461 for (int j = 0; j < ntups; j++)
8462 {
8463 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8465
8466 /*
8467 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8468 * order.
8469 */
8470 if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8471 {
8472 while (++curtblindx < numTables)
8473 {
8474 tbinfo = &tblinfo[curtblindx];
8475 if (tbinfo->dobj.catId.oid == conrelid)
8476 break;
8477 }
8478 if (curtblindx >= numTables)
8479 pg_fatal("unrecognized table OID %u", conrelid);
8480 }
8481
8482 constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8483 constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8484 constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8485 AssignDumpId(&constrinfo[j].dobj);
8486 constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8487 constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8488 constrinfo[j].contable = tbinfo;
8489 constrinfo[j].condomain = NULL;
8490 constrinfo[j].contype = 'f';
8491 constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8492 constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8493 constrinfo[j].conindex = 0;
8494 constrinfo[j].condeferrable = false;
8495 constrinfo[j].condeferred = false;
8496 constrinfo[j].conislocal = true;
8497 constrinfo[j].separate = true;
8498
8499 /*
8500 * Restoring an FK that points to a partitioned table requires that
8501 * all partition indexes have been attached beforehand. Ensure that
8502 * happens by making the constraint depend on each index partition
8503 * attach object.
8504 */
8505 reftable = findTableByOid(constrinfo[j].confrelid);
8506 if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8507 {
8508 Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8509
8510 if (indexOid != InvalidOid)
8511 {
8512 for (int k = 0; k < reftable->numIndexes; k++)
8513 {
8515
8516 /* not our index? */
8517 if (reftable->indexes[k].dobj.catId.oid != indexOid)
8518 continue;
8519
8520 refidx = &reftable->indexes[k];
8522 break;
8523 }
8524 }
8525 }
8526 }
8527
8528 PQclear(res);
8529
8530 destroyPQExpBuffer(query);
8532}
8533
8534/*
8535 * addConstrChildIdxDeps
8536 *
8537 * Recursive subroutine for getConstraints
8538 *
8539 * Given an object representing a foreign key constraint and an index on the
8540 * partitioned table it references, mark the constraint object as dependent
8541 * on the DO_INDEX_ATTACH object of each index partition, recursively
8542 * drilling down to their partitions if any. This ensures that the FK is not
8543 * restored until the index is fully marked valid.
8544 */
8545static void
8547{
8548 SimplePtrListCell *cell;
8549
8551
8552 for (cell = refidx->partattaches.head; cell; cell = cell->next)
8553 {
8555
8556 addObjectDependency(dobj, attach->dobj.dumpId);
8557
8558 if (attach->partitionIdx->partattaches.head != NULL)
8559 addConstrChildIdxDeps(dobj, attach->partitionIdx);
8560 }
8561}
8562
8563/*
8564 * getDomainConstraints
8565 *
8566 * Get info about constraints on a domain.
8567 */
8568static void
8570{
8573 PGresult *res;
8574 int i_tableoid,
8575 i_oid,
8576 i_conname,
8577 i_consrc,
8579 i_contype;
8580 int ntups;
8581
8583 {
8584 /*
8585 * Set up query for constraint-specific details. For servers 17 and
8586 * up, domains have constraints of type 'n' as well as 'c', otherwise
8587 * just the latter.
8588 */
8589 appendPQExpBuffer(query,
8590 "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8591 "SELECT tableoid, oid, conname, "
8592 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8593 "convalidated, contype "
8594 "FROM pg_catalog.pg_constraint "
8595 "WHERE contypid = $1 AND contype IN (%s) "
8596 "ORDER BY conname",
8597 fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8598
8599 ExecuteSqlStatement(fout, query->data);
8600
8602 }
8603
8604 printfPQExpBuffer(query,
8605 "EXECUTE getDomainConstraints('%u')",
8606 tyinfo->dobj.catId.oid);
8607
8608 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8609
8610 ntups = PQntuples(res);
8611
8612 i_tableoid = PQfnumber(res, "tableoid");
8613 i_oid = PQfnumber(res, "oid");
8614 i_conname = PQfnumber(res, "conname");
8615 i_consrc = PQfnumber(res, "consrc");
8616 i_convalidated = PQfnumber(res, "convalidated");
8617 i_contype = PQfnumber(res, "contype");
8618
8620 tyinfo->domChecks = constrinfo;
8621
8622 /* 'i' tracks result rows; 'j' counts CHECK constraints */
8623 for (int i = 0, j = 0; i < ntups; i++)
8624 {
8625 bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8626 char contype = (PQgetvalue(res, i, i_contype))[0];
8627 ConstraintInfo *constraint;
8628
8629 if (contype == CONSTRAINT_CHECK)
8630 {
8631 constraint = &constrinfo[j++];
8632 tyinfo->nDomChecks++;
8633 }
8634 else
8635 {
8636 Assert(contype == CONSTRAINT_NOTNULL);
8637 Assert(tyinfo->notnull == NULL);
8638 /* use last item in array for the not-null constraint */
8639 tyinfo->notnull = &(constrinfo[ntups - 1]);
8640 constraint = tyinfo->notnull;
8641 }
8642
8643 constraint->dobj.objType = DO_CONSTRAINT;
8644 constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8645 constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8646 AssignDumpId(&(constraint->dobj));
8647 constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8648 constraint->dobj.namespace = tyinfo->dobj.namespace;
8649 constraint->contable = NULL;
8650 constraint->condomain = tyinfo;
8651 constraint->contype = contype;
8652 constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8653 constraint->confrelid = InvalidOid;
8654 constraint->conindex = 0;
8655 constraint->condeferrable = false;
8656 constraint->condeferred = false;
8657 constraint->conislocal = true;
8658
8659 constraint->separate = !validated;
8660
8661 /*
8662 * Make the domain depend on the constraint, ensuring it won't be
8663 * output till any constraint dependencies are OK. If the constraint
8664 * has not been validated, it's going to be dumped after the domain
8665 * anyway, so this doesn't matter.
8666 */
8667 if (validated)
8668 addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8669 }
8670
8671 PQclear(res);
8672
8673 destroyPQExpBuffer(query);
8674}
8675
8676/*
8677 * getRules
8678 * get basic information about every rule in the system
8679 */
8680void
8682{
8683 PGresult *res;
8684 int ntups;
8685 int i;
8688 int i_tableoid;
8689 int i_oid;
8690 int i_rulename;
8691 int i_ruletable;
8692 int i_ev_type;
8693 int i_is_instead;
8694 int i_ev_enabled;
8695
8696 appendPQExpBufferStr(query, "SELECT "
8697 "tableoid, oid, rulename, "
8698 "ev_class AS ruletable, ev_type, is_instead, "
8699 "ev_enabled "
8700 "FROM pg_rewrite "
8701 "ORDER BY oid");
8702
8703 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8704
8705 ntups = PQntuples(res);
8706
8708
8709 i_tableoid = PQfnumber(res, "tableoid");
8710 i_oid = PQfnumber(res, "oid");
8711 i_rulename = PQfnumber(res, "rulename");
8712 i_ruletable = PQfnumber(res, "ruletable");
8713 i_ev_type = PQfnumber(res, "ev_type");
8714 i_is_instead = PQfnumber(res, "is_instead");
8715 i_ev_enabled = PQfnumber(res, "ev_enabled");
8716
8717 for (i = 0; i < ntups; i++)
8718 {
8720
8721 ruleinfo[i].dobj.objType = DO_RULE;
8722 ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8723 ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8724 AssignDumpId(&ruleinfo[i].dobj);
8725 ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8727 ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8728 if (ruleinfo[i].ruletable == NULL)
8729 pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8730 ruletableoid, ruleinfo[i].dobj.catId.oid);
8731 ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8732 ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8733 ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8734 ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8735 ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8736 if (ruleinfo[i].ruletable)
8737 {
8738 /*
8739 * If the table is a view or materialized view, force its ON
8740 * SELECT rule to be sorted before the view itself --- this
8741 * ensures that any dependencies for the rule affect the table's
8742 * positioning. Other rules are forced to appear after their
8743 * table.
8744 */
8745 if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8746 ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8747 ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8748 {
8749 addObjectDependency(&ruleinfo[i].ruletable->dobj,
8750 ruleinfo[i].dobj.dumpId);
8751 /* We'll merge the rule into CREATE VIEW, if possible */
8752 ruleinfo[i].separate = false;
8753 }
8754 else
8755 {
8757 ruleinfo[i].ruletable->dobj.dumpId);
8758 ruleinfo[i].separate = true;
8759 }
8760 }
8761 else
8762 ruleinfo[i].separate = true;
8763 }
8764
8765 PQclear(res);
8766
8767 destroyPQExpBuffer(query);
8768}
8769
8770/*
8771 * getTriggers
8772 * get information about every trigger on a dumpable table
8773 *
8774 * Note: trigger data is not returned directly to the caller, but it
8775 * does get entered into the DumpableObject tables.
8776 */
8777void
8778getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8779{
8782 PGresult *res;
8783 int ntups;
8784 int curtblindx;
8786 int i_tableoid,
8787 i_oid,
8788 i_tgrelid,
8789 i_tgname,
8792 i_tgdef;
8793
8794 /*
8795 * We want to perform just one query against pg_trigger. However, we
8796 * mustn't try to select every row of the catalog and then sort it out on
8797 * the client side, because some of the server-side functions we need
8798 * would be unsafe to apply to tables we don't have lock on. Hence, we
8799 * build an array of the OIDs of tables we care about (and now have lock
8800 * on!), and use a WHERE clause to constrain which rows are selected.
8801 */
8803 for (int i = 0; i < numTables; i++)
8804 {
8805 TableInfo *tbinfo = &tblinfo[i];
8806
8807 if (!tbinfo->hastriggers ||
8808 !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8809 continue;
8810
8811 /* OK, we need info for this table */
8812 if (tbloids->len > 1) /* do we have more than the '{'? */
8814 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8815 }
8817
8818 if (fout->remoteVersion >= 150000)
8819 {
8820 /*
8821 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8822 * result in non-forward-compatible dumps of WHEN clauses due to
8823 * under-parenthesization.
8824 *
8825 * NB: We need to see partition triggers in case the tgenabled flag
8826 * has been changed from the parent.
8827 */
8828 appendPQExpBuffer(query,
8829 "SELECT t.tgrelid, t.tgname, "
8830 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8831 "t.tgenabled, t.tableoid, t.oid, "
8832 "t.tgparentid <> 0 AS tgispartition\n"
8833 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8834 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8835 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8836 "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8837 "OR t.tgenabled != u.tgenabled) "
8838 "ORDER BY t.tgrelid, t.tgname",
8839 tbloids->data);
8840 }
8841 else if (fout->remoteVersion >= 130000)
8842 {
8843 /*
8844 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8845 * result in non-forward-compatible dumps of WHEN clauses due to
8846 * under-parenthesization.
8847 *
8848 * NB: We need to see tgisinternal triggers in partitions, in case the
8849 * tgenabled flag has been changed from the parent.
8850 */
8851 appendPQExpBuffer(query,
8852 "SELECT t.tgrelid, t.tgname, "
8853 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8854 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8855 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8856 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8857 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8858 "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8859 "ORDER BY t.tgrelid, t.tgname",
8860 tbloids->data);
8861 }
8862 else if (fout->remoteVersion >= 110000)
8863 {
8864 /*
8865 * NB: We need to see tgisinternal triggers in partitions, in case the
8866 * tgenabled flag has been changed from the parent. No tgparentid in
8867 * version 11-12, so we have to match them via pg_depend.
8868 *
8869 * See above about pretty=true in pg_get_triggerdef.
8870 */
8871 appendPQExpBuffer(query,
8872 "SELECT t.tgrelid, t.tgname, "
8873 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8874 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8875 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8876 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8877 "LEFT JOIN pg_catalog.pg_depend AS d ON "
8878 " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8879 " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8880 " d.objid = t.oid "
8881 "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8882 "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8883 "ORDER BY t.tgrelid, t.tgname",
8884 tbloids->data);
8885 }
8886 else
8887 {
8888 /* See above about pretty=true in pg_get_triggerdef */
8889 appendPQExpBuffer(query,
8890 "SELECT t.tgrelid, t.tgname, "
8891 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8892 "t.tgenabled, false as tgispartition, "
8893 "t.tableoid, t.oid "
8894 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8895 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8896 "WHERE NOT tgisinternal "
8897 "ORDER BY t.tgrelid, t.tgname",
8898 tbloids->data);
8899 }
8900
8901 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8902
8903 ntups = PQntuples(res);
8904
8905 i_tableoid = PQfnumber(res, "tableoid");
8906 i_oid = PQfnumber(res, "oid");
8907 i_tgrelid = PQfnumber(res, "tgrelid");
8908 i_tgname = PQfnumber(res, "tgname");
8909 i_tgenabled = PQfnumber(res, "tgenabled");
8910 i_tgispartition = PQfnumber(res, "tgispartition");
8911 i_tgdef = PQfnumber(res, "tgdef");
8912
8914
8915 /*
8916 * Outer loop iterates once per table, not once per row. Incrementing of
8917 * j is handled by the inner loop.
8918 */
8919 curtblindx = -1;
8920 for (int j = 0; j < ntups;)
8921 {
8924 int numtrigs;
8925
8926 /* Count rows for this table */
8927 for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8928 if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8929 break;
8930
8931 /*
8932 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8933 * order.
8934 */
8935 while (++curtblindx < numTables)
8936 {
8937 tbinfo = &tblinfo[curtblindx];
8938 if (tbinfo->dobj.catId.oid == tgrelid)
8939 break;
8940 }
8941 if (curtblindx >= numTables)
8942 pg_fatal("unrecognized table OID %u", tgrelid);
8943
8944 /* Save data for this table */
8945 tbinfo->triggers = tginfo + j;
8946 tbinfo->numTriggers = numtrigs;
8947
8948 for (int c = 0; c < numtrigs; c++, j++)
8949 {
8950 tginfo[j].dobj.objType = DO_TRIGGER;
8951 tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8952 tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8953 AssignDumpId(&tginfo[j].dobj);
8954 tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8955 tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8956 tginfo[j].tgtable = tbinfo;
8957 tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8958 tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8959 tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8960 }
8961 }
8962
8963 PQclear(res);
8964
8965 destroyPQExpBuffer(query);
8967}
8968
8969/*
8970 * getEventTriggers
8971 * get information about event triggers
8972 */
8973void
8975{
8976 int i;
8977 PQExpBuffer query;
8978 PGresult *res;
8980 int i_tableoid,
8981 i_oid,
8982 i_evtname,
8983 i_evtevent,
8984 i_evtowner,
8985 i_evttags,
8986 i_evtfname,
8988 int ntups;
8989
8990 /* Before 9.3, there are no event triggers */
8991 if (fout->remoteVersion < 90300)
8992 return;
8993
8994 query = createPQExpBuffer();
8995
8997 "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8998 "evtevent, evtowner, "
8999 "array_to_string(array("
9000 "select quote_literal(x) "
9001 " from unnest(evttags) as t(x)), ', ') as evttags, "
9002 "e.evtfoid::regproc as evtfname "
9003 "FROM pg_event_trigger e "
9004 "ORDER BY e.oid");
9005
9006 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9007
9008 ntups = PQntuples(res);
9009
9011
9012 i_tableoid = PQfnumber(res, "tableoid");
9013 i_oid = PQfnumber(res, "oid");
9014 i_evtname = PQfnumber(res, "evtname");
9015 i_evtevent = PQfnumber(res, "evtevent");
9016 i_evtowner = PQfnumber(res, "evtowner");
9017 i_evttags = PQfnumber(res, "evttags");
9018 i_evtfname = PQfnumber(res, "evtfname");
9019 i_evtenabled = PQfnumber(res, "evtenabled");
9020
9021 for (i = 0; i < ntups; i++)
9022 {
9023 evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
9024 evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9025 evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9026 AssignDumpId(&evtinfo[i].dobj);
9027 evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
9028 evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
9029 evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
9030 evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
9031 evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
9032 evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
9033 evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
9034
9035 /* Decide whether we want to dump it */
9037 }
9038
9039 PQclear(res);
9040
9041 destroyPQExpBuffer(query);
9042}
9043
9044/*
9045 * getProcLangs
9046 * get basic information about every procedural language in the system
9047 *
9048 * NB: this must run after getFuncs() because we assume we can do
9049 * findFuncByOid().
9050 */
9051void
9053{
9054 PGresult *res;
9055 int ntups;
9056 int i;
9059 int i_tableoid;
9060 int i_oid;
9061 int i_lanname;
9062 int i_lanpltrusted;
9063 int i_lanplcallfoid;
9064 int i_laninline;
9065 int i_lanvalidator;
9066 int i_lanacl;
9067 int i_acldefault;
9068 int i_lanowner;
9069
9070 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9071 "lanname, lanpltrusted, lanplcallfoid, "
9072 "laninline, lanvalidator, "
9073 "lanacl, "
9074 "acldefault('l', lanowner) AS acldefault, "
9075 "lanowner "
9076 "FROM pg_language "
9077 "WHERE lanispl "
9078 "ORDER BY oid");
9079
9080 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9081
9082 ntups = PQntuples(res);
9083
9085
9086 i_tableoid = PQfnumber(res, "tableoid");
9087 i_oid = PQfnumber(res, "oid");
9088 i_lanname = PQfnumber(res, "lanname");
9089 i_lanpltrusted = PQfnumber(res, "lanpltrusted");
9090 i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
9091 i_laninline = PQfnumber(res, "laninline");
9092 i_lanvalidator = PQfnumber(res, "lanvalidator");
9093 i_lanacl = PQfnumber(res, "lanacl");
9094 i_acldefault = PQfnumber(res, "acldefault");
9095 i_lanowner = PQfnumber(res, "lanowner");
9096
9097 for (i = 0; i < ntups; i++)
9098 {
9099 planginfo[i].dobj.objType = DO_PROCLANG;
9100 planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9101 planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9102 AssignDumpId(&planginfo[i].dobj);
9103
9104 planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
9105 planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
9106 planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9107 planginfo[i].dacl.privtype = 0;
9108 planginfo[i].dacl.initprivs = NULL;
9109 planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
9110 planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
9111 planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
9112 planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
9113 planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
9114
9115 /* Decide whether we want to dump it */
9117
9118 /* Mark whether language has an ACL */
9119 if (!PQgetisnull(res, i, i_lanacl))
9120 planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9121 }
9122
9123 PQclear(res);
9124
9125 destroyPQExpBuffer(query);
9126}
9127
9128/*
9129 * getCasts
9130 * get basic information about most casts in the system
9131 *
9132 * Skip casts from a range to its multirange, since we'll create those
9133 * automatically.
9134 */
9135void
9137{
9138 PGresult *res;
9139 int ntups;
9140 int i;
9143 int i_tableoid;
9144 int i_oid;
9145 int i_castsource;
9146 int i_casttarget;
9147 int i_castfunc;
9148 int i_castcontext;
9149 int i_castmethod;
9150
9151 if (fout->remoteVersion >= 140000)
9152 {
9153 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9154 "castsource, casttarget, castfunc, castcontext, "
9155 "castmethod "
9156 "FROM pg_cast c "
9157 "WHERE NOT EXISTS ( "
9158 "SELECT 1 FROM pg_range r "
9159 "WHERE c.castsource = r.rngtypid "
9160 "AND c.casttarget = r.rngmultitypid "
9161 ") "
9162 "ORDER BY 3,4");
9163 }
9164 else
9165 {
9166 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9167 "castsource, casttarget, castfunc, castcontext, "
9168 "castmethod "
9169 "FROM pg_cast ORDER BY 3,4");
9170 }
9171
9172 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9173
9174 ntups = PQntuples(res);
9175
9177
9178 i_tableoid = PQfnumber(res, "tableoid");
9179 i_oid = PQfnumber(res, "oid");
9180 i_castsource = PQfnumber(res, "castsource");
9181 i_casttarget = PQfnumber(res, "casttarget");
9182 i_castfunc = PQfnumber(res, "castfunc");
9183 i_castcontext = PQfnumber(res, "castcontext");
9184 i_castmethod = PQfnumber(res, "castmethod");
9185
9186 for (i = 0; i < ntups; i++)
9187 {
9191
9193 castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9194 castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9195 AssignDumpId(&castinfo[i].dobj);
9196 castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9197 castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9198 castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9199 castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9200 castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9201
9202 /*
9203 * Try to name cast as concatenation of typnames. This is only used
9204 * for purposes of sorting. If we fail to find either type, the name
9205 * will be an empty string.
9206 */
9208 sTypeInfo = findTypeByOid(castinfo[i].castsource);
9209 tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9210 if (sTypeInfo && tTypeInfo)
9211 appendPQExpBuffer(&namebuf, "%s %s",
9212 sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9213 castinfo[i].dobj.name = namebuf.data;
9214
9215 /* Decide whether we want to dump it */
9217 }
9218
9219 PQclear(res);
9220
9221 destroyPQExpBuffer(query);
9222}
9223
9224static char *
9226{
9227 PQExpBuffer query;
9228 PGresult *res;
9229 char *lanname;
9230
9231 query = createPQExpBuffer();
9232 appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9233 res = ExecuteSqlQueryForSingleRow(fout, query->data);
9234 lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9235 destroyPQExpBuffer(query);
9236 PQclear(res);
9237
9238 return lanname;
9239}
9240
9241/*
9242 * getTransforms
9243 * get basic information about every transform in the system
9244 */
9245void
9247{
9248 PGresult *res;
9249 int ntups;
9250 int i;
9251 PQExpBuffer query;
9253 int i_tableoid;
9254 int i_oid;
9255 int i_trftype;
9256 int i_trflang;
9257 int i_trffromsql;
9258 int i_trftosql;
9259
9260 /* Transforms didn't exist pre-9.5 */
9261 if (fout->remoteVersion < 90500)
9262 return;
9263
9264 query = createPQExpBuffer();
9265
9266 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9267 "trftype, trflang, trffromsql::oid, trftosql::oid "
9268 "FROM pg_transform "
9269 "ORDER BY 3,4");
9270
9271 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9272
9273 ntups = PQntuples(res);
9274
9276
9277 i_tableoid = PQfnumber(res, "tableoid");
9278 i_oid = PQfnumber(res, "oid");
9279 i_trftype = PQfnumber(res, "trftype");
9280 i_trflang = PQfnumber(res, "trflang");
9281 i_trffromsql = PQfnumber(res, "trffromsql");
9282 i_trftosql = PQfnumber(res, "trftosql");
9283
9284 for (i = 0; i < ntups; i++)
9285 {
9288 char *lanname;
9289
9291 transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9292 transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9294 transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9295 transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9296 transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9297 transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9298
9299 /*
9300 * Try to name transform as concatenation of type and language name.
9301 * This is only used for purposes of sorting. If we fail to find
9302 * either, the name will be an empty string.
9303 */
9307 if (typeInfo && lanname)
9308 appendPQExpBuffer(&namebuf, "%s %s",
9309 typeInfo->dobj.name, lanname);
9310 transforminfo[i].dobj.name = namebuf.data;
9311 free(lanname);
9312
9313 /* Decide whether we want to dump it */
9315 }
9316
9317 PQclear(res);
9318
9319 destroyPQExpBuffer(query);
9320}
9321
9322/*
9323 * getTableAttrs -
9324 * for each interesting table, read info about its attributes
9325 * (names, types, default values, CHECK constraints, etc)
9326 *
9327 * modifies tblinfo
9328 */
9329void
9331{
9332 DumpOptions *dopt = fout->dopt;
9337 PGresult *res;
9338 int ntups;
9339 int curtblindx;
9340 int i_attrelid;
9341 int i_attnum;
9342 int i_attname;
9343 int i_atttypname;
9344 int i_attstattarget;
9345 int i_attstorage;
9346 int i_typstorage;
9347 int i_attidentity;
9348 int i_attgenerated;
9349 int i_attisdropped;
9350 int i_attlen;
9351 int i_attalign;
9352 int i_attislocal;
9353 int i_notnull_name;
9358 int i_attoptions;
9359 int i_attcollation;
9360 int i_attcompression;
9361 int i_attfdwoptions;
9362 int i_attmissingval;
9363 int i_atthasdef;
9364
9365 /*
9366 * We want to perform just one query against pg_attribute, and then just
9367 * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9368 * (for CHECK constraints and for NOT NULL constraints). However, we
9369 * mustn't try to select every row of those catalogs and then sort it out
9370 * on the client side, because some of the server-side functions we need
9371 * would be unsafe to apply to tables we don't have lock on. Hence, we
9372 * build an array of the OIDs of tables we care about (and now have lock
9373 * on!), and use a WHERE clause to constrain which rows are selected.
9374 */
9377 for (int i = 0; i < numTables; i++)
9378 {
9379 TableInfo *tbinfo = &tblinfo[i];
9380
9381 /* Don't bother to collect info for sequences */
9382 if (tbinfo->relkind == RELKIND_SEQUENCE)
9383 continue;
9384
9385 /*
9386 * Don't bother with uninteresting tables, either. For binary
9387 * upgrades, this is bypassed for pg_largeobject_metadata and
9388 * pg_shdepend so that the columns names are collected for the
9389 * corresponding COPY commands. Restoring the data for those catalogs
9390 * is faster than restoring the equivalent set of large object
9391 * commands.
9392 */
9393 if (!tbinfo->interesting &&
9394 !(fout->dopt->binary_upgrade &&
9395 (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9396 tbinfo->dobj.catId.oid == SharedDependRelationId)))
9397 continue;
9398
9399 /* OK, we need info for this table */
9400 if (tbloids->len > 1) /* do we have more than the '{'? */
9402 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9403
9404 if (tbinfo->ncheck > 0)
9405 {
9406 /* Also make a list of the ones with check constraints */
9407 if (checkoids->len > 1) /* do we have more than the '{'? */
9409 appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9410 }
9411 }
9414
9415 /*
9416 * Find all the user attributes and their types.
9417 *
9418 * Since we only want to dump COLLATE clauses for attributes whose
9419 * collation is different from their type's default, we use a CASE here to
9420 * suppress uninteresting attcollations cheaply.
9421 */
9423 "SELECT\n"
9424 "a.attrelid,\n"
9425 "a.attnum,\n"
9426 "a.attname,\n"
9427 "a.attstattarget,\n"
9428 "a.attstorage,\n"
9429 "t.typstorage,\n"
9430 "a.atthasdef,\n"
9431 "a.attisdropped,\n"
9432 "a.attlen,\n"
9433 "a.attalign,\n"
9434 "a.attislocal,\n"
9435 "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9436 "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9437 "CASE WHEN a.attcollation <> t.typcollation "
9438 "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9439 "pg_catalog.array_to_string(ARRAY("
9440 "SELECT pg_catalog.quote_ident(option_name) || "
9441 "' ' || pg_catalog.quote_literal(option_value) "
9442 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9443 "ORDER BY option_name"
9444 "), E',\n ') AS attfdwoptions,\n");
9445
9446 /*
9447 * Find out any NOT NULL markings for each column. In 18 and up we read
9448 * pg_constraint to obtain the constraint name, and for valid constraints
9449 * also pg_description to obtain its comment. notnull_noinherit is set
9450 * according to the NO INHERIT property. For versions prior to 18, we
9451 * store an empty string as the name when a constraint is marked as
9452 * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9453 * without a name); also, such cases are never NO INHERIT.
9454 *
9455 * For invalid constraints, we need to store their OIDs for processing
9456 * elsewhere, so we bring the pg_constraint.oid value when the constraint
9457 * is invalid, and NULL otherwise. Their comments are handled not here
9458 * but by collectComments, because they're their own dumpable object.
9459 *
9460 * We track in notnull_islocal whether the constraint was defined directly
9461 * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9462 * might modify this later.
9463 */
9464 if (fout->remoteVersion >= 180000)
9466 "co.conname AS notnull_name,\n"
9467 "CASE WHEN co.convalidated THEN pt.description"
9468 " ELSE NULL END AS notnull_comment,\n"
9469 "CASE WHEN NOT co.convalidated THEN co.oid "
9470 "ELSE NULL END AS notnull_invalidoid,\n"
9471 "co.connoinherit AS notnull_noinherit,\n"
9472 "co.conislocal AS notnull_islocal,\n");
9473 else
9475 "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9476 "NULL AS notnull_comment,\n"
9477 "NULL AS notnull_invalidoid,\n"
9478 "false AS notnull_noinherit,\n"
9479 "CASE WHEN a.attislocal THEN true\n"
9480 " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
9481 " ELSE false\n"
9482 "END AS notnull_islocal,\n");
9483
9484 if (fout->remoteVersion >= 140000)
9486 "a.attcompression AS attcompression,\n");
9487 else
9489 "'' AS attcompression,\n");
9490
9491 if (fout->remoteVersion >= 100000)
9493 "a.attidentity,\n");
9494 else
9496 "'' AS attidentity,\n");
9497
9498 if (fout->remoteVersion >= 110000)
9500 "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9501 "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9502 else
9504 "NULL AS attmissingval,\n");
9505
9506 if (fout->remoteVersion >= 120000)
9508 "a.attgenerated\n");
9509 else
9511 "'' AS attgenerated\n");
9512
9513 /* need left join to pg_type to not fail on dropped columns ... */
9515 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9516 "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9517 "LEFT JOIN pg_catalog.pg_type t "
9518 "ON (a.atttypid = t.oid)\n",
9519 tbloids->data);
9520
9521 /*
9522 * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9523 * entries and pg_description to get their comments.
9524 */
9525 if (fout->remoteVersion >= 180000)
9527 " LEFT JOIN pg_catalog.pg_constraint co ON "
9528 "(a.attrelid = co.conrelid\n"
9529 " AND co.contype = 'n' AND "
9530 "co.conkey = array[a.attnum])\n"
9531 " LEFT JOIN pg_catalog.pg_description pt ON "
9532 "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9533
9535 "WHERE a.attnum > 0::pg_catalog.int2\n");
9536
9537 /*
9538 * For binary upgrades from <v12, be sure to pick up
9539 * pg_largeobject_metadata's oid column.
9540 */
9541 if (fout->dopt->binary_upgrade && fout->remoteVersion < 120000)
9543 "OR (a.attnum = -2::pg_catalog.int2 AND src.tbloid = "
9545
9547 "ORDER BY a.attrelid, a.attnum");
9548
9550
9551 ntups = PQntuples(res);
9552
9553 i_attrelid = PQfnumber(res, "attrelid");
9554 i_attnum = PQfnumber(res, "attnum");
9555 i_attname = PQfnumber(res, "attname");
9556 i_atttypname = PQfnumber(res, "atttypname");
9557 i_attstattarget = PQfnumber(res, "attstattarget");
9558 i_attstorage = PQfnumber(res, "attstorage");
9559 i_typstorage = PQfnumber(res, "typstorage");
9560 i_attidentity = PQfnumber(res, "attidentity");
9561 i_attgenerated = PQfnumber(res, "attgenerated");
9562 i_attisdropped = PQfnumber(res, "attisdropped");
9563 i_attlen = PQfnumber(res, "attlen");
9564 i_attalign = PQfnumber(res, "attalign");
9565 i_attislocal = PQfnumber(res, "attislocal");
9566 i_notnull_name = PQfnumber(res, "notnull_name");
9567 i_notnull_comment = PQfnumber(res, "notnull_comment");
9568 i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9569 i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9570 i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9571 i_attoptions = PQfnumber(res, "attoptions");
9572 i_attcollation = PQfnumber(res, "attcollation");
9573 i_attcompression = PQfnumber(res, "attcompression");
9574 i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9575 i_attmissingval = PQfnumber(res, "attmissingval");
9576 i_atthasdef = PQfnumber(res, "atthasdef");
9577
9578 /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9581
9582 /*
9583 * Outer loop iterates once per table, not once per row. Incrementing of
9584 * r is handled by the inner loop.
9585 */
9586 curtblindx = -1;
9587 for (int r = 0; r < ntups;)
9588 {
9589 Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9591 int numatts;
9592 bool hasdefaults;
9593
9594 /* Count rows for this table */
9595 for (numatts = 1; numatts < ntups - r; numatts++)
9596 if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9597 break;
9598
9599 /*
9600 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9601 * order.
9602 */
9603 while (++curtblindx < numTables)
9604 {
9605 tbinfo = &tblinfo[curtblindx];
9606 if (tbinfo->dobj.catId.oid == attrelid)
9607 break;
9608 }
9609 if (curtblindx >= numTables)
9610 pg_fatal("unrecognized table OID %u", attrelid);
9611 /* cross-check that we only got requested tables */
9612 if (tbinfo->relkind == RELKIND_SEQUENCE ||
9613 (!tbinfo->interesting &&
9614 !(fout->dopt->binary_upgrade &&
9615 (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9616 tbinfo->dobj.catId.oid == SharedDependRelationId))))
9617 pg_fatal("unexpected column data for table \"%s\"",
9618 tbinfo->dobj.name);
9619
9620 /* Save data for this table */
9621 tbinfo->numatts = numatts;
9622 tbinfo->attnames = pg_malloc_array(char *, numatts);
9623 tbinfo->atttypnames = pg_malloc_array(char *, numatts);
9624 tbinfo->attstattarget = pg_malloc_array(int, numatts);
9625 tbinfo->attstorage = pg_malloc_array(char, numatts);
9626 tbinfo->typstorage = pg_malloc_array(char, numatts);
9627 tbinfo->attidentity = pg_malloc_array(char, numatts);
9628 tbinfo->attgenerated = pg_malloc_array(char, numatts);
9629 tbinfo->attisdropped = pg_malloc_array(bool, numatts);
9630 tbinfo->attlen = pg_malloc_array(int, numatts);
9631 tbinfo->attalign = pg_malloc_array(char, numatts);
9632 tbinfo->attislocal = pg_malloc_array(bool, numatts);
9633 tbinfo->attoptions = pg_malloc_array(char *, numatts);
9634 tbinfo->attcollation = pg_malloc_array(Oid, numatts);
9635 tbinfo->attcompression = pg_malloc_array(char, numatts);
9636 tbinfo->attfdwoptions = pg_malloc_array(char *, numatts);
9637 tbinfo->attmissingval = pg_malloc_array(char *, numatts);
9638 tbinfo->notnull_constrs = pg_malloc_array(char *, numatts);
9639 tbinfo->notnull_comment = pg_malloc_array(char *, numatts);
9640 tbinfo->notnull_invalid = pg_malloc_array(bool, numatts);
9641 tbinfo->notnull_noinh = pg_malloc_array(bool, numatts);
9642 tbinfo->notnull_islocal = pg_malloc_array(bool, numatts);
9643 tbinfo->attrdefs = pg_malloc_array(AttrDefInfo *, numatts);
9644 hasdefaults = false;
9645
9646 for (int j = 0; j < numatts; j++, r++)
9647 {
9648 if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)) &&
9649 !(fout->dopt->binary_upgrade && fout->remoteVersion < 120000 &&
9650 tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId))
9651 pg_fatal("invalid column numbering in table \"%s\"",
9652 tbinfo->dobj.name);
9653 tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9654 tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9655 if (PQgetisnull(res, r, i_attstattarget))
9656 tbinfo->attstattarget[j] = -1;
9657 else
9658 tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9659 tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9660 tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9661 tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9662 tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9663 tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9664 tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9665 tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9666 tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9667 tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9668
9669 /* Handle not-null constraint name and flags */
9671 tbinfo, j,
9678
9679 tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9681 tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9682 tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9683 tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9684 tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9685 tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9686 tbinfo->attrdefs[j] = NULL; /* fix below */
9687 if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9688 hasdefaults = true;
9689 }
9690
9691 if (hasdefaults)
9692 {
9693 /* Collect OIDs of interesting tables that have defaults */
9694 if (tbloids->len > 1) /* do we have more than the '{'? */
9696 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9697 }
9698 }
9699
9700 /* If invalidnotnulloids has any data, finalize it */
9701 if (invalidnotnulloids != NULL)
9703
9704 PQclear(res);
9705
9706 /*
9707 * Now get info about column defaults. This is skipped for a data-only
9708 * dump, as it is only needed for table schemas.
9709 */
9710 if (dopt->dumpSchema && tbloids->len > 1)
9711 {
9712 AttrDefInfo *attrdefs;
9713 int numDefaults;
9715
9716 pg_log_info("finding table default expressions");
9717
9719
9720 printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9721 "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9722 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9723 "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9724 "ORDER BY a.adrelid, a.adnum",
9725 tbloids->data);
9726
9728
9729 numDefaults = PQntuples(res);
9731
9732 curtblindx = -1;
9733 for (int j = 0; j < numDefaults; j++)
9734 {
9735 Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9736 Oid adoid = atooid(PQgetvalue(res, j, 1));
9737 Oid adrelid = atooid(PQgetvalue(res, j, 2));
9738 int adnum = atoi(PQgetvalue(res, j, 3));
9739 char *adsrc = PQgetvalue(res, j, 4);
9740
9741 /*
9742 * Locate the associated TableInfo; we rely on tblinfo[] being in
9743 * OID order.
9744 */
9745 if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9746 {
9747 while (++curtblindx < numTables)
9748 {
9749 tbinfo = &tblinfo[curtblindx];
9750 if (tbinfo->dobj.catId.oid == adrelid)
9751 break;
9752 }
9753 if (curtblindx >= numTables)
9754 pg_fatal("unrecognized table OID %u", adrelid);
9755 }
9756
9757 if (adnum <= 0 || adnum > tbinfo->numatts)
9758 pg_fatal("invalid adnum value %d for table \"%s\"",
9759 adnum, tbinfo->dobj.name);
9760
9761 /*
9762 * dropped columns shouldn't have defaults, but just in case,
9763 * ignore 'em
9764 */
9765 if (tbinfo->attisdropped[adnum - 1])
9766 continue;
9767
9768 attrdefs[j].dobj.objType = DO_ATTRDEF;
9769 attrdefs[j].dobj.catId.tableoid = adtableoid;
9770 attrdefs[j].dobj.catId.oid = adoid;
9771 AssignDumpId(&attrdefs[j].dobj);
9772 attrdefs[j].adtable = tbinfo;
9773 attrdefs[j].adnum = adnum;
9774 attrdefs[j].adef_expr = pg_strdup(adsrc);
9775
9776 attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9777 attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9778
9779 attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9780
9781 /*
9782 * Figure out whether the default/generation expression should be
9783 * dumped as part of the main CREATE TABLE (or similar) command or
9784 * as a separate ALTER TABLE (or similar) command. The preference
9785 * is to put it into the CREATE command, but in some cases that's
9786 * not possible.
9787 */
9788 if (tbinfo->attgenerated[adnum - 1])
9789 {
9790 /*
9791 * Column generation expressions cannot be dumped separately,
9792 * because there is no syntax for it. By setting separate to
9793 * false here we prevent the "default" from being processed as
9794 * its own dumpable object. Later, flagInhAttrs() will mark
9795 * it as not to be dumped at all, if possible (that is, if it
9796 * can be inherited from a parent).
9797 */
9798 attrdefs[j].separate = false;
9799 }
9800 else if (tbinfo->relkind == RELKIND_VIEW)
9801 {
9802 /*
9803 * Defaults on a VIEW must always be dumped as separate ALTER
9804 * TABLE commands.
9805 */
9806 attrdefs[j].separate = true;
9807 }
9808 else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9809 {
9810 /* column will be suppressed, print default separately */
9811 attrdefs[j].separate = true;
9812 }
9813 else
9814 {
9815 attrdefs[j].separate = false;
9816 }
9817
9818 if (!attrdefs[j].separate)
9819 {
9820 /*
9821 * Mark the default as needing to appear before the table, so
9822 * that any dependencies it has must be emitted before the
9823 * CREATE TABLE. If this is not possible, we'll change to
9824 * "separate" mode while sorting dependencies.
9825 */
9827 attrdefs[j].dobj.dumpId);
9828 }
9829
9830 tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9831 }
9832
9833 PQclear(res);
9834 }
9835
9836 /*
9837 * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9838 * data-only dump, as it is only needed for table schemas.
9839 */
9840 if (dopt->dumpSchema && invalidnotnulloids)
9841 {
9843 int numConstrs;
9844 int i_tableoid;
9845 int i_oid;
9846 int i_conrelid;
9847 int i_conname;
9848 int i_consrc;
9849 int i_conislocal;
9850
9851 pg_log_info("finding invalid not-null constraints");
9852
9855 "SELECT c.tableoid, c.oid, conrelid, conname, "
9856 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9857 "conislocal, convalidated "
9858 "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9859 "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9860 "ORDER BY c.conrelid, c.conname",
9861 invalidnotnulloids->data);
9862
9864
9865 numConstrs = PQntuples(res);
9867
9868 i_tableoid = PQfnumber(res, "tableoid");
9869 i_oid = PQfnumber(res, "oid");
9870 i_conrelid = PQfnumber(res, "conrelid");
9871 i_conname = PQfnumber(res, "conname");
9872 i_consrc = PQfnumber(res, "consrc");
9873 i_conislocal = PQfnumber(res, "conislocal");
9874
9875 /* As above, this loop iterates once per table, not once per row */
9876 curtblindx = -1;
9877 for (int j = 0; j < numConstrs;)
9878 {
9879 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9881 int numcons;
9882
9883 /* Count rows for this table */
9884 for (numcons = 1; numcons < numConstrs - j; numcons++)
9885 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9886 break;
9887
9888 /*
9889 * Locate the associated TableInfo; we rely on tblinfo[] being in
9890 * OID order.
9891 */
9892 while (++curtblindx < numTables)
9893 {
9894 tbinfo = &tblinfo[curtblindx];
9895 if (tbinfo->dobj.catId.oid == conrelid)
9896 break;
9897 }
9898 if (curtblindx >= numTables)
9899 pg_fatal("unrecognized table OID %u", conrelid);
9900
9901 for (int c = 0; c < numcons; c++, j++)
9902 {
9903 constrs[j].dobj.objType = DO_CONSTRAINT;
9904 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9905 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9906 AssignDumpId(&constrs[j].dobj);
9907 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9908 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9909 constrs[j].contable = tbinfo;
9910 constrs[j].condomain = NULL;
9911 constrs[j].contype = 'n';
9912 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9913 constrs[j].confrelid = InvalidOid;
9914 constrs[j].conindex = 0;
9915 constrs[j].condeferrable = false;
9916 constrs[j].condeferred = false;
9917 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9918
9919 /*
9920 * All invalid not-null constraints must be dumped separately,
9921 * because CREATE TABLE would not create them as invalid, and
9922 * also because they must be created after potentially
9923 * violating data has been loaded.
9924 */
9925 constrs[j].separate = true;
9926
9927 constrs[j].dobj.dump = tbinfo->dobj.dump;
9928 }
9929 }
9930 PQclear(res);
9931 }
9932
9933 /*
9934 * Get info about table CHECK constraints. This is skipped for a
9935 * data-only dump, as it is only needed for table schemas.
9936 */
9937 if (dopt->dumpSchema && checkoids->len > 2)
9938 {
9940 int numConstrs;
9941 int i_tableoid;
9942 int i_oid;
9943 int i_conrelid;
9944 int i_conname;
9945 int i_consrc;
9946 int i_conislocal;
9947 int i_convalidated;
9948
9949 pg_log_info("finding table check constraints");
9950
9953 "SELECT c.tableoid, c.oid, conrelid, conname, "
9954 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9955 "conislocal, convalidated "
9956 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9957 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9958 "WHERE contype = 'c' "
9959 "ORDER BY c.conrelid, c.conname",
9960 checkoids->data);
9961
9963
9964 numConstrs = PQntuples(res);
9966
9967 i_tableoid = PQfnumber(res, "tableoid");
9968 i_oid = PQfnumber(res, "oid");
9969 i_conrelid = PQfnumber(res, "conrelid");
9970 i_conname = PQfnumber(res, "conname");
9971 i_consrc = PQfnumber(res, "consrc");
9972 i_conislocal = PQfnumber(res, "conislocal");
9973 i_convalidated = PQfnumber(res, "convalidated");
9974
9975 /* As above, this loop iterates once per table, not once per row */
9976 curtblindx = -1;
9977 for (int j = 0; j < numConstrs;)
9978 {
9979 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9981 int numcons;
9982
9983 /* Count rows for this table */
9984 for (numcons = 1; numcons < numConstrs - j; numcons++)
9985 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9986 break;
9987
9988 /*
9989 * Locate the associated TableInfo; we rely on tblinfo[] being in
9990 * OID order.
9991 */
9992 while (++curtblindx < numTables)
9993 {
9994 tbinfo = &tblinfo[curtblindx];
9995 if (tbinfo->dobj.catId.oid == conrelid)
9996 break;
9997 }
9998 if (curtblindx >= numTables)
9999 pg_fatal("unrecognized table OID %u", conrelid);
10000
10001 if (numcons != tbinfo->ncheck)
10002 {
10003 pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
10004 "expected %d check constraints on table \"%s\" but found %d",
10005 tbinfo->ncheck),
10006 tbinfo->ncheck, tbinfo->dobj.name, numcons);
10007 pg_log_error_hint("The system catalogs might be corrupted.");
10008 exit_nicely(1);
10009 }
10010
10011 tbinfo->checkexprs = constrs + j;
10012
10013 for (int c = 0; c < numcons; c++, j++)
10014 {
10015 bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
10016
10017 constrs[j].dobj.objType = DO_CONSTRAINT;
10018 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
10019 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
10020 AssignDumpId(&constrs[j].dobj);
10021 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
10022 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
10023 constrs[j].contable = tbinfo;
10024 constrs[j].condomain = NULL;
10025 constrs[j].contype = 'c';
10026 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
10027 constrs[j].confrelid = InvalidOid;
10028 constrs[j].conindex = 0;
10029 constrs[j].condeferrable = false;
10030 constrs[j].condeferred = false;
10031 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
10032
10033 /*
10034 * An unvalidated constraint needs to be dumped separately, so
10035 * that potentially-violating existing data is loaded before
10036 * the constraint.
10037 */
10038 constrs[j].separate = !validated;
10039
10040 constrs[j].dobj.dump = tbinfo->dobj.dump;
10041
10042 /*
10043 * Mark the constraint as needing to appear before the table
10044 * --- this is so that any other dependencies of the
10045 * constraint will be emitted before we try to create the
10046 * table. If the constraint is to be dumped separately, it
10047 * will be dumped after data is loaded anyway, so don't do it.
10048 * (There's an automatic dependency in the opposite direction
10049 * anyway, so don't need to add one manually here.)
10050 */
10051 if (!constrs[j].separate)
10053 constrs[j].dobj.dumpId);
10054
10055 /*
10056 * We will detect later whether the constraint must be split
10057 * out from the table definition.
10058 */
10059 }
10060 }
10061
10062 PQclear(res);
10063 }
10064
10068}
10069
10070/*
10071 * Based on the getTableAttrs query's row corresponding to one column, set
10072 * the name and flags to handle a not-null constraint for that column in
10073 * the tbinfo struct.
10074 *
10075 * Result row 'r' is for tbinfo's attribute 'j'.
10076 *
10077 * There are four possibilities:
10078 * 1) the column has no not-null constraints. In that case, ->notnull_constrs
10079 * (the constraint name) remains NULL.
10080 * 2) The column has a constraint with no name (this is the case when
10081 * constraints come from pre-18 servers). In this case, ->notnull_constrs
10082 * is set to the empty string; dumpTableSchema will print just "NOT NULL".
10083 * 3) The column has an invalid not-null constraint. This must be treated
10084 * as a separate object (because it must be created after the table data
10085 * is loaded). So we add its OID to invalidnotnulloids for processing
10086 * elsewhere and do nothing further with it here. We distinguish this
10087 * case because the "notnull_invalidoid" column has been set to a non-NULL
10088 * value, which is the constraint OID. Valid constraints have a null OID.
10089 * 4) The column has a constraint with a known name; in that case
10090 * notnull_constrs carries that name and dumpTableSchema will print
10091 * "CONSTRAINT the_name NOT NULL". However, if the name is the default
10092 * (table_column_not_null) and there's no comment on the constraint,
10093 * there's no need to print that name in the dump, so notnull_constrs
10094 * is set to the empty string and it behaves as case 2.
10095 *
10096 * In a child table that inherits from a parent already containing NOT NULL
10097 * constraints and the columns in the child don't have their own NOT NULL
10098 * declarations, we suppress printing constraints in the child: the
10099 * constraints are acquired at the point where the child is attached to the
10100 * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
10101 * set not here but in flagInhAttrs. That flag is also used when the
10102 * constraint was validated in a child but all its parent have it as NOT
10103 * VALID.
10104 *
10105 * Any of these constraints might have the NO INHERIT bit. If so we set
10106 * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
10107 *
10108 * In case 4 above, the name comparison is a bit of a hack; it actually fails
10109 * to do the right thing in all but the trivial case. However, the downside
10110 * of getting it wrong is simply that the name is printed rather than
10111 * suppressed, so it's not a big deal.
10112 *
10113 * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
10114 * constraints are found, it is initialized and filled with the array of
10115 * OIDs of such constraints, for later processing.
10116 */
10117static void
10119 TableInfo *tbinfo, int j,
10120 int i_notnull_name,
10126{
10127 DumpOptions *dopt = fout->dopt;
10128
10129 /*
10130 * If this not-null constraint is not valid, list its OID in
10131 * invalidnotnulloids and do nothing further. It'll be processed
10132 * elsewhere later.
10133 *
10134 * Because invalid not-null constraints are rare, we don't want to malloc
10135 * invalidnotnulloids until we're sure we're going it need it, which
10136 * happens here.
10137 */
10138 if (!PQgetisnull(res, r, i_notnull_invalidoid))
10139 {
10140 char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10141
10142 if (*invalidnotnulloids == NULL)
10143 {
10147 }
10148 else
10150
10151 /*
10152 * Track when a parent constraint is invalid for the cases where a
10153 * child constraint has been validated independenly.
10154 */
10155 tbinfo->notnull_invalid[j] = true;
10156
10157 /* nothing else to do */
10158 tbinfo->notnull_constrs[j] = NULL;
10159 return;
10160 }
10161
10162 /*
10163 * notnull_noinh is straight from the query result. notnull_islocal also,
10164 * though flagInhAttrs may change that one later.
10165 */
10166 tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10167 tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10168 tbinfo->notnull_invalid[j] = false;
10169
10170 /*
10171 * Determine a constraint name to use. If the column is not marked not-
10172 * null, we set NULL which cues ... to do nothing. An empty string says
10173 * to print an unnamed NOT NULL, and anything else is a constraint name to
10174 * use.
10175 */
10176 if (fout->remoteVersion < 180000)
10177 {
10178 /*
10179 * < 18 doesn't have not-null names, so an unnamed constraint is
10180 * sufficient.
10181 */
10182 if (PQgetisnull(res, r, i_notnull_name))
10183 tbinfo->notnull_constrs[j] = NULL;
10184 else
10185 tbinfo->notnull_constrs[j] = "";
10186 }
10187 else
10188 {
10189 if (PQgetisnull(res, r, i_notnull_name))
10190 tbinfo->notnull_constrs[j] = NULL;
10191 else
10192 {
10193 /*
10194 * In binary upgrade of inheritance child tables, must have a
10195 * constraint name that we can UPDATE later; same if there's a
10196 * comment on the constraint.
10197 */
10198 if ((dopt->binary_upgrade &&
10199 !tbinfo->ispartition &&
10200 !tbinfo->notnull_islocal[j]) ||
10202 {
10203 tbinfo->notnull_constrs[j] =
10205 }
10206 else
10207 {
10208 char *default_name;
10209
10210 /* XXX should match ChooseConstraintName better */
10211 default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10212 tbinfo->attnames[j]);
10213 if (strcmp(default_name,
10214 PQgetvalue(res, r, i_notnull_name)) == 0)
10215 tbinfo->notnull_constrs[j] = "";
10216 else
10217 {
10218 tbinfo->notnull_constrs[j] =
10220 }
10222 }
10223 }
10224 }
10225}
10226
10227/*
10228 * Test whether a column should be printed as part of table's CREATE TABLE.
10229 * Column number is zero-based.
10230 *
10231 * Normally this is always true, but it's false for dropped columns, as well
10232 * as those that were inherited without any local definition. (If we print
10233 * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10234 * For partitions, it's always true, because we want the partitions to be
10235 * created independently and ATTACH PARTITION used afterwards.
10236 *
10237 * In binary_upgrade mode, we must print all columns and fix the attislocal/
10238 * attisdropped state later, so as to keep control of the physical column
10239 * order.
10240 *
10241 * This function exists because there are scattered nonobvious places that
10242 * must be kept in sync with this decision.
10243 */
10244bool
10245shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10246{
10247 if (dopt->binary_upgrade)
10248 return true;
10249 if (tbinfo->attisdropped[colno])
10250 return false;
10251 return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10252}
10253
10254
10255/*
10256 * getTSParsers:
10257 * get information about all text search parsers in the system catalogs
10258 */
10259void
10261{
10262 PGresult *res;
10263 int ntups;
10264 int i;
10265 PQExpBuffer query;
10267 int i_tableoid;
10268 int i_oid;
10269 int i_prsname;
10270 int i_prsnamespace;
10271 int i_prsstart;
10272 int i_prstoken;
10273 int i_prsend;
10274 int i_prsheadline;
10275 int i_prslextype;
10276
10277 query = createPQExpBuffer();
10278
10279 /*
10280 * find all text search objects, including builtin ones; we filter out
10281 * system-defined objects at dump-out time.
10282 */
10283
10284 appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10285 "prsstart::oid, prstoken::oid, "
10286 "prsend::oid, prsheadline::oid, prslextype::oid "
10287 "FROM pg_ts_parser");
10288
10289 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10290
10291 ntups = PQntuples(res);
10292
10294
10295 i_tableoid = PQfnumber(res, "tableoid");
10296 i_oid = PQfnumber(res, "oid");
10297 i_prsname = PQfnumber(res, "prsname");
10298 i_prsnamespace = PQfnumber(res, "prsnamespace");
10299 i_prsstart = PQfnumber(res, "prsstart");
10300 i_prstoken = PQfnumber(res, "prstoken");
10301 i_prsend = PQfnumber(res, "prsend");
10302 i_prsheadline = PQfnumber(res, "prsheadline");
10303 i_prslextype = PQfnumber(res, "prslextype");
10304
10305 for (i = 0; i < ntups; i++)
10306 {
10307 prsinfo[i].dobj.objType = DO_TSPARSER;
10308 prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10309 prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10310 AssignDumpId(&prsinfo[i].dobj);
10311 prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10312 prsinfo[i].dobj.namespace =
10314 prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10315 prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10316 prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10317 prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10318 prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10319
10320 /* Decide whether we want to dump it */
10322 }
10323
10324 PQclear(res);
10325
10326 destroyPQExpBuffer(query);
10327}
10328
10329/*
10330 * getTSDictionaries:
10331 * get information about all text search dictionaries in the system catalogs
10332 */
10333void
10335{
10336 PGresult *res;
10337 int ntups;
10338 int i;
10339 PQExpBuffer query;
10341 int i_tableoid;
10342 int i_oid;
10343 int i_dictname;
10344 int i_dictnamespace;
10345 int i_dictowner;
10346 int i_dicttemplate;
10347 int i_dictinitoption;
10348
10349 query = createPQExpBuffer();
10350
10351 appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10352 "dictnamespace, dictowner, "
10353 "dicttemplate, dictinitoption "
10354 "FROM pg_ts_dict");
10355
10356 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10357
10358 ntups = PQntuples(res);
10359
10361
10362 i_tableoid = PQfnumber(res, "tableoid");
10363 i_oid = PQfnumber(res, "oid");
10364 i_dictname = PQfnumber(res, "dictname");
10365 i_dictnamespace = PQfnumber(res, "dictnamespace");
10366 i_dictowner = PQfnumber(res, "dictowner");
10367 i_dictinitoption = PQfnumber(res, "dictinitoption");
10368 i_dicttemplate = PQfnumber(res, "dicttemplate");
10369
10370 for (i = 0; i < ntups; i++)
10371 {
10372 dictinfo[i].dobj.objType = DO_TSDICT;
10373 dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10374 dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10375 AssignDumpId(&dictinfo[i].dobj);
10376 dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10377 dictinfo[i].dobj.namespace =
10379 dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10380 dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10381 if (PQgetisnull(res, i, i_dictinitoption))
10382 dictinfo[i].dictinitoption = NULL;
10383 else
10384 dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10385
10386 /* Decide whether we want to dump it */
10388 }
10389
10390 PQclear(res);
10391
10392 destroyPQExpBuffer(query);
10393}
10394
10395/*
10396 * getTSTemplates:
10397 * get information about all text search templates in the system catalogs
10398 */
10399void
10401{
10402 PGresult *res;
10403 int ntups;
10404 int i;
10405 PQExpBuffer query;
10407 int i_tableoid;
10408 int i_oid;
10409 int i_tmplname;
10410 int i_tmplnamespace;
10411 int i_tmplinit;
10412 int i_tmpllexize;
10413
10414 query = createPQExpBuffer();
10415
10416 appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10417 "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10418 "FROM pg_ts_template");
10419
10420 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10421
10422 ntups = PQntuples(res);
10423
10425
10426 i_tableoid = PQfnumber(res, "tableoid");
10427 i_oid = PQfnumber(res, "oid");
10428 i_tmplname = PQfnumber(res, "tmplname");
10429 i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10430 i_tmplinit = PQfnumber(res, "tmplinit");
10431 i_tmpllexize = PQfnumber(res, "tmpllexize");
10432
10433 for (i = 0; i < ntups; i++)
10434 {
10435 tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10436 tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10437 tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10438 AssignDumpId(&tmplinfo[i].dobj);
10439 tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10440 tmplinfo[i].dobj.namespace =
10442 tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10443 tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10444
10445 /* Decide whether we want to dump it */
10447 }
10448
10449 PQclear(res);
10450
10451 destroyPQExpBuffer(query);
10452}
10453
10454/*
10455 * getTSConfigurations:
10456 * get information about all text search configurations
10457 */
10458void
10460{
10461 PGresult *res;
10462 int ntups;
10463 int i;
10464 PQExpBuffer query;
10466 int i_tableoid;
10467 int i_oid;
10468 int i_cfgname;
10469 int i_cfgnamespace;
10470 int i_cfgowner;
10471 int i_cfgparser;
10472
10473 query = createPQExpBuffer();
10474
10475 appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10476 "cfgnamespace, cfgowner, cfgparser "
10477 "FROM pg_ts_config");
10478
10479 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10480
10481 ntups = PQntuples(res);
10482
10484
10485 i_tableoid = PQfnumber(res, "tableoid");
10486 i_oid = PQfnumber(res, "oid");
10487 i_cfgname = PQfnumber(res, "cfgname");
10488 i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10489 i_cfgowner = PQfnumber(res, "cfgowner");
10490 i_cfgparser = PQfnumber(res, "cfgparser");
10491
10492 for (i = 0; i < ntups; i++)
10493 {
10494 cfginfo[i].dobj.objType = DO_TSCONFIG;
10495 cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10496 cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10497 AssignDumpId(&cfginfo[i].dobj);
10498 cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10499 cfginfo[i].dobj.namespace =
10501 cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10502 cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10503
10504 /* Decide whether we want to dump it */
10506 }
10507
10508 PQclear(res);
10509
10510 destroyPQExpBuffer(query);
10511}
10512
10513/*
10514 * getForeignDataWrappers:
10515 * get information about all foreign-data wrappers in the system catalogs
10516 */
10517void
10519{
10520 PGresult *res;
10521 int ntups;
10522 int i;
10523 PQExpBuffer query;
10525 int i_tableoid;
10526 int i_oid;
10527 int i_fdwname;
10528 int i_fdwowner;
10529 int i_fdwhandler;
10530 int i_fdwvalidator;
10531 int i_fdwconnection;
10532 int i_fdwacl;
10533 int i_acldefault;
10534 int i_fdwoptions;
10535
10536 query = createPQExpBuffer();
10537
10538 appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10539 "fdwowner, "
10540 "fdwhandler::pg_catalog.regproc, "
10541 "fdwvalidator::pg_catalog.regproc, ");
10542
10543 if (fout->remoteVersion >= 190000)
10544 appendPQExpBufferStr(query, "fdwconnection::pg_catalog.regproc, ");
10545 else
10546 appendPQExpBufferStr(query, "'-' AS fdwconnection, ");
10547
10549 "fdwacl, "
10550 "acldefault('F', fdwowner) AS acldefault, "
10551 "array_to_string(ARRAY("
10552 "SELECT quote_ident(option_name) || ' ' || "
10553 "quote_literal(option_value) "
10554 "FROM pg_options_to_table(fdwoptions) "
10555 "ORDER BY option_name"
10556 "), E',\n ') AS fdwoptions "
10557 "FROM pg_foreign_data_wrapper");
10558
10559 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10560
10561 ntups = PQntuples(res);
10562
10564
10565 i_tableoid = PQfnumber(res, "tableoid");
10566 i_oid = PQfnumber(res, "oid");
10567 i_fdwname = PQfnumber(res, "fdwname");
10568 i_fdwowner = PQfnumber(res, "fdwowner");
10569 i_fdwhandler = PQfnumber(res, "fdwhandler");
10570 i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10571 i_fdwconnection = PQfnumber(res, "fdwconnection");
10572 i_fdwacl = PQfnumber(res, "fdwacl");
10573 i_acldefault = PQfnumber(res, "acldefault");
10574 i_fdwoptions = PQfnumber(res, "fdwoptions");
10575
10576 for (i = 0; i < ntups; i++)
10577 {
10578 fdwinfo[i].dobj.objType = DO_FDW;
10579 fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10580 fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10581 AssignDumpId(&fdwinfo[i].dobj);
10582 fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10583 fdwinfo[i].dobj.namespace = NULL;
10584 fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10585 fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10586 fdwinfo[i].dacl.privtype = 0;
10587 fdwinfo[i].dacl.initprivs = NULL;
10588 fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10589 fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10590 fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10591 fdwinfo[i].fdwconnection = pg_strdup(PQgetvalue(res, i, i_fdwconnection));
10592 fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10593
10594 /* Decide whether we want to dump it */
10596
10597 /* Mark whether FDW has an ACL */
10598 if (!PQgetisnull(res, i, i_fdwacl))
10599 fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10600 }
10601
10602 PQclear(res);
10603
10604 destroyPQExpBuffer(query);
10605}
10606
10607/*
10608 * getForeignServers:
10609 * get information about all foreign servers in the system catalogs
10610 */
10611void
10613{
10614 PGresult *res;
10615 int ntups;
10616 int i;
10617 PQExpBuffer query;
10619 int i_tableoid;
10620 int i_oid;
10621 int i_srvname;
10622 int i_srvowner;
10623 int i_srvfdw;
10624 int i_srvtype;
10625 int i_srvversion;
10626 int i_srvacl;
10627 int i_acldefault;
10628 int i_srvoptions;
10629
10630 query = createPQExpBuffer();
10631
10632 appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10633 "srvowner, "
10634 "srvfdw, srvtype, srvversion, srvacl, "
10635 "acldefault('S', srvowner) AS acldefault, "
10636 "array_to_string(ARRAY("
10637 "SELECT quote_ident(option_name) || ' ' || "
10638 "quote_literal(option_value) "
10639 "FROM pg_options_to_table(srvoptions) "
10640 "ORDER BY option_name"
10641 "), E',\n ') AS srvoptions "
10642 "FROM pg_foreign_server");
10643
10644 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10645
10646 ntups = PQntuples(res);
10647
10649
10650 i_tableoid = PQfnumber(res, "tableoid");
10651 i_oid = PQfnumber(res, "oid");
10652 i_srvname = PQfnumber(res, "srvname");
10653 i_srvowner = PQfnumber(res, "srvowner");
10654 i_srvfdw = PQfnumber(res, "srvfdw");
10655 i_srvtype = PQfnumber(res, "srvtype");
10656 i_srvversion = PQfnumber(res, "srvversion");
10657 i_srvacl = PQfnumber(res, "srvacl");
10658 i_acldefault = PQfnumber(res, "acldefault");
10659 i_srvoptions = PQfnumber(res, "srvoptions");
10660
10661 for (i = 0; i < ntups; i++)
10662 {
10663 srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10664 srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10665 srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10666 AssignDumpId(&srvinfo[i].dobj);
10667 srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10668 srvinfo[i].dobj.namespace = NULL;
10669 srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10670 srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10671 srvinfo[i].dacl.privtype = 0;
10672 srvinfo[i].dacl.initprivs = NULL;
10673 srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10674 srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10675 srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10676 srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10677 srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10678
10679 /* Decide whether we want to dump it */
10681
10682 /* Servers have user mappings */
10683 srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10684
10685 /* Mark whether server has an ACL */
10686 if (!PQgetisnull(res, i, i_srvacl))
10687 srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10688 }
10689
10690 PQclear(res);
10691
10692 destroyPQExpBuffer(query);
10693}
10694
10695/*
10696 * getDefaultACLs:
10697 * get information about all default ACL information in the system catalogs
10698 */
10699void
10701{
10702 DumpOptions *dopt = fout->dopt;
10704 PQExpBuffer query;
10705 PGresult *res;
10706 int i_oid;
10707 int i_tableoid;
10708 int i_defaclrole;
10710 int i_defaclobjtype;
10711 int i_defaclacl;
10712 int i_acldefault;
10713 int i,
10714 ntups;
10715
10716 query = createPQExpBuffer();
10717
10718 /*
10719 * Global entries (with defaclnamespace=0) replace the hard-wired default
10720 * ACL for their object type. We should dump them as deltas from the
10721 * default ACL, since that will be used as a starting point for
10722 * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10723 * non-global entries can only add privileges not revoke them. We must
10724 * dump those as-is (i.e., as deltas from an empty ACL).
10725 *
10726 * We can use defaclobjtype as the object type for acldefault(), except
10727 * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10728 * 's'.
10729 */
10731 "SELECT oid, tableoid, "
10732 "defaclrole, "
10733 "defaclnamespace, "
10734 "defaclobjtype, "
10735 "defaclacl, "
10736 "CASE WHEN defaclnamespace = 0 THEN "
10737 "acldefault(CASE WHEN defaclobjtype = 'S' "
10738 "THEN 's'::\"char\" ELSE defaclobjtype END, "
10739 "defaclrole) ELSE '{}' END AS acldefault "
10740 "FROM pg_default_acl");
10741
10742 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10743
10744 ntups = PQntuples(res);
10745
10747
10748 i_oid = PQfnumber(res, "oid");
10749 i_tableoid = PQfnumber(res, "tableoid");
10750 i_defaclrole = PQfnumber(res, "defaclrole");
10751 i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10752 i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10753 i_defaclacl = PQfnumber(res, "defaclacl");
10754 i_acldefault = PQfnumber(res, "acldefault");
10755
10756 for (i = 0; i < ntups; i++)
10757 {
10759
10760 daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10761 daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10762 daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10763 AssignDumpId(&daclinfo[i].dobj);
10764 /* cheesy ... is it worth coming up with a better object name? */
10765 daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10766
10767 if (nspid != InvalidOid)
10768 daclinfo[i].dobj.namespace = findNamespace(nspid);
10769 else
10770 daclinfo[i].dobj.namespace = NULL;
10771
10772 daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10773 daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10774 daclinfo[i].dacl.privtype = 0;
10775 daclinfo[i].dacl.initprivs = NULL;
10776 daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10777 daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10778
10779 /* Default ACLs are ACLs, of course */
10780 daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10781
10782 /* Decide whether we want to dump it */
10784 }
10785
10786 PQclear(res);
10787
10788 destroyPQExpBuffer(query);
10789}
10790
10791/*
10792 * getRoleName -- look up the name of a role, given its OID
10793 *
10794 * In current usage, we don't expect failures, so error out for a bad OID.
10795 */
10796static const char *
10797getRoleName(const char *roleoid_str)
10798{
10799 Oid roleoid = atooid(roleoid_str);
10800
10801 /*
10802 * Do binary search to find the appropriate item.
10803 */
10804 if (nrolenames > 0)
10805 {
10806 RoleNameItem *low = &rolenames[0];
10807 RoleNameItem *high = &rolenames[nrolenames - 1];
10808
10809 while (low <= high)
10810 {
10811 RoleNameItem *middle = low + (high - low) / 2;
10812
10813 if (roleoid < middle->roleoid)
10814 high = middle - 1;
10815 else if (roleoid > middle->roleoid)
10816 low = middle + 1;
10817 else
10818 return middle->rolename; /* found a match */
10819 }
10820 }
10821
10822 pg_fatal("role with OID %u does not exist", roleoid);
10823 return NULL; /* keep compiler quiet */
10824}
10825
10826/*
10827 * collectRoleNames --
10828 *
10829 * Construct a table of all known roles.
10830 * The table is sorted by OID for speed in lookup.
10831 */
10832static void
10834{
10835 PGresult *res;
10836 const char *query;
10837 int i;
10838
10839 query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10840
10841 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10842
10843 nrolenames = PQntuples(res);
10844
10846
10847 for (i = 0; i < nrolenames; i++)
10848 {
10849 rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10851 }
10852
10853 PQclear(res);
10854}
10855
10856/*
10857 * getAdditionalACLs
10858 *
10859 * We have now created all the DumpableObjects, and collected the ACL data
10860 * that appears in the directly-associated catalog entries. However, there's
10861 * more ACL-related info to collect. If any of a table's columns have ACLs,
10862 * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10863 * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10864 * Also, in versions having the pg_init_privs catalog, read that and load the
10865 * information into the relevant DumpableObjects.
10866 */
10867static void
10869{
10871 PGresult *res;
10872 int ntups,
10873 i;
10874
10875 /* Check for per-column ACLs */
10877 "SELECT DISTINCT attrelid FROM pg_attribute "
10878 "WHERE attacl IS NOT NULL");
10879
10880 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10881
10882 ntups = PQntuples(res);
10883 for (i = 0; i < ntups; i++)
10884 {
10885 Oid relid = atooid(PQgetvalue(res, i, 0));
10886 TableInfo *tblinfo;
10887
10888 tblinfo = findTableByOid(relid);
10889 /* OK to ignore tables we haven't got a DumpableObject for */
10890 if (tblinfo)
10891 {
10893 tblinfo->hascolumnACLs = true;
10894 }
10895 }
10896 PQclear(res);
10897
10898 /* Fetch initial-privileges data */
10899 if (fout->remoteVersion >= 90600)
10900 {
10901 printfPQExpBuffer(query,
10902 "SELECT objoid, classoid, objsubid, privtype, initprivs "
10903 "FROM pg_init_privs");
10904
10905 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10906
10907 ntups = PQntuples(res);
10908 for (i = 0; i < ntups; i++)
10909 {
10910 Oid objoid = atooid(PQgetvalue(res, i, 0));
10911 Oid classoid = atooid(PQgetvalue(res, i, 1));
10912 int objsubid = atoi(PQgetvalue(res, i, 2));
10913 char privtype = *(PQgetvalue(res, i, 3));
10914 char *initprivs = PQgetvalue(res, i, 4);
10915 CatalogId objId;
10916 DumpableObject *dobj;
10917
10918 objId.tableoid = classoid;
10919 objId.oid = objoid;
10920 dobj = findObjectByCatalogId(objId);
10921 /* OK to ignore entries we haven't got a DumpableObject for */
10922 if (dobj)
10923 {
10924 /* Cope with sub-object initprivs */
10925 if (objsubid != 0)
10926 {
10927 if (dobj->objType == DO_TABLE)
10928 {
10929 /* For a column initprivs, set the table's ACL flags */
10931 ((TableInfo *) dobj)->hascolumnACLs = true;
10932 }
10933 else
10934 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10935 classoid, objoid, objsubid);
10936 continue;
10937 }
10938
10939 /*
10940 * We ignore any pg_init_privs.initprivs entry for the public
10941 * schema, as explained in getNamespaces().
10942 */
10943 if (dobj->objType == DO_NAMESPACE &&
10944 strcmp(dobj->name, "public") == 0)
10945 continue;
10946
10947 /* Else it had better be of a type we think has ACLs */
10948 if (dobj->objType == DO_NAMESPACE ||
10949 dobj->objType == DO_TYPE ||
10950 dobj->objType == DO_FUNC ||
10951 dobj->objType == DO_AGG ||
10952 dobj->objType == DO_TABLE ||
10953 dobj->objType == DO_PROCLANG ||
10954 dobj->objType == DO_FDW ||
10955 dobj->objType == DO_FOREIGN_SERVER)
10956 {
10958
10959 daobj->dacl.privtype = privtype;
10960 daobj->dacl.initprivs = pstrdup(initprivs);
10961 }
10962 else
10963 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10964 classoid, objoid, objsubid);
10965 }
10966 }
10967 PQclear(res);
10968 }
10969
10970 destroyPQExpBuffer(query);
10971}
10972
10973/*
10974 * dumpCommentExtended --
10975 *
10976 * This routine is used to dump any comments associated with the
10977 * object handed to this routine. The routine takes the object type
10978 * and object name (ready to print, except for schema decoration), plus
10979 * the namespace and owner of the object (for labeling the ArchiveEntry),
10980 * plus catalog ID and subid which are the lookup key for pg_description,
10981 * plus the dump ID for the object (for setting a dependency).
10982 * If a matching pg_description entry is found, it is dumped.
10983 *
10984 * Note: in some cases, such as comments for triggers and rules, the "type"
10985 * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10986 * but it doesn't seem worth complicating the API for all callers to make
10987 * it cleaner.
10988 *
10989 * Note: although this routine takes a dumpId for dependency purposes,
10990 * that purpose is just to mark the dependency in the emitted dump file
10991 * for possible future use by pg_restore. We do NOT use it for determining
10992 * ordering of the comment in the dump file, because this routine is called
10993 * after dependency sorting occurs. This routine should be called just after
10994 * calling ArchiveEntry() for the specified object.
10995 */
10996static void
10997dumpCommentExtended(Archive *fout, const char *type,
10998 const char *name, const char *namespace,
10999 const char *owner, CatalogId catalogId,
11000 int subid, DumpId dumpId,
11001 const char *initdb_comment)
11002{
11003 DumpOptions *dopt = fout->dopt;
11005 int ncomments;
11006
11007 /* do nothing, if --no-comments is supplied */
11008 if (dopt->no_comments)
11009 return;
11010
11011 /* Comments are schema not data ... except LO comments are data */
11012 if (strcmp(type, "LARGE OBJECT") != 0)
11013 {
11014 if (!dopt->dumpSchema)
11015 return;
11016 }
11017 else
11018 {
11019 /* We do dump LO comments in binary-upgrade mode */
11020 if (!dopt->dumpData && !dopt->binary_upgrade)
11021 return;
11022 }
11023
11024 /* Search for comments associated with catalogId, using table */
11025 ncomments = findComments(catalogId.tableoid, catalogId.oid,
11026 &comments);
11027
11028 /* Is there one matching the subid? */
11029 while (ncomments > 0)
11030 {
11031 if (comments->objsubid == subid)
11032 break;
11033 comments++;
11034 ncomments--;
11035 }
11036
11037 if (initdb_comment != NULL)
11038 {
11039 static CommentItem empty_comment = {.descr = ""};
11040
11041 /*
11042 * initdb creates this object with a comment. Skip dumping the
11043 * initdb-provided comment, which would complicate matters for
11044 * non-superuser use of pg_dump. When the DBA has removed initdb's
11045 * comment, replicate that.
11046 */
11047 if (ncomments == 0)
11048 {
11050 ncomments = 1;
11051 }
11052 else if (strcmp(comments->descr, initdb_comment) == 0)
11053 ncomments = 0;
11054 }
11055
11056 /* If a comment exists, build COMMENT ON statement */
11057 if (ncomments > 0)
11058 {
11061
11062 appendPQExpBuffer(query, "COMMENT ON %s ", type);
11063 if (namespace && *namespace)
11064 appendPQExpBuffer(query, "%s.", fmtId(namespace));
11065 appendPQExpBuffer(query, "%s IS ", name);
11067 appendPQExpBufferStr(query, ";\n");
11068
11069 appendPQExpBuffer(tag, "%s %s", type, name);
11070
11071 /*
11072 * We mark comments as SECTION_NONE because they really belong in the
11073 * same section as their parent, whether that is pre-data or
11074 * post-data.
11075 */
11077 ARCHIVE_OPTS(.tag = tag->data,
11078 .namespace = namespace,
11079 .owner = owner,
11080 .description = "COMMENT",
11081 .section = SECTION_NONE,
11082 .createStmt = query->data,
11083 .deps = &dumpId,
11084 .nDeps = 1));
11085
11086 destroyPQExpBuffer(query);
11087 destroyPQExpBuffer(tag);
11088 }
11089}
11090
11091/*
11092 * dumpComment --
11093 *
11094 * Typical simplification of the above function.
11095 */
11096static inline void
11097dumpComment(Archive *fout, const char *type,
11098 const char *name, const char *namespace,
11099 const char *owner, CatalogId catalogId,
11100 int subid, DumpId dumpId)
11101{
11102 dumpCommentExtended(fout, type, name, namespace, owner,
11103 catalogId, subid, dumpId, NULL);
11104}
11105
11106/*
11107 * appendNamedArgument --
11108 *
11109 * Convenience routine for constructing parameters of the form:
11110 * 'paraname', 'value'::type
11111 */
11112static void
11113appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
11114 const char *argtype, const char *argval)
11115{
11116 appendPQExpBufferStr(out, ",\n\t");
11117
11118 appendStringLiteralAH(out, argname, fout);
11119 appendPQExpBufferStr(out, ", ");
11120
11122 appendPQExpBuffer(out, "::%s", argtype);
11123}
11124
11125/*
11126 * fetchAttributeStats --
11127 *
11128 * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
11129 */
11130static PGresult *
11132{
11134 PQExpBuffer relids = createPQExpBuffer();
11137 int count = 0;
11138 PGresult *res = NULL;
11139 static TocEntry *te;
11140 static bool restarted;
11142
11143 /*
11144 * Our query for retrieving statistics for multiple relations uses WITH
11145 * ORDINALITY and multi-argument UNNEST(), both of which were introduced
11146 * in v9.4. For older versions, we resort to gathering statistics for a
11147 * single relation at a time.
11148 */
11149 if (fout->remoteVersion < 90400)
11150 max_rels = 1;
11151
11152 /* If we're just starting, set our TOC pointer. */
11153 if (!te)
11154 te = AH->toc->next;
11155
11156 /*
11157 * We can't easily avoid a second TOC scan for the tar format because it
11158 * writes restore.sql separately, which means we must execute the queries
11159 * twice. This feels risky, but there is no known reason it should
11160 * generate different output than the first pass. Even if it does, the
11161 * worst-case scenario is that restore.sql might have different statistics
11162 * data than the archive.
11163 */
11164 if (!restarted && te == AH->toc && AH->format == archTar)
11165 {
11166 te = AH->toc->next;
11167 restarted = true;
11168 }
11169
11170 appendPQExpBufferChar(relids, '{');
11173
11174 /*
11175 * Scan the TOC for the next set of relevant stats entries. We assume
11176 * that statistics are dumped in the order they are listed in the TOC.
11177 * This is perhaps not the sturdiest assumption, so we verify it matches
11178 * reality in dumpRelationStats_dumper().
11179 */
11180 for (; te != AH->toc && count < max_rels; te = te->next)
11181 {
11182 if ((te->reqs & REQ_STATS) == 0 ||
11183 strcmp(te->desc, "STATISTICS DATA") != 0)
11184 continue;
11185
11186 if (fout->remoteVersion >= 190000)
11187 {
11188 const RelStatsInfo *rsinfo = (const RelStatsInfo *) te->defnDumperArg;
11189 char relid[32];
11190
11191 sprintf(relid, "%u", rsinfo->relid);
11192 appendPGArray(relids, relid);
11193 }
11194 else
11195 {
11196 appendPGArray(nspnames, te->namespace);
11198 }
11199
11200 count++;
11201 }
11202
11203 appendPQExpBufferChar(relids, '}');
11206
11207 /* Execute the query for the next batch of relations. */
11208 if (count > 0)
11209 {
11211
11212 appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11213
11214 if (fout->remoteVersion >= 190000)
11215 {
11216 appendStringLiteralAH(query, relids->data, fout);
11217 appendPQExpBufferStr(query, "::pg_catalog.oid[])");
11218 }
11219 else
11220 {
11221 appendStringLiteralAH(query, nspnames->data, fout);
11222 appendPQExpBufferStr(query, "::pg_catalog.name[],");
11223 appendStringLiteralAH(query, relnames->data, fout);
11224 appendPQExpBufferStr(query, "::pg_catalog.name[])");
11225 }
11226
11227 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11228 destroyPQExpBuffer(query);
11229 }
11230
11231 destroyPQExpBuffer(relids);
11234 return res;
11235}
11236
11237/*
11238 * dumpRelationStats_dumper --
11239 *
11240 * Generate command to import stats into the relation on the new database.
11241 * This routine is called by the Archiver when it wants the statistics to be
11242 * dumped.
11243 */
11244static char *
11245dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11246{
11247 const RelStatsInfo *rsinfo = userArg;
11248 static PGresult *res;
11249 static int rownum;
11250 PQExpBuffer query;
11252 PQExpBuffer out = &out_data;
11253 int i_schemaname;
11254 int i_tablename;
11255 int i_attname;
11256 int i_inherited;
11257 int i_null_frac;
11258 int i_avg_width;
11259 int i_n_distinct;
11263 int i_correlation;
11270 static TocEntry *expected_te;
11271
11272 /*
11273 * fetchAttributeStats() assumes that the statistics are dumped in the
11274 * order they are listed in the TOC. We verify that here for safety.
11275 */
11276 if (!expected_te)
11277 expected_te = ((ArchiveHandle *) fout)->toc;
11278
11280 while ((expected_te->reqs & REQ_STATS) == 0 ||
11281 strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11283
11284 if (te != expected_te)
11285 pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11286 te->dumpId, te->desc, te->tag,
11287 expected_te->dumpId, expected_te->desc, expected_te->tag);
11288
11289 query = createPQExpBuffer();
11291 {
11292 if (fout->remoteVersion >= 190000)
11294 "PREPARE getAttributeStats(pg_catalog.oid[]) AS\n");
11295 else
11297 "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n");
11298
11300 "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11301 "s.null_frac, s.avg_width, s.n_distinct, "
11302 "s.most_common_vals, s.most_common_freqs, "
11303 "s.histogram_bounds, s.correlation, "
11304 "s.most_common_elems, s.most_common_elem_freqs, "
11305 "s.elem_count_histogram, ");
11306
11307 if (fout->remoteVersion >= 170000)
11309 "s.range_length_histogram, "
11310 "s.range_empty_frac, "
11311 "s.range_bounds_histogram ");
11312 else
11314 "NULL AS range_length_histogram,"
11315 "NULL AS range_empty_frac,"
11316 "NULL AS range_bounds_histogram ");
11317
11318 /*
11319 * The results must be in the order of the relations supplied in the
11320 * parameters to ensure we remain in sync as we walk through the TOC.
11321 *
11322 * For v9.4 through v18, the redundant filter clause on s.tablename =
11323 * ANY(...) seems sufficient to convince the planner to use
11324 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11325 * In newer versions, pg_stats returns the table OIDs, eliminating the
11326 * need for that hack.
11327 *
11328 * Our query for retrieving statistics for multiple relations uses
11329 * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11330 * introduced in v9.4. For older versions, we resort to gathering
11331 * statistics for a single relation at a time.
11332 */
11333 if (fout->remoteVersion >= 190000)
11335 "FROM pg_catalog.pg_stats s "
11336 "JOIN unnest($1) WITH ORDINALITY AS u (tableid, ord) "
11337 "ON s.tableid = u.tableid "
11338 "ORDER BY u.ord, s.attname, s.inherited");
11339 else if (fout->remoteVersion >= 90400)
11341 "FROM pg_catalog.pg_stats s "
11342 "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11343 "ON s.schemaname = u.schemaname "
11344 "AND s.tablename = u.tablename "
11345 "WHERE s.tablename = ANY($2) "
11346 "ORDER BY u.ord, s.attname, s.inherited");
11347 else
11349 "FROM pg_catalog.pg_stats s "
11350 "WHERE s.schemaname = $1[1] "
11351 "AND s.tablename = $2[1] "
11352 "ORDER BY s.attname, s.inherited");
11353
11354 ExecuteSqlStatement(fout, query->data);
11355
11357 resetPQExpBuffer(query);
11358 }
11359
11360 initPQExpBuffer(out);
11361
11362 /* restore relation stats */
11363 appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11364 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11366 appendPQExpBufferStr(out, "\t'schemaname', ");
11367 appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11368 appendPQExpBufferStr(out, ",\n");
11369 appendPQExpBufferStr(out, "\t'relname', ");
11370 appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11371 appendPQExpBufferStr(out, ",\n");
11372 appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11373
11374 /*
11375 * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11376 * the relation is empty, or it could mean that it hadn't yet been
11377 * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11378 * This ambiguity allegedly can cause the planner to choose inefficient
11379 * plans after restoring to v18 or newer. To deal with this, let's just
11380 * set reltuples to -1 in that case.
11381 */
11382 if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11383 appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11384 else
11385 appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11386
11387 appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11388 rsinfo->relallvisible);
11389
11390 if (fout->remoteVersion >= 180000)
11391 appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11392
11393 appendPQExpBufferStr(out, "\n);\n");
11394
11395 /* Fetch the next batch of attribute statistics if needed. */
11396 if (rownum >= PQntuples(res))
11397 {
11398 PQclear(res);
11400 rownum = 0;
11401 }
11402
11403 i_schemaname = PQfnumber(res, "schemaname");
11404 i_tablename = PQfnumber(res, "tablename");
11405 i_attname = PQfnumber(res, "attname");
11406 i_inherited = PQfnumber(res, "inherited");
11407 i_null_frac = PQfnumber(res, "null_frac");
11408 i_avg_width = PQfnumber(res, "avg_width");
11409 i_n_distinct = PQfnumber(res, "n_distinct");
11410 i_most_common_vals = PQfnumber(res, "most_common_vals");
11411 i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11412 i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11413 i_correlation = PQfnumber(res, "correlation");
11414 i_most_common_elems = PQfnumber(res, "most_common_elems");
11415 i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11416 i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11417 i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11418 i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11419 i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11420
11421 /* restore attribute stats */
11422 for (; rownum < PQntuples(res); rownum++)
11423 {
11424 const char *attname;
11425
11426 /* Stop if the next stat row in our cache isn't for this relation. */
11427 if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11428 strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11429 break;
11430
11431 appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11432 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11434 appendPQExpBufferStr(out, "\t'schemaname', ");
11435 appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11436 appendPQExpBufferStr(out, ",\n\t'relname', ");
11437 appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11438
11439 if (PQgetisnull(res, rownum, i_attname))
11440 pg_fatal("unexpected null attname");
11441 attname = PQgetvalue(res, rownum, i_attname);
11442
11443 /*
11444 * Indexes look up attname in indAttNames to derive attnum, all others
11445 * use attname directly. We must specify attnum for indexes, since
11446 * their attnames are not necessarily stable across dump/reload.
11447 */
11448 if (rsinfo->nindAttNames == 0)
11449 {
11450 appendPQExpBufferStr(out, ",\n\t'attname', ");
11452 }
11453 else
11454 {
11455 bool found = false;
11456
11457 for (int i = 0; i < rsinfo->nindAttNames; i++)
11458 {
11459 if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11460 {
11461 appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11462 i + 1);
11463 found = true;
11464 break;
11465 }
11466 }
11467
11468 if (!found)
11469 pg_fatal("could not find index attname \"%s\"", attname);
11470 }
11471
11472 if (!PQgetisnull(res, rownum, i_inherited))
11473 appendNamedArgument(out, fout, "inherited", "boolean",
11474 PQgetvalue(res, rownum, i_inherited));
11475 if (!PQgetisnull(res, rownum, i_null_frac))
11476 appendNamedArgument(out, fout, "null_frac", "real",
11477 PQgetvalue(res, rownum, i_null_frac));
11478 if (!PQgetisnull(res, rownum, i_avg_width))
11479 appendNamedArgument(out, fout, "avg_width", "integer",
11480 PQgetvalue(res, rownum, i_avg_width));
11481 if (!PQgetisnull(res, rownum, i_n_distinct))
11482 appendNamedArgument(out, fout, "n_distinct", "real",
11483 PQgetvalue(res, rownum, i_n_distinct));
11484 if (!PQgetisnull(res, rownum, i_most_common_vals))
11485 appendNamedArgument(out, fout, "most_common_vals", "text",
11486 PQgetvalue(res, rownum, i_most_common_vals));
11487 if (!PQgetisnull(res, rownum, i_most_common_freqs))
11488 appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11489 PQgetvalue(res, rownum, i_most_common_freqs));
11490 if (!PQgetisnull(res, rownum, i_histogram_bounds))
11491 appendNamedArgument(out, fout, "histogram_bounds", "text",
11492 PQgetvalue(res, rownum, i_histogram_bounds));
11493 if (!PQgetisnull(res, rownum, i_correlation))
11494 appendNamedArgument(out, fout, "correlation", "real",
11495 PQgetvalue(res, rownum, i_correlation));
11496 if (!PQgetisnull(res, rownum, i_most_common_elems))
11497 appendNamedArgument(out, fout, "most_common_elems", "text",
11498 PQgetvalue(res, rownum, i_most_common_elems));
11499 if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11500 appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11501 PQgetvalue(res, rownum, i_most_common_elem_freqs));
11502 if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11503 appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11504 PQgetvalue(res, rownum, i_elem_count_histogram));
11505 if (fout->remoteVersion >= 170000)
11506 {
11507 if (!PQgetisnull(res, rownum, i_range_length_histogram))
11508 appendNamedArgument(out, fout, "range_length_histogram", "text",
11509 PQgetvalue(res, rownum, i_range_length_histogram));
11510 if (!PQgetisnull(res, rownum, i_range_empty_frac))
11511 appendNamedArgument(out, fout, "range_empty_frac", "real",
11512 PQgetvalue(res, rownum, i_range_empty_frac));
11513 if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11514 appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11515 PQgetvalue(res, rownum, i_range_bounds_histogram));
11516 }
11517 appendPQExpBufferStr(out, "\n);\n");
11518 }
11519
11520 destroyPQExpBuffer(query);
11521 return out->data;
11522}
11523
11524/*
11525 * dumpRelationStats --
11526 *
11527 * Make an ArchiveEntry for the relation statistics. The Archiver will take
11528 * care of gathering the statistics and generating the restore commands when
11529 * they are needed.
11530 */
11531static void
11533{
11534 const DumpableObject *dobj = &rsinfo->dobj;
11535
11536 /* nothing to do if we are not dumping statistics */
11537 if (!fout->dopt->dumpStatistics)
11538 return;
11539
11541 ARCHIVE_OPTS(.tag = dobj->name,
11542 .namespace = dobj->namespace->dobj.name,
11543 .description = "STATISTICS DATA",
11544 .section = rsinfo->section,
11545 .defnFn = dumpRelationStats_dumper,
11546 .defnArg = rsinfo,
11547 .deps = dobj->dependencies,
11548 .nDeps = dobj->nDeps));
11549}
11550
11551/*
11552 * dumpTableComment --
11553 *
11554 * As above, but dump comments for both the specified table (or view)
11555 * and its columns.
11556 */
11557static void
11559 const char *reltypename)
11560{
11561 DumpOptions *dopt = fout->dopt;
11563 int ncomments;
11564 PQExpBuffer query;
11565 PQExpBuffer tag;
11566
11567 /* do nothing, if --no-comments is supplied */
11568 if (dopt->no_comments)
11569 return;
11570
11571 /* Comments are SCHEMA not data */
11572 if (!dopt->dumpSchema)
11573 return;
11574
11575 /* Search for comments associated with relation, using table */
11576 ncomments = findComments(tbinfo->dobj.catId.tableoid,
11577 tbinfo->dobj.catId.oid,
11578 &comments);
11579
11580 /* If comments exist, build COMMENT ON statements */
11581 if (ncomments <= 0)
11582 return;
11583
11584 query = createPQExpBuffer();
11585 tag = createPQExpBuffer();
11586
11587 while (ncomments > 0)
11588 {
11589 const char *descr = comments->descr;
11590 int objsubid = comments->objsubid;
11591
11592 if (objsubid == 0)
11593 {
11594 resetPQExpBuffer(tag);
11595 appendPQExpBuffer(tag, "%s %s", reltypename,
11596 fmtId(tbinfo->dobj.name));
11597
11598 resetPQExpBuffer(query);
11599 appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11601 appendStringLiteralAH(query, descr, fout);
11602 appendPQExpBufferStr(query, ";\n");
11603
11605 ARCHIVE_OPTS(.tag = tag->data,
11606 .namespace = tbinfo->dobj.namespace->dobj.name,
11607 .owner = tbinfo->rolname,
11608 .description = "COMMENT",
11609 .section = SECTION_NONE,
11610 .createStmt = query->data,
11611 .deps = &(tbinfo->dobj.dumpId),
11612 .nDeps = 1));
11613 }
11614 else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11615 {
11616 resetPQExpBuffer(tag);
11617 appendPQExpBuffer(tag, "COLUMN %s.",
11618 fmtId(tbinfo->dobj.name));
11619 appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11620
11621 resetPQExpBuffer(query);
11622 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11624 appendPQExpBuffer(query, "%s IS ",
11625 fmtId(tbinfo->attnames[objsubid - 1]));
11626 appendStringLiteralAH(query, descr, fout);
11627 appendPQExpBufferStr(query, ";\n");
11628
11630 ARCHIVE_OPTS(.tag = tag->data,
11631 .namespace = tbinfo->dobj.namespace->dobj.name,
11632 .owner = tbinfo->rolname,
11633 .description = "COMMENT",
11634 .section = SECTION_NONE,
11635 .createStmt = query->data,
11636 .deps = &(tbinfo->dobj.dumpId),
11637 .nDeps = 1));
11638 }
11639
11640 comments++;
11641 ncomments--;
11642 }
11643
11644 destroyPQExpBuffer(query);
11645 destroyPQExpBuffer(tag);
11646}
11647
11648/*
11649 * findComments --
11650 *
11651 * Find the comment(s), if any, associated with the given object. All the
11652 * objsubid values associated with the given classoid/objoid are found with
11653 * one search.
11654 */
11655static int
11656findComments(Oid classoid, Oid objoid, CommentItem **items)
11657{
11659 CommentItem *low;
11660 CommentItem *high;
11661 int nmatch;
11662
11663 /*
11664 * Do binary search to find some item matching the object.
11665 */
11666 low = &comments[0];
11667 high = &comments[ncomments - 1];
11668 while (low <= high)
11669 {
11670 middle = low + (high - low) / 2;
11671
11672 if (classoid < middle->classoid)
11673 high = middle - 1;
11674 else if (classoid > middle->classoid)
11675 low = middle + 1;
11676 else if (objoid < middle->objoid)
11677 high = middle - 1;
11678 else if (objoid > middle->objoid)
11679 low = middle + 1;
11680 else
11681 break; /* found a match */
11682 }
11683
11684 if (low > high) /* no matches */
11685 {
11686 *items = NULL;
11687 return 0;
11688 }
11689
11690 /*
11691 * Now determine how many items match the object. The search loop
11692 * invariant still holds: only items between low and high inclusive could
11693 * match.
11694 */
11695 nmatch = 1;
11696 while (middle > low)
11697 {
11698 if (classoid != middle[-1].classoid ||
11699 objoid != middle[-1].objoid)
11700 break;
11701 middle--;
11702 nmatch++;
11703 }
11704
11705 *items = middle;
11706
11707 middle += nmatch;
11708 while (middle <= high)
11709 {
11710 if (classoid != middle->classoid ||
11711 objoid != middle->objoid)
11712 break;
11713 middle++;
11714 nmatch++;
11715 }
11716
11717 return nmatch;
11718}
11719
11720/*
11721 * collectComments --
11722 *
11723 * Construct a table of all comments available for database objects;
11724 * also set the has-comment component flag for each relevant object.
11725 *
11726 * We used to do per-object queries for the comments, but it's much faster
11727 * to pull them all over at once, and on most databases the memory cost
11728 * isn't high.
11729 *
11730 * The table is sorted by classoid/objid/objsubid for speed in lookup.
11731 */
11732static void
11734{
11735 PGresult *res;
11736 PQExpBuffer query;
11737 int i_description;
11738 int i_classoid;
11739 int i_objoid;
11740 int i_objsubid;
11741 int ntups;
11742 int i;
11743 DumpableObject *dobj;
11744
11745 query = createPQExpBuffer();
11746
11747 appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11748 "FROM pg_catalog.pg_description "
11749 "ORDER BY classoid, objoid, objsubid");
11750
11751 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11752
11753 /* Construct lookup table containing OIDs in numeric form */
11754
11755 i_description = PQfnumber(res, "description");
11756 i_classoid = PQfnumber(res, "classoid");
11757 i_objoid = PQfnumber(res, "objoid");
11758 i_objsubid = PQfnumber(res, "objsubid");
11759
11760 ntups = PQntuples(res);
11761
11763 ncomments = 0;
11764 dobj = NULL;
11765
11766 for (i = 0; i < ntups; i++)
11767 {
11768 CatalogId objId;
11769 int subid;
11770
11771 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11772 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11773 subid = atoi(PQgetvalue(res, i, i_objsubid));
11774
11775 /* We needn't remember comments that don't match any dumpable object */
11776 if (dobj == NULL ||
11777 dobj->catId.tableoid != objId.tableoid ||
11778 dobj->catId.oid != objId.oid)
11779 dobj = findObjectByCatalogId(objId);
11780 if (dobj == NULL)
11781 continue;
11782
11783 /*
11784 * Comments on columns of composite types are linked to the type's
11785 * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11786 * in the type's own DumpableObject.
11787 */
11788 if (subid != 0 && dobj->objType == DO_TABLE &&
11789 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11790 {
11792
11793 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11794 if (cTypeInfo)
11795 cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11796 }
11797 else
11798 dobj->components |= DUMP_COMPONENT_COMMENT;
11799
11802 comments[ncomments].objoid = objId.oid;
11803 comments[ncomments].objsubid = subid;
11804 ncomments++;
11805 }
11806
11807 PQclear(res);
11808 destroyPQExpBuffer(query);
11809}
11810
11811/*
11812 * dumpDumpableObject
11813 *
11814 * This routine and its subsidiaries are responsible for creating
11815 * ArchiveEntries (TOC objects) for each object to be dumped.
11816 */
11817static void
11819{
11820 /*
11821 * Clear any dump-request bits for components that don't exist for this
11822 * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11823 * request for every kind of object.)
11824 */
11825 dobj->dump &= dobj->components;
11826
11827 /* Now, short-circuit if there's nothing to be done here. */
11828 if (dobj->dump == 0)
11829 return;
11830
11831 switch (dobj->objType)
11832 {
11833 case DO_NAMESPACE:
11834 dumpNamespace(fout, (const NamespaceInfo *) dobj);
11835 break;
11836 case DO_EXTENSION:
11837 dumpExtension(fout, (const ExtensionInfo *) dobj);
11838 break;
11839 case DO_TYPE:
11840 dumpType(fout, (const TypeInfo *) dobj);
11841 break;
11842 case DO_SHELL_TYPE:
11843 dumpShellType(fout, (const ShellTypeInfo *) dobj);
11844 break;
11845 case DO_FUNC:
11846 dumpFunc(fout, (const FuncInfo *) dobj);
11847 break;
11848 case DO_AGG:
11849 dumpAgg(fout, (const AggInfo *) dobj);
11850 break;
11851 case DO_OPERATOR:
11852 dumpOpr(fout, (const OprInfo *) dobj);
11853 break;
11854 case DO_ACCESS_METHOD:
11855 dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11856 break;
11857 case DO_OPCLASS:
11858 dumpOpclass(fout, (const OpclassInfo *) dobj);
11859 break;
11860 case DO_OPFAMILY:
11861 dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11862 break;
11863 case DO_COLLATION:
11864 dumpCollation(fout, (const CollInfo *) dobj);
11865 break;
11866 case DO_CONVERSION:
11867 dumpConversion(fout, (const ConvInfo *) dobj);
11868 break;
11869 case DO_TABLE:
11870 dumpTable(fout, (const TableInfo *) dobj);
11871 break;
11872 case DO_TABLE_ATTACH:
11873 dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11874 break;
11875 case DO_ATTRDEF:
11876 dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11877 break;
11878 case DO_INDEX:
11879 dumpIndex(fout, (const IndxInfo *) dobj);
11880 break;
11881 case DO_INDEX_ATTACH:
11882 dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11883 break;
11884 case DO_STATSEXT:
11885 dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11886 dumpStatisticsExtStats(fout, (const StatsExtInfo *) dobj);
11887 break;
11888 case DO_REFRESH_MATVIEW:
11889 refreshMatViewData(fout, (const TableDataInfo *) dobj);
11890 break;
11891 case DO_RULE:
11892 dumpRule(fout, (const RuleInfo *) dobj);
11893 break;
11894 case DO_TRIGGER:
11895 dumpTrigger(fout, (const TriggerInfo *) dobj);
11896 break;
11897 case DO_EVENT_TRIGGER:
11898 dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11899 break;
11900 case DO_CONSTRAINT:
11901 dumpConstraint(fout, (const ConstraintInfo *) dobj);
11902 break;
11903 case DO_FK_CONSTRAINT:
11904 dumpConstraint(fout, (const ConstraintInfo *) dobj);
11905 break;
11906 case DO_PROCLANG:
11907 dumpProcLang(fout, (const ProcLangInfo *) dobj);
11908 break;
11909 case DO_CAST:
11910 dumpCast(fout, (const CastInfo *) dobj);
11911 break;
11912 case DO_TRANSFORM:
11913 dumpTransform(fout, (const TransformInfo *) dobj);
11914 break;
11915 case DO_SEQUENCE_SET:
11916 dumpSequenceData(fout, (const TableDataInfo *) dobj);
11917 break;
11918 case DO_TABLE_DATA:
11919 dumpTableData(fout, (const TableDataInfo *) dobj);
11920 break;
11921 case DO_DUMMY_TYPE:
11922 /* table rowtypes and array types are never dumped separately */
11923 break;
11924 case DO_TSPARSER:
11925 dumpTSParser(fout, (const TSParserInfo *) dobj);
11926 break;
11927 case DO_TSDICT:
11928 dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11929 break;
11930 case DO_TSTEMPLATE:
11931 dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11932 break;
11933 case DO_TSCONFIG:
11934 dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11935 break;
11936 case DO_FDW:
11937 dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11938 break;
11939 case DO_FOREIGN_SERVER:
11940 dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11941 break;
11942 case DO_DEFAULT_ACL:
11943 dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11944 break;
11945 case DO_LARGE_OBJECT:
11946 dumpLO(fout, (const LoInfo *) dobj);
11947 break;
11949 if (dobj->dump & DUMP_COMPONENT_DATA)
11950 {
11951 LoInfo *loinfo;
11952 TocEntry *te;
11953
11955 if (loinfo == NULL)
11956 pg_fatal("missing metadata for large objects \"%s\"",
11957 dobj->name);
11958
11959 te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11960 ARCHIVE_OPTS(.tag = dobj->name,
11961 .owner = loinfo->rolname,
11962 .description = "BLOBS",
11963 .section = SECTION_DATA,
11964 .deps = dobj->dependencies,
11965 .nDeps = dobj->nDeps,
11966 .dumpFn = dumpLOs,
11967 .dumpArg = loinfo));
11968
11969 /*
11970 * Set the TocEntry's dataLength in case we are doing a
11971 * parallel dump and want to order dump jobs by table size.
11972 * (We need some size estimate for every TocEntry with a
11973 * DataDumper function.) We don't currently have any cheap
11974 * way to estimate the size of LOs, but fortunately it doesn't
11975 * matter too much as long as we get large batches of LOs
11976 * processed reasonably early. Assume 8K per blob.
11977 */
11978 te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11979 }
11980 break;
11981 case DO_POLICY:
11982 dumpPolicy(fout, (const PolicyInfo *) dobj);
11983 break;
11984 case DO_PUBLICATION:
11985 dumpPublication(fout, (const PublicationInfo *) dobj);
11986 break;
11987 case DO_PUBLICATION_REL:
11989 break;
11992 (const PublicationSchemaInfo *) dobj);
11993 break;
11994 case DO_SUBSCRIPTION:
11995 dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11996 break;
11998 dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11999 break;
12000 case DO_REL_STATS:
12001 dumpRelationStats(fout, (const RelStatsInfo *) dobj);
12002 break;
12005 /* never dumped, nothing to do */
12006 break;
12007 }
12008}
12009
12010/*
12011 * dumpNamespace
12012 * writes out to fout the queries to recreate a user-defined namespace
12013 */
12014static void
12016{
12017 DumpOptions *dopt = fout->dopt;
12018 PQExpBuffer q;
12020 char *qnspname;
12021
12022 /* Do nothing if not dumping schema */
12023 if (!dopt->dumpSchema)
12024 return;
12025
12026 q = createPQExpBuffer();
12028
12029 qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
12030
12031 if (nspinfo->create)
12032 {
12033 appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
12034 appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
12035 }
12036 else
12037 {
12038 /* see selectDumpableNamespace() */
12040 "-- *not* dropping schema, since initdb creates it\n");
12042 "-- *not* creating schema, since initdb creates it\n");
12043 }
12044
12045 if (dopt->binary_upgrade)
12047 "SCHEMA", qnspname, NULL);
12048
12049 if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12050 ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
12051 ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
12052 .owner = nspinfo->rolname,
12053 .description = "SCHEMA",
12054 .section = SECTION_PRE_DATA,
12055 .createStmt = q->data,
12056 .dropStmt = delq->data));
12057
12058 /* Dump Schema Comments and Security Labels */
12059 if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12060 {
12061 const char *initdb_comment = NULL;
12062
12063 if (!nspinfo->create && strcmp(qnspname, "public") == 0)
12064 initdb_comment = "standard public schema";
12066 NULL, nspinfo->rolname,
12067 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
12069 }
12070
12071 if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12072 dumpSecLabel(fout, "SCHEMA", qnspname,
12073 NULL, nspinfo->rolname,
12074 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
12075
12076 if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
12077 dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
12078 qnspname, NULL, NULL,
12079 NULL, nspinfo->rolname, &nspinfo->dacl);
12080
12081 free(qnspname);
12082
12085}
12086
12087/*
12088 * dumpExtension
12089 * writes out to fout the queries to recreate an extension
12090 */
12091static void
12093{
12094 DumpOptions *dopt = fout->dopt;
12095 PQExpBuffer q;
12097 char *qextname;
12098
12099 /* Do nothing if not dumping schema */
12100 if (!dopt->dumpSchema)
12101 return;
12102
12103 q = createPQExpBuffer();
12105
12106 qextname = pg_strdup(fmtId(extinfo->dobj.name));
12107
12108 appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
12109
12110 if (!dopt->binary_upgrade)
12111 {
12112 /*
12113 * In a regular dump, we simply create the extension, intentionally
12114 * not specifying a version, so that the destination installation's
12115 * default version is used.
12116 *
12117 * Use of IF NOT EXISTS here is unlike our behavior for other object
12118 * types; but there are various scenarios in which it's convenient to
12119 * manually create the desired extension before restoring, so we
12120 * prefer to allow it to exist already.
12121 */
12122 appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
12123 qextname, fmtId(extinfo->namespace));
12124 }
12125 else
12126 {
12127 /*
12128 * In binary-upgrade mode, it's critical to reproduce the state of the
12129 * database exactly, so our procedure is to create an empty extension,
12130 * restore all the contained objects normally, and add them to the
12131 * extension one by one. This function performs just the first of
12132 * those steps. binary_upgrade_extension_member() takes care of
12133 * adding member objects as they're created.
12134 */
12135 int i;
12136 int n;
12137
12138 appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
12139
12140 /*
12141 * We unconditionally create the extension, so we must drop it if it
12142 * exists. This could happen if the user deleted 'plpgsql' and then
12143 * readded it, causing its oid to be greater than g_last_builtin_oid.
12144 */
12145 appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
12146
12148 "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
12149 appendStringLiteralAH(q, extinfo->dobj.name, fout);
12150 appendPQExpBufferStr(q, ", ");
12151 appendStringLiteralAH(q, extinfo->namespace, fout);
12152 appendPQExpBufferStr(q, ", ");
12153 appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
12154 appendStringLiteralAH(q, extinfo->extversion, fout);
12155 appendPQExpBufferStr(q, ", ");
12156
12157 /*
12158 * Note that we're pushing extconfig (an OID array) back into
12159 * pg_extension exactly as-is. This is OK because pg_class OIDs are
12160 * preserved in binary upgrade.
12161 */
12162 if (strlen(extinfo->extconfig) > 2)
12163 appendStringLiteralAH(q, extinfo->extconfig, fout);
12164 else
12165 appendPQExpBufferStr(q, "NULL");
12166 appendPQExpBufferStr(q, ", ");
12167 if (strlen(extinfo->extcondition) > 2)
12168 appendStringLiteralAH(q, extinfo->extcondition, fout);
12169 else
12170 appendPQExpBufferStr(q, "NULL");
12171 appendPQExpBufferStr(q, ", ");
12172 appendPQExpBufferStr(q, "ARRAY[");
12173 n = 0;
12174 for (i = 0; i < extinfo->dobj.nDeps; i++)
12175 {
12177
12178 extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
12179 if (extobj && extobj->objType == DO_EXTENSION)
12180 {
12181 if (n++ > 0)
12182 appendPQExpBufferChar(q, ',');
12184 }
12185 }
12186 appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12187 appendPQExpBufferStr(q, ");\n");
12188 }
12189
12190 if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12191 ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12192 ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12193 .description = "EXTENSION",
12194 .section = SECTION_PRE_DATA,
12195 .createStmt = q->data,
12196 .dropStmt = delq->data));
12197
12198 /* Dump Extension Comments */
12199 if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12200 dumpComment(fout, "EXTENSION", qextname,
12201 NULL, "",
12202 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12203
12204 free(qextname);
12205
12208}
12209
12210/*
12211 * dumpType
12212 * writes out to fout the queries to recreate a user-defined type
12213 */
12214static void
12216{
12217 DumpOptions *dopt = fout->dopt;
12218
12219 /* Do nothing if not dumping schema */
12220 if (!dopt->dumpSchema)
12221 return;
12222
12223 /* Dump out in proper style */
12224 if (tyinfo->typtype == TYPTYPE_BASE)
12226 else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12228 else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12230 else if (tyinfo->typtype == TYPTYPE_ENUM)
12232 else if (tyinfo->typtype == TYPTYPE_RANGE)
12234 else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12236 else
12237 pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12238 tyinfo->dobj.name);
12239}
12240
12241/*
12242 * dumpEnumType
12243 * writes out to fout the queries to recreate a user-defined enum type
12244 */
12245static void
12247{
12248 DumpOptions *dopt = fout->dopt;
12252 PGresult *res;
12253 int num,
12254 i;
12255 Oid enum_oid;
12256 char *qtypname;
12257 char *qualtypname;
12258 char *label;
12259 int i_enumlabel;
12260 int i_oid;
12261
12263 {
12264 /* Set up query for enum-specific details */
12266 "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12267 "SELECT oid, enumlabel "
12268 "FROM pg_catalog.pg_enum "
12269 "WHERE enumtypid = $1 "
12270 "ORDER BY enumsortorder");
12271
12272 ExecuteSqlStatement(fout, query->data);
12273
12275 }
12276
12277 printfPQExpBuffer(query,
12278 "EXECUTE dumpEnumType('%u')",
12279 tyinfo->dobj.catId.oid);
12280
12281 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12282
12283 num = PQntuples(res);
12284
12285 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12287
12288 /*
12289 * CASCADE shouldn't be required here as for normal types since the I/O
12290 * functions are generic and do not get dropped.
12291 */
12292 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12293
12294 if (dopt->binary_upgrade)
12296 tyinfo->dobj.catId.oid,
12297 false, false);
12298
12299 appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12300 qualtypname);
12301
12302 if (!dopt->binary_upgrade)
12303 {
12304 i_enumlabel = PQfnumber(res, "enumlabel");
12305
12306 /* Labels with server-assigned oids */
12307 for (i = 0; i < num; i++)
12308 {
12309 label = PQgetvalue(res, i, i_enumlabel);
12310 if (i > 0)
12311 appendPQExpBufferChar(q, ',');
12312 appendPQExpBufferStr(q, "\n ");
12314 }
12315 }
12316
12317 appendPQExpBufferStr(q, "\n);\n");
12318
12319 if (dopt->binary_upgrade)
12320 {
12321 i_oid = PQfnumber(res, "oid");
12322 i_enumlabel = PQfnumber(res, "enumlabel");
12323
12324 /* Labels with dump-assigned (preserved) oids */
12325 for (i = 0; i < num; i++)
12326 {
12327 enum_oid = atooid(PQgetvalue(res, i, i_oid));
12328 label = PQgetvalue(res, i, i_enumlabel);
12329
12330 if (i == 0)
12331 appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12333 "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12334 enum_oid);
12335 appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12337 appendPQExpBufferStr(q, ";\n\n");
12338 }
12339 }
12340
12341 if (dopt->binary_upgrade)
12343 "TYPE", qtypname,
12344 tyinfo->dobj.namespace->dobj.name);
12345
12346 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12347 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12348 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12349 .namespace = tyinfo->dobj.namespace->dobj.name,
12350 .owner = tyinfo->rolname,
12351 .description = "TYPE",
12352 .section = SECTION_PRE_DATA,
12353 .createStmt = q->data,
12354 .dropStmt = delq->data));
12355
12356 /* Dump Type Comments and Security Labels */
12357 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12358 dumpComment(fout, "TYPE", qtypname,
12359 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12360 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12361
12362 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12363 dumpSecLabel(fout, "TYPE", qtypname,
12364 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12365 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12366
12367 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12368 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12369 qtypname, NULL,
12370 tyinfo->dobj.namespace->dobj.name,
12371 NULL, tyinfo->rolname, &tyinfo->dacl);
12372
12373 PQclear(res);
12376 destroyPQExpBuffer(query);
12377 free(qtypname);
12379}
12380
12381/*
12382 * dumpRangeType
12383 * writes out to fout the queries to recreate a user-defined range type
12384 */
12385static void
12387{
12388 DumpOptions *dopt = fout->dopt;
12392 PGresult *res;
12394 char *qtypname;
12395 char *qualtypname;
12396 char *procname;
12397
12399 {
12400 /* Set up query for range-specific details */
12402 "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12403
12405 "SELECT ");
12406
12407 if (fout->remoteVersion >= 140000)
12409 "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12410 else
12412 "NULL AS rngmultitype, ");
12413
12415 "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12416 "opc.opcname AS opcname, "
12417 "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12418 " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12419 "opc.opcdefault, "
12420 "CASE WHEN rngcollation = st.typcollation THEN 0 "
12421 " ELSE rngcollation END AS collation, "
12422 "rngcanonical, rngsubdiff "
12423 "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12424 " pg_catalog.pg_opclass opc "
12425 "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12426 "rngtypid = $1");
12427
12428 ExecuteSqlStatement(fout, query->data);
12429
12431 }
12432
12433 printfPQExpBuffer(query,
12434 "EXECUTE dumpRangeType('%u')",
12435 tyinfo->dobj.catId.oid);
12436
12437 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12438
12439 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12441
12442 /*
12443 * CASCADE shouldn't be required here as for normal types since the I/O
12444 * functions are generic and do not get dropped.
12445 */
12446 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12447
12448 if (dopt->binary_upgrade)
12450 tyinfo->dobj.catId.oid,
12451 false, true);
12452
12453 appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12454 qualtypname);
12455
12456 appendPQExpBuffer(q, "\n subtype = %s",
12457 PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12458
12459 if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12460 appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12461 PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12462
12463 /* print subtype_opclass only if not default for subtype */
12464 if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12465 {
12466 char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12467 char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12468
12469 appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12470 fmtId(nspname));
12472 }
12473
12474 collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12476 {
12478
12479 if (coll)
12480 appendPQExpBuffer(q, ",\n collation = %s",
12481 fmtQualifiedDumpable(coll));
12482 }
12483
12484 procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12485 if (strcmp(procname, "-") != 0)
12486 appendPQExpBuffer(q, ",\n canonical = %s", procname);
12487
12488 procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12489 if (strcmp(procname, "-") != 0)
12490 appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12491
12492 appendPQExpBufferStr(q, "\n);\n");
12493
12494 if (dopt->binary_upgrade)
12496 "TYPE", qtypname,
12497 tyinfo->dobj.namespace->dobj.name);
12498
12499 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12500 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12501 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12502 .namespace = tyinfo->dobj.namespace->dobj.name,
12503 .owner = tyinfo->rolname,
12504 .description = "TYPE",
12505 .section = SECTION_PRE_DATA,
12506 .createStmt = q->data,
12507 .dropStmt = delq->data));
12508
12509 /* Dump Type Comments and Security Labels */
12510 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12511 dumpComment(fout, "TYPE", qtypname,
12512 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12513 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12514
12515 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12516 dumpSecLabel(fout, "TYPE", qtypname,
12517 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12518 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12519
12520 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12521 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12522 qtypname, NULL,
12523 tyinfo->dobj.namespace->dobj.name,
12524 NULL, tyinfo->rolname, &tyinfo->dacl);
12525
12526 PQclear(res);
12529 destroyPQExpBuffer(query);
12530 free(qtypname);
12532}
12533
12534/*
12535 * dumpUndefinedType
12536 * writes out to fout the queries to recreate a !typisdefined type
12537 *
12538 * This is a shell type, but we use different terminology to distinguish
12539 * this case from where we have to emit a shell type definition to break
12540 * circular dependencies. An undefined type shouldn't ever have anything
12541 * depending on it.
12542 */
12543static void
12545{
12546 DumpOptions *dopt = fout->dopt;
12549 char *qtypname;
12550 char *qualtypname;
12551
12552 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12554
12555 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12556
12557 if (dopt->binary_upgrade)
12559 tyinfo->dobj.catId.oid,
12560 false, false);
12561
12562 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12563 qualtypname);
12564
12565 if (dopt->binary_upgrade)
12567 "TYPE", qtypname,
12568 tyinfo->dobj.namespace->dobj.name);
12569
12570 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12571 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12572 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12573 .namespace = tyinfo->dobj.namespace->dobj.name,
12574 .owner = tyinfo->rolname,
12575 .description = "TYPE",
12576 .section = SECTION_PRE_DATA,
12577 .createStmt = q->data,
12578 .dropStmt = delq->data));
12579
12580 /* Dump Type Comments and Security Labels */
12581 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12582 dumpComment(fout, "TYPE", qtypname,
12583 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12584 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12585
12586 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12587 dumpSecLabel(fout, "TYPE", qtypname,
12588 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12589 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12590
12591 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12592 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12593 qtypname, NULL,
12594 tyinfo->dobj.namespace->dobj.name,
12595 NULL, tyinfo->rolname, &tyinfo->dacl);
12596
12599 free(qtypname);
12601}
12602
12603/*
12604 * dumpBaseType
12605 * writes out to fout the queries to recreate a user-defined base type
12606 */
12607static void
12609{
12610 DumpOptions *dopt = fout->dopt;
12614 PGresult *res;
12615 char *qtypname;
12616 char *qualtypname;
12617 char *typlen;
12618 char *typinput;
12619 char *typoutput;
12620 char *typreceive;
12621 char *typsend;
12622 char *typmodin;
12623 char *typmodout;
12624 char *typanalyze;
12625 char *typsubscript;
12632 char *typcategory;
12633 char *typispreferred;
12634 char *typdelim;
12635 char *typbyval;
12636 char *typalign;
12637 char *typstorage;
12638 char *typcollatable;
12639 char *typdefault;
12640 bool typdefault_is_literal = false;
12641
12643 {
12644 /* Set up query for type-specific details */
12646 "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12647 "SELECT typlen, "
12648 "typinput, typoutput, typreceive, typsend, "
12649 "typreceive::pg_catalog.oid AS typreceiveoid, "
12650 "typsend::pg_catalog.oid AS typsendoid, "
12651 "typanalyze, "
12652 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12653 "typdelim, typbyval, typalign, typstorage, "
12654 "typmodin, typmodout, "
12655 "typmodin::pg_catalog.oid AS typmodinoid, "
12656 "typmodout::pg_catalog.oid AS typmodoutoid, "
12657 "typcategory, typispreferred, "
12658 "(typcollation <> 0) AS typcollatable, "
12659 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12660
12661 if (fout->remoteVersion >= 140000)
12663 "typsubscript, "
12664 "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12665 else
12667 "'-' AS typsubscript, 0 AS typsubscriptoid ");
12668
12669 appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12670 "WHERE oid = $1");
12671
12672 ExecuteSqlStatement(fout, query->data);
12673
12675 }
12676
12677 printfPQExpBuffer(query,
12678 "EXECUTE dumpBaseType('%u')",
12679 tyinfo->dobj.catId.oid);
12680
12681 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12682
12683 typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12684 typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12685 typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12686 typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12687 typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12688 typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12689 typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12690 typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12691 typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12692 typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12693 typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12694 typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12695 typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12696 typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12697 typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12698 typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12699 typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12700 typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12701 typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12702 typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12703 typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12704 typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12705 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12706 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12707 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12708 {
12709 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12710 typdefault_is_literal = true; /* it needs quotes */
12711 }
12712 else
12713 typdefault = NULL;
12714
12715 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12717
12718 /*
12719 * The reason we include CASCADE is that the circular dependency between
12720 * the type and its I/O functions makes it impossible to drop the type any
12721 * other way.
12722 */
12723 appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12724
12725 /*
12726 * We might already have a shell type, but setting pg_type_oid is
12727 * harmless, and in any case we'd better set the array type OID.
12728 */
12729 if (dopt->binary_upgrade)
12731 tyinfo->dobj.catId.oid,
12732 false, false);
12733
12735 "CREATE TYPE %s (\n"
12736 " INTERNALLENGTH = %s",
12738 (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12739
12740 /* regproc result is sufficiently quoted already */
12741 appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12742 appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12744 appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12746 appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12748 appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12750 appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12752 appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12753
12754 if (strcmp(typcollatable, "t") == 0)
12755 appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12756
12757 if (typdefault != NULL)
12758 {
12759 appendPQExpBufferStr(q, ",\n DEFAULT = ");
12762 else
12764 }
12765
12767 appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12768
12769 if (OidIsValid(tyinfo->typelem))
12770 appendPQExpBuffer(q, ",\n ELEMENT = %s",
12772 zeroIsError));
12773
12774 if (strcmp(typcategory, "U") != 0)
12775 {
12776 appendPQExpBufferStr(q, ",\n CATEGORY = ");
12778 }
12779
12780 if (strcmp(typispreferred, "t") == 0)
12781 appendPQExpBufferStr(q, ",\n PREFERRED = true");
12782
12783 if (typdelim && strcmp(typdelim, ",") != 0)
12784 {
12785 appendPQExpBufferStr(q, ",\n DELIMITER = ");
12786 appendStringLiteralAH(q, typdelim, fout);
12787 }
12788
12789 if (*typalign == TYPALIGN_CHAR)
12790 appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12791 else if (*typalign == TYPALIGN_SHORT)
12792 appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12793 else if (*typalign == TYPALIGN_INT)
12794 appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12795 else if (*typalign == TYPALIGN_DOUBLE)
12796 appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12797
12798 if (*typstorage == TYPSTORAGE_PLAIN)
12799 appendPQExpBufferStr(q, ",\n STORAGE = plain");
12800 else if (*typstorage == TYPSTORAGE_EXTERNAL)
12801 appendPQExpBufferStr(q, ",\n STORAGE = external");
12802 else if (*typstorage == TYPSTORAGE_EXTENDED)
12803 appendPQExpBufferStr(q, ",\n STORAGE = extended");
12804 else if (*typstorage == TYPSTORAGE_MAIN)
12805 appendPQExpBufferStr(q, ",\n STORAGE = main");
12806
12807 if (strcmp(typbyval, "t") == 0)
12808 appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12809
12810 appendPQExpBufferStr(q, "\n);\n");
12811
12812 if (dopt->binary_upgrade)
12814 "TYPE", qtypname,
12815 tyinfo->dobj.namespace->dobj.name);
12816
12817 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12818 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12819 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12820 .namespace = tyinfo->dobj.namespace->dobj.name,
12821 .owner = tyinfo->rolname,
12822 .description = "TYPE",
12823 .section = SECTION_PRE_DATA,
12824 .createStmt = q->data,
12825 .dropStmt = delq->data));
12826
12827 /* Dump Type Comments and Security Labels */
12828 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12829 dumpComment(fout, "TYPE", qtypname,
12830 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12831 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12832
12833 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12834 dumpSecLabel(fout, "TYPE", qtypname,
12835 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12836 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12837
12838 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12839 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12840 qtypname, NULL,
12841 tyinfo->dobj.namespace->dobj.name,
12842 NULL, tyinfo->rolname, &tyinfo->dacl);
12843
12844 PQclear(res);
12847 destroyPQExpBuffer(query);
12848 free(qtypname);
12850}
12851
12852/*
12853 * dumpDomain
12854 * writes out to fout the queries to recreate a user-defined domain
12855 */
12856static void
12858{
12859 DumpOptions *dopt = fout->dopt;
12863 PGresult *res;
12864 int i;
12865 char *qtypname;
12866 char *qualtypname;
12867 char *typnotnull;
12868 char *typdefn;
12869 char *typdefault;
12870 Oid typcollation;
12871 bool typdefault_is_literal = false;
12872
12874 {
12875 /* Set up query for domain-specific details */
12877 "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12878
12879 appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12880 "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12881 "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12882 "t.typdefault, "
12883 "CASE WHEN t.typcollation <> u.typcollation "
12884 "THEN t.typcollation ELSE 0 END AS typcollation "
12885 "FROM pg_catalog.pg_type t "
12886 "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12887 "WHERE t.oid = $1");
12888
12889 ExecuteSqlStatement(fout, query->data);
12890
12892 }
12893
12894 printfPQExpBuffer(query,
12895 "EXECUTE dumpDomain('%u')",
12896 tyinfo->dobj.catId.oid);
12897
12898 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12899
12900 typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12901 typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12902 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12903 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12904 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12905 {
12906 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12907 typdefault_is_literal = true; /* it needs quotes */
12908 }
12909 else
12910 typdefault = NULL;
12911 typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12912
12913 if (dopt->binary_upgrade)
12915 tyinfo->dobj.catId.oid,
12916 true, /* force array type */
12917 false); /* force multirange type */
12918
12919 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12921
12923 "CREATE DOMAIN %s AS %s",
12925 typdefn);
12926
12927 /* Print collation only if different from base type's collation */
12928 if (OidIsValid(typcollation))
12929 {
12930 CollInfo *coll;
12931
12932 coll = findCollationByOid(typcollation);
12933 if (coll)
12934 appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12935 }
12936
12937 /*
12938 * Print a not-null constraint if there's one. In servers older than 17
12939 * these don't have names, so just print it unadorned; in newer ones they
12940 * do, but most of the time it's going to be the standard generated one,
12941 * so omit the name in that case also.
12942 */
12943 if (typnotnull[0] == 't')
12944 {
12945 if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12946 appendPQExpBufferStr(q, " NOT NULL");
12947 else
12948 {
12949 ConstraintInfo *notnull = tyinfo->notnull;
12950
12951 if (!notnull->separate)
12952 {
12953 char *default_name;
12954
12955 /* XXX should match ChooseConstraintName better */
12956 default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12957
12958 if (strcmp(default_name, notnull->dobj.name) == 0)
12959 appendPQExpBufferStr(q, " NOT NULL");
12960 else
12961 appendPQExpBuffer(q, " CONSTRAINT %s %s",
12962 fmtId(notnull->dobj.name), notnull->condef);
12964 }
12965 }
12966 }
12967
12968 if (typdefault != NULL)
12969 {
12970 appendPQExpBufferStr(q, " DEFAULT ");
12973 else
12975 }
12976
12977 PQclear(res);
12978
12979 /*
12980 * Add any CHECK constraints for the domain
12981 */
12982 for (i = 0; i < tyinfo->nDomChecks; i++)
12983 {
12984 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12985
12986 if (!domcheck->separate && domcheck->contype == 'c')
12987 appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12988 fmtId(domcheck->dobj.name), domcheck->condef);
12989 }
12990
12991 appendPQExpBufferStr(q, ";\n");
12992
12993 appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12994
12995 if (dopt->binary_upgrade)
12997 "DOMAIN", qtypname,
12998 tyinfo->dobj.namespace->dobj.name);
12999
13000 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13001 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13002 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13003 .namespace = tyinfo->dobj.namespace->dobj.name,
13004 .owner = tyinfo->rolname,
13005 .description = "DOMAIN",
13006 .section = SECTION_PRE_DATA,
13007 .createStmt = q->data,
13008 .dropStmt = delq->data));
13009
13010 /* Dump Domain Comments and Security Labels */
13011 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13012 dumpComment(fout, "DOMAIN", qtypname,
13013 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13014 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13015
13016 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13017 dumpSecLabel(fout, "DOMAIN", qtypname,
13018 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13019 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13020
13021 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13022 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13023 qtypname, NULL,
13024 tyinfo->dobj.namespace->dobj.name,
13025 NULL, tyinfo->rolname, &tyinfo->dacl);
13026
13027 /* Dump any per-constraint comments */
13028 for (i = 0; i < tyinfo->nDomChecks; i++)
13029 {
13030 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
13032
13033 /* but only if the constraint itself was dumped here */
13034 if (domcheck->separate)
13035 continue;
13036
13038 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
13039 fmtId(domcheck->dobj.name));
13040
13041 if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
13043 tyinfo->dobj.namespace->dobj.name,
13044 tyinfo->rolname,
13045 domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
13046
13048 }
13049
13050 /*
13051 * And a comment on the not-null constraint, if there's one -- but only if
13052 * the constraint itself was dumped here
13053 */
13054 if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
13055 {
13057
13058 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
13059 fmtId(tyinfo->notnull->dobj.name));
13060
13061 if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
13063 tyinfo->dobj.namespace->dobj.name,
13064 tyinfo->rolname,
13065 tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
13067 }
13068
13071 destroyPQExpBuffer(query);
13072 free(qtypname);
13074}
13075
13076/*
13077 * dumpCompositeType
13078 * writes out to fout the queries to recreate a user-defined stand-alone
13079 * composite type
13080 */
13081static void
13083{
13084 DumpOptions *dopt = fout->dopt;
13086 PQExpBuffer dropped = createPQExpBuffer();
13089 PGresult *res;
13090 char *qtypname;
13091 char *qualtypname;
13092 int ntups;
13093 int i_attname;
13094 int i_atttypdefn;
13095 int i_attlen;
13096 int i_attalign;
13097 int i_attisdropped;
13098 int i_attcollation;
13099 int i;
13100 int actual_atts;
13101
13103 {
13104 /*
13105 * Set up query for type-specific details.
13106 *
13107 * Since we only want to dump COLLATE clauses for attributes whose
13108 * collation is different from their type's default, we use a CASE
13109 * here to suppress uninteresting attcollations cheaply. atttypid
13110 * will be 0 for dropped columns; collation does not matter for those.
13111 */
13113 "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
13114 "SELECT a.attname, a.attnum, "
13115 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
13116 "a.attlen, a.attalign, a.attisdropped, "
13117 "CASE WHEN a.attcollation <> at.typcollation "
13118 "THEN a.attcollation ELSE 0 END AS attcollation "
13119 "FROM pg_catalog.pg_type ct "
13120 "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
13121 "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
13122 "WHERE ct.oid = $1 "
13123 "ORDER BY a.attnum");
13124
13125 ExecuteSqlStatement(fout, query->data);
13126
13128 }
13129
13130 printfPQExpBuffer(query,
13131 "EXECUTE dumpCompositeType('%u')",
13132 tyinfo->dobj.catId.oid);
13133
13134 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13135
13136 ntups = PQntuples(res);
13137
13138 i_attname = PQfnumber(res, "attname");
13139 i_atttypdefn = PQfnumber(res, "atttypdefn");
13140 i_attlen = PQfnumber(res, "attlen");
13141 i_attalign = PQfnumber(res, "attalign");
13142 i_attisdropped = PQfnumber(res, "attisdropped");
13143 i_attcollation = PQfnumber(res, "attcollation");
13144
13145 if (dopt->binary_upgrade)
13146 {
13148 tyinfo->dobj.catId.oid,
13149 false, false);
13151 }
13152
13153 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
13155
13156 appendPQExpBuffer(q, "CREATE TYPE %s AS (",
13157 qualtypname);
13158
13159 actual_atts = 0;
13160 for (i = 0; i < ntups; i++)
13161 {
13162 char *attname;
13163 char *atttypdefn;
13164 char *attlen;
13165 char *attalign;
13166 bool attisdropped;
13167 Oid attcollation;
13168
13169 attname = PQgetvalue(res, i, i_attname);
13171 attlen = PQgetvalue(res, i, i_attlen);
13173 attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
13174 attcollation = atooid(PQgetvalue(res, i, i_attcollation));
13175
13176 if (attisdropped && !dopt->binary_upgrade)
13177 continue;
13178
13179 /* Format properly if not first attr */
13180 if (actual_atts++ > 0)
13181 appendPQExpBufferChar(q, ',');
13182 appendPQExpBufferStr(q, "\n\t");
13183
13184 if (!attisdropped)
13185 {
13187
13188 /* Add collation if not default for the column type */
13189 if (OidIsValid(attcollation))
13190 {
13191 CollInfo *coll;
13192
13193 coll = findCollationByOid(attcollation);
13194 if (coll)
13195 appendPQExpBuffer(q, " COLLATE %s",
13196 fmtQualifiedDumpable(coll));
13197 }
13198 }
13199 else
13200 {
13201 /*
13202 * This is a dropped attribute and we're in binary_upgrade mode.
13203 * Insert a placeholder for it in the CREATE TYPE command, and set
13204 * length and alignment with direct UPDATE to the catalogs
13205 * afterwards. See similar code in dumpTableSchema().
13206 */
13207 appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13208
13209 /* stash separately for insertion after the CREATE TYPE */
13210 appendPQExpBufferStr(dropped,
13211 "\n-- For binary upgrade, recreate dropped column.\n");
13212 appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13213 "SET attlen = %s, "
13214 "attalign = '%s', attbyval = false\n"
13215 "WHERE attname = ", attlen, attalign);
13217 appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13219 appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13220
13221 appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13222 qualtypname);
13223 appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13224 fmtId(attname));
13225 }
13226 }
13227 appendPQExpBufferStr(q, "\n);\n");
13228 appendPQExpBufferStr(q, dropped->data);
13229
13230 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13231
13232 if (dopt->binary_upgrade)
13234 "TYPE", qtypname,
13235 tyinfo->dobj.namespace->dobj.name);
13236
13237 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13238 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13239 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13240 .namespace = tyinfo->dobj.namespace->dobj.name,
13241 .owner = tyinfo->rolname,
13242 .description = "TYPE",
13243 .section = SECTION_PRE_DATA,
13244 .createStmt = q->data,
13245 .dropStmt = delq->data));
13246
13247
13248 /* Dump Type Comments and Security Labels */
13249 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13250 dumpComment(fout, "TYPE", qtypname,
13251 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13252 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13253
13254 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13255 dumpSecLabel(fout, "TYPE", qtypname,
13256 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13257 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13258
13259 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13260 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13261 qtypname, NULL,
13262 tyinfo->dobj.namespace->dobj.name,
13263 NULL, tyinfo->rolname, &tyinfo->dacl);
13264
13265 /* Dump any per-column comments */
13266 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13268
13269 PQclear(res);
13271 destroyPQExpBuffer(dropped);
13273 destroyPQExpBuffer(query);
13274 free(qtypname);
13276}
13277
13278/*
13279 * dumpCompositeTypeColComments
13280 * writes out to fout the queries to recreate comments on the columns of
13281 * a user-defined stand-alone composite type.
13282 *
13283 * The caller has already made a query to collect the names and attnums
13284 * of the type's columns, so we just pass that result into here rather
13285 * than reading them again.
13286 */
13287static void
13289 PGresult *res)
13290{
13292 int ncomments;
13293 PQExpBuffer query;
13294 PQExpBuffer target;
13295 int i;
13296 int ntups;
13297 int i_attname;
13298 int i_attnum;
13299 int i_attisdropped;
13300
13301 /* do nothing, if --no-comments is supplied */
13302 if (fout->dopt->no_comments)
13303 return;
13304
13305 /* Search for comments associated with type's pg_class OID */
13307 &comments);
13308
13309 /* If no comments exist, we're done */
13310 if (ncomments <= 0)
13311 return;
13312
13313 /* Build COMMENT ON statements */
13314 query = createPQExpBuffer();
13315 target = createPQExpBuffer();
13316
13317 ntups = PQntuples(res);
13318 i_attnum = PQfnumber(res, "attnum");
13319 i_attname = PQfnumber(res, "attname");
13320 i_attisdropped = PQfnumber(res, "attisdropped");
13321 while (ncomments > 0)
13322 {
13323 const char *attname;
13324
13325 attname = NULL;
13326 for (i = 0; i < ntups; i++)
13327 {
13328 if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13329 PQgetvalue(res, i, i_attisdropped)[0] != 't')
13330 {
13331 attname = PQgetvalue(res, i, i_attname);
13332 break;
13333 }
13334 }
13335 if (attname) /* just in case we don't find it */
13336 {
13337 const char *descr = comments->descr;
13338
13339 resetPQExpBuffer(target);
13340 appendPQExpBuffer(target, "COLUMN %s.",
13341 fmtId(tyinfo->dobj.name));
13343
13344 resetPQExpBuffer(query);
13345 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13347 appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13348 appendStringLiteralAH(query, descr, fout);
13349 appendPQExpBufferStr(query, ";\n");
13350
13352 ARCHIVE_OPTS(.tag = target->data,
13353 .namespace = tyinfo->dobj.namespace->dobj.name,
13354 .owner = tyinfo->rolname,
13355 .description = "COMMENT",
13356 .section = SECTION_NONE,
13357 .createStmt = query->data,
13358 .deps = &(tyinfo->dobj.dumpId),
13359 .nDeps = 1));
13360 }
13361
13362 comments++;
13363 ncomments--;
13364 }
13365
13366 destroyPQExpBuffer(query);
13367 destroyPQExpBuffer(target);
13368}
13369
13370/*
13371 * dumpShellType
13372 * writes out to fout the queries to create a shell type
13373 *
13374 * We dump a shell definition in advance of the I/O functions for the type.
13375 */
13376static void
13378{
13379 DumpOptions *dopt = fout->dopt;
13380 PQExpBuffer q;
13381
13382 /* Do nothing if not dumping schema */
13383 if (!dopt->dumpSchema)
13384 return;
13385
13386 q = createPQExpBuffer();
13387
13388 /*
13389 * Note the lack of a DROP command for the shell type; any required DROP
13390 * is driven off the base type entry, instead. This interacts with
13391 * _printTocEntry()'s use of the presence of a DROP command to decide
13392 * whether an entry needs an ALTER OWNER command. We don't want to alter
13393 * the shell type's owner immediately on creation; that should happen only
13394 * after it's filled in, otherwise the backend complains.
13395 */
13396
13397 if (dopt->binary_upgrade)
13399 stinfo->baseType->dobj.catId.oid,
13400 false, false);
13401
13402 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13404
13405 if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13406 ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13407 ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13408 .namespace = stinfo->dobj.namespace->dobj.name,
13409 .owner = stinfo->baseType->rolname,
13410 .description = "SHELL TYPE",
13411 .section = SECTION_PRE_DATA,
13412 .createStmt = q->data));
13413
13415}
13416
13417/*
13418 * dumpProcLang
13419 * writes out to fout the queries to recreate a user-defined
13420 * procedural language
13421 */
13422static void
13424{
13425 DumpOptions *dopt = fout->dopt;
13428 bool useParams;
13429 char *qlanname;
13433
13434 /* Do nothing if not dumping schema */
13435 if (!dopt->dumpSchema)
13436 return;
13437
13438 /*
13439 * Try to find the support function(s). It is not an error if we don't
13440 * find them --- if the functions are in the pg_catalog schema, as is
13441 * standard in 8.1 and up, then we won't have loaded them. (In this case
13442 * we will emit a parameterless CREATE LANGUAGE command, which will
13443 * require PL template knowledge in the backend to reload.)
13444 */
13445
13446 funcInfo = findFuncByOid(plang->lanplcallfoid);
13447 if (funcInfo != NULL && !funcInfo->dobj.dump)
13448 funcInfo = NULL; /* treat not-dumped same as not-found */
13449
13450 if (OidIsValid(plang->laninline))
13451 {
13452 inlineInfo = findFuncByOid(plang->laninline);
13453 if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13454 inlineInfo = NULL;
13455 }
13456
13457 if (OidIsValid(plang->lanvalidator))
13458 {
13459 validatorInfo = findFuncByOid(plang->lanvalidator);
13460 if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13462 }
13463
13464 /*
13465 * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13466 * parameters. Otherwise, we'll write a parameterless command, which will
13467 * be interpreted as CREATE EXTENSION.
13468 */
13469 useParams = (funcInfo != NULL &&
13470 (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13471 (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13472
13475
13476 qlanname = pg_strdup(fmtId(plang->dobj.name));
13477
13478 appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13479 qlanname);
13480
13481 if (useParams)
13482 {
13483 appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13484 plang->lanpltrusted ? "TRUSTED " : "",
13485 qlanname);
13486 appendPQExpBuffer(defqry, " HANDLER %s",
13488 if (OidIsValid(plang->laninline))
13489 appendPQExpBuffer(defqry, " INLINE %s",
13491 if (OidIsValid(plang->lanvalidator))
13492 appendPQExpBuffer(defqry, " VALIDATOR %s",
13494 }
13495 else
13496 {
13497 /*
13498 * If not dumping parameters, then use CREATE OR REPLACE so that the
13499 * command will not fail if the language is preinstalled in the target
13500 * database.
13501 *
13502 * Modern servers will interpret this as CREATE EXTENSION IF NOT
13503 * EXISTS; perhaps we should emit that instead? But it might just add
13504 * confusion.
13505 */
13506 appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13507 qlanname);
13508 }
13510
13511 if (dopt->binary_upgrade)
13513 "LANGUAGE", qlanname, NULL);
13514
13515 if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13516 ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13517 ARCHIVE_OPTS(.tag = plang->dobj.name,
13518 .owner = plang->lanowner,
13519 .description = "PROCEDURAL LANGUAGE",
13520 .section = SECTION_PRE_DATA,
13521 .createStmt = defqry->data,
13522 .dropStmt = delqry->data,
13523 ));
13524
13525 /* Dump Proc Lang Comments and Security Labels */
13526 if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13527 dumpComment(fout, "LANGUAGE", qlanname,
13528 NULL, plang->lanowner,
13529 plang->dobj.catId, 0, plang->dobj.dumpId);
13530
13531 if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13532 dumpSecLabel(fout, "LANGUAGE", qlanname,
13533 NULL, plang->lanowner,
13534 plang->dobj.catId, 0, plang->dobj.dumpId);
13535
13536 if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13537 dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13538 qlanname, NULL, NULL,
13539 NULL, plang->lanowner, &plang->dacl);
13540
13541 free(qlanname);
13542
13545}
13546
13547/*
13548 * format_function_arguments: generate function name and argument list
13549 *
13550 * This is used when we can rely on pg_get_function_arguments to format
13551 * the argument list. Note, however, that pg_get_function_arguments
13552 * does not special-case zero-argument aggregates.
13553 */
13554static char *
13555format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13556{
13558
13561 if (is_agg && finfo->nargs == 0)
13562 appendPQExpBufferStr(&fn, "(*)");
13563 else
13564 appendPQExpBuffer(&fn, "(%s)", funcargs);
13565 return fn.data;
13566}
13567
13568/*
13569 * format_function_signature: generate function name and argument list
13570 *
13571 * Only a minimal list of input argument types is generated; this is
13572 * sufficient to reference the function, but not to define it.
13573 *
13574 * If honor_quotes is false then the function name is never quoted.
13575 * This is appropriate for use in TOC tags, but not in SQL commands.
13576 */
13577static char *
13579{
13581 int j;
13582
13584 if (honor_quotes)
13585 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13586 else
13587 appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13588 for (j = 0; j < finfo->nargs; j++)
13589 {
13590 if (j > 0)
13591 appendPQExpBufferStr(&fn, ", ");
13592
13595 zeroIsError));
13596 }
13598 return fn.data;
13599}
13600
13601
13602/*
13603 * dumpFunc:
13604 * dump out one function
13605 */
13606static void
13607dumpFunc(Archive *fout, const FuncInfo *finfo)
13608{
13609 DumpOptions *dopt = fout->dopt;
13610 PQExpBuffer query;
13611 PQExpBuffer q;
13614 PGresult *res;
13615 char *funcsig; /* identity signature */
13616 char *funcfullsig = NULL; /* full signature */
13617 char *funcsig_tag;
13618 char *qual_funcsig;
13619 char *proretset;
13620 char *prosrc;
13621 char *probin;
13622 char *prosqlbody;
13623 char *funcargs;
13624 char *funciargs;
13625 char *funcresult;
13626 char *protrftypes;
13627 char *prokind;
13628 char *provolatile;
13629 char *proisstrict;
13630 char *prosecdef;
13631 char *proleakproof;
13632 char *proconfig;
13633 char *procost;
13634 char *prorows;
13635 char *prosupport;
13636 char *proparallel;
13637 char *lanname;
13638 char **configitems = NULL;
13639 int nconfigitems = 0;
13640 const char *keyword;
13641
13642 /* Do nothing if not dumping schema */
13643 if (!dopt->dumpSchema)
13644 return;
13645
13646 query = createPQExpBuffer();
13647 q = createPQExpBuffer();
13650
13652 {
13653 /* Set up query for function-specific details */
13655 "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13656
13658 "SELECT\n"
13659 "proretset,\n"
13660 "prosrc,\n"
13661 "probin,\n"
13662 "provolatile,\n"
13663 "proisstrict,\n"
13664 "prosecdef,\n"
13665 "lanname,\n"
13666 "proconfig,\n"
13667 "procost,\n"
13668 "prorows,\n"
13669 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13670 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13671 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13672 "proleakproof,\n");
13673
13674 if (fout->remoteVersion >= 90500)
13676 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13677 else
13679 "NULL AS protrftypes,\n");
13680
13681 if (fout->remoteVersion >= 90600)
13683 "proparallel,\n");
13684 else
13686 "'u' AS proparallel,\n");
13687
13688 if (fout->remoteVersion >= 110000)
13690 "prokind,\n");
13691 else
13693 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13694
13695 if (fout->remoteVersion >= 120000)
13697 "prosupport,\n");
13698 else
13700 "'-' AS prosupport,\n");
13701
13702 if (fout->remoteVersion >= 140000)
13704 "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13705 else
13707 "NULL AS prosqlbody\n");
13708
13710 "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13711 "WHERE p.oid = $1 "
13712 "AND l.oid = p.prolang");
13713
13714 ExecuteSqlStatement(fout, query->data);
13715
13717 }
13718
13719 printfPQExpBuffer(query,
13720 "EXECUTE dumpFunc('%u')",
13721 finfo->dobj.catId.oid);
13722
13723 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13724
13725 proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13726 if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13727 {
13728 prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13729 probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13730 prosqlbody = NULL;
13731 }
13732 else
13733 {
13734 prosrc = NULL;
13735 probin = NULL;
13736 prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13737 }
13738 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13739 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13740 funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13741 protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13742 prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13743 provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13744 proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13745 prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13746 proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13747 proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13748 procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13749 prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13750 prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13751 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13752 lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13753
13754 /*
13755 * See backend/commands/functioncmds.c for details of how the 'AS' clause
13756 * is used.
13757 */
13758 if (prosqlbody)
13759 {
13761 }
13762 else if (probin[0] != '\0')
13763 {
13766 if (prosrc[0] != '\0')
13767 {
13769
13770 /*
13771 * where we have bin, use dollar quoting if allowed and src
13772 * contains quote or backslash; else use regular quoting.
13773 */
13774 if (dopt->disable_dollar_quoting ||
13775 (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13777 else
13779 }
13780 }
13781 else
13782 {
13784 /* with no bin, dollar quote src unconditionally if allowed */
13785 if (dopt->disable_dollar_quoting)
13787 else
13789 }
13790
13791 if (*proconfig)
13792 {
13794 pg_fatal("could not parse %s array", "proconfig");
13795 }
13796 else
13797 {
13798 configitems = NULL;
13799 nconfigitems = 0;
13800 }
13801
13804
13806
13807 qual_funcsig = psprintf("%s.%s",
13808 fmtId(finfo->dobj.namespace->dobj.name),
13809 funcsig);
13810
13811 if (prokind[0] == PROKIND_PROCEDURE)
13812 keyword = "PROCEDURE";
13813 else
13814 keyword = "FUNCTION"; /* works for window functions too */
13815
13816 appendPQExpBuffer(delqry, "DROP %s %s;\n",
13817 keyword, qual_funcsig);
13818
13819 appendPQExpBuffer(q, "CREATE %s %s.%s",
13820 keyword,
13821 fmtId(finfo->dobj.namespace->dobj.name),
13823 funcsig);
13824
13825 if (prokind[0] == PROKIND_PROCEDURE)
13826 /* no result type to output */ ;
13827 else if (funcresult)
13828 appendPQExpBuffer(q, " RETURNS %s", funcresult);
13829 else
13830 appendPQExpBuffer(q, " RETURNS %s%s",
13831 (proretset[0] == 't') ? "SETOF " : "",
13832 getFormattedTypeName(fout, finfo->prorettype,
13833 zeroIsError));
13834
13835 appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13836
13837 if (*protrftypes)
13838 {
13840 int i;
13841
13842 appendPQExpBufferStr(q, " TRANSFORM ");
13844 for (i = 0; typeids[i]; i++)
13845 {
13846 if (i != 0)
13847 appendPQExpBufferStr(q, ", ");
13848 appendPQExpBuffer(q, "FOR TYPE %s",
13850 }
13851
13852 free(typeids);
13853 }
13854
13855 if (prokind[0] == PROKIND_WINDOW)
13856 appendPQExpBufferStr(q, " WINDOW");
13857
13859 {
13861 appendPQExpBufferStr(q, " IMMUTABLE");
13862 else if (provolatile[0] == PROVOLATILE_STABLE)
13863 appendPQExpBufferStr(q, " STABLE");
13864 else if (provolatile[0] != PROVOLATILE_VOLATILE)
13865 pg_fatal("unrecognized provolatile value for function \"%s\"",
13866 finfo->dobj.name);
13867 }
13868
13869 if (proisstrict[0] == 't')
13870 appendPQExpBufferStr(q, " STRICT");
13871
13872 if (prosecdef[0] == 't')
13873 appendPQExpBufferStr(q, " SECURITY DEFINER");
13874
13875 if (proleakproof[0] == 't')
13876 appendPQExpBufferStr(q, " LEAKPROOF");
13877
13878 /*
13879 * COST and ROWS are emitted only if present and not default, so as not to
13880 * break backwards-compatibility of the dump without need. Keep this code
13881 * in sync with the defaults in functioncmds.c.
13882 */
13883 if (strcmp(procost, "0") != 0)
13884 {
13885 if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13886 {
13887 /* default cost is 1 */
13888 if (strcmp(procost, "1") != 0)
13889 appendPQExpBuffer(q, " COST %s", procost);
13890 }
13891 else
13892 {
13893 /* default cost is 100 */
13894 if (strcmp(procost, "100") != 0)
13895 appendPQExpBuffer(q, " COST %s", procost);
13896 }
13897 }
13898 if (proretset[0] == 't' &&
13899 strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13900 appendPQExpBuffer(q, " ROWS %s", prorows);
13901
13902 if (strcmp(prosupport, "-") != 0)
13903 {
13904 /* We rely on regprocout to provide quoting and qualification */
13905 appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13906 }
13907
13909 {
13910 if (proparallel[0] == PROPARALLEL_SAFE)
13911 appendPQExpBufferStr(q, " PARALLEL SAFE");
13912 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13913 appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13914 else if (proparallel[0] != PROPARALLEL_UNSAFE)
13915 pg_fatal("unrecognized proparallel value for function \"%s\"",
13916 finfo->dobj.name);
13917 }
13918
13919 for (int i = 0; i < nconfigitems; i++)
13920 {
13921 /* we feel free to scribble on configitems[] here */
13922 char *configitem = configitems[i];
13923 char *pos;
13924
13925 pos = strchr(configitem, '=');
13926 if (pos == NULL)
13927 continue;
13928 *pos++ = '\0';
13929 appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13930
13931 /*
13932 * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13933 * by flatten_set_variable_args() before they were put into the
13934 * proconfig array. However, because the quoting rules used there
13935 * aren't exactly like SQL's, we have to break the list value apart
13936 * and then quote the elements as string literals. (The elements may
13937 * be double-quoted as-is, but we can't just feed them to the SQL
13938 * parser; it would do the wrong thing with elements that are
13939 * zero-length or longer than NAMEDATALEN.) Also, we need a special
13940 * case for empty lists.
13941 *
13942 * Variables that are not so marked should just be emitted as simple
13943 * string literals. If the variable is not known to
13944 * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13945 * to use GUC_LIST_QUOTE for extension variables.
13946 */
13948 {
13949 char **namelist;
13950 char **nameptr;
13951
13952 /* Parse string into list of identifiers */
13953 /* this shouldn't fail really */
13954 if (SplitGUCList(pos, ',', &namelist))
13955 {
13956 /* Special case: represent an empty list as NULL */
13957 if (*namelist == NULL)
13958 appendPQExpBufferStr(q, "NULL");
13959 for (nameptr = namelist; *nameptr; nameptr++)
13960 {
13961 if (nameptr != namelist)
13962 appendPQExpBufferStr(q, ", ");
13964 }
13965 }
13967 }
13968 else
13969 appendStringLiteralAH(q, pos, fout);
13970 }
13971
13972 appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13973
13975 "pg_catalog.pg_proc", keyword,
13976 qual_funcsig);
13977
13978 if (dopt->binary_upgrade)
13980 keyword, funcsig,
13981 finfo->dobj.namespace->dobj.name);
13982
13983 if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13984 ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13986 .namespace = finfo->dobj.namespace->dobj.name,
13987 .owner = finfo->rolname,
13988 .description = keyword,
13989 .section = finfo->postponed_def ?
13991 .createStmt = q->data,
13992 .dropStmt = delqry->data));
13993
13994 /* Dump Function Comments and Security Labels */
13995 if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13996 dumpComment(fout, keyword, funcsig,
13997 finfo->dobj.namespace->dobj.name, finfo->rolname,
13998 finfo->dobj.catId, 0, finfo->dobj.dumpId);
13999
14000 if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
14001 dumpSecLabel(fout, keyword, funcsig,
14002 finfo->dobj.namespace->dobj.name, finfo->rolname,
14003 finfo->dobj.catId, 0, finfo->dobj.dumpId);
14004
14005 if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
14006 dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
14007 funcsig, NULL,
14008 finfo->dobj.namespace->dobj.name,
14009 NULL, finfo->rolname, &finfo->dacl);
14010
14011 PQclear(res);
14012
14013 destroyPQExpBuffer(query);
14017 free(funcsig);
14022}
14023
14024
14025/*
14026 * Dump a user-defined cast
14027 */
14028static void
14030{
14031 DumpOptions *dopt = fout->dopt;
14037 const char *sourceType;
14038 const char *targetType;
14039
14040 /* Do nothing if not dumping schema */
14041 if (!dopt->dumpSchema)
14042 return;
14043
14044 /* Cannot dump if we don't have the cast function's info */
14045 if (OidIsValid(cast->castfunc))
14046 {
14047 funcInfo = findFuncByOid(cast->castfunc);
14048 if (funcInfo == NULL)
14049 pg_fatal("could not find function definition for function with OID %u",
14050 cast->castfunc);
14051 }
14052
14057
14060 appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
14062
14063 appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
14065
14066 switch (cast->castmethod)
14067 {
14069 appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
14070 break;
14072 appendPQExpBufferStr(defqry, "WITH INOUT");
14073 break;
14075 if (funcInfo)
14076 {
14078
14079 /*
14080 * Always qualify the function name (format_function_signature
14081 * won't qualify it).
14082 */
14083 appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
14084 fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
14085 free(fsig);
14086 }
14087 else
14088 pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
14089 break;
14090 default:
14091 pg_log_warning("bogus value in pg_cast.castmethod field");
14092 }
14093
14094 if (cast->castcontext == 'a')
14095 appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
14096 else if (cast->castcontext == 'i')
14097 appendPQExpBufferStr(defqry, " AS IMPLICIT");
14099
14100 appendPQExpBuffer(labelq, "CAST (%s AS %s)",
14102
14103 appendPQExpBuffer(castargs, "(%s AS %s)",
14105
14106 if (dopt->binary_upgrade)
14108 "CAST", castargs->data, NULL);
14109
14110 if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
14111 ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
14112 ARCHIVE_OPTS(.tag = labelq->data,
14113 .description = "CAST",
14114 .section = SECTION_PRE_DATA,
14115 .createStmt = defqry->data,
14116 .dropStmt = delqry->data));
14117
14118 /* Dump Cast Comments */
14119 if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
14120 dumpComment(fout, "CAST", castargs->data,
14121 NULL, "",
14122 cast->dobj.catId, 0, cast->dobj.dumpId);
14123
14128}
14129
14130/*
14131 * Dump a transform
14132 */
14133static void
14134dumpTransform(Archive *fout, const TransformInfo *transform)
14135{
14136 DumpOptions *dopt = fout->dopt;
14143 char *lanname;
14144 const char *transformType;
14145
14146 /* Do nothing if not dumping schema */
14147 if (!dopt->dumpSchema)
14148 return;
14149
14150 /* Cannot dump if we don't have the transform functions' info */
14151 if (OidIsValid(transform->trffromsql))
14152 {
14154 if (fromsqlFuncInfo == NULL)
14155 pg_fatal("could not find function definition for function with OID %u",
14156 transform->trffromsql);
14157 }
14158 if (OidIsValid(transform->trftosql))
14159 {
14160 tosqlFuncInfo = findFuncByOid(transform->trftosql);
14161 if (tosqlFuncInfo == NULL)
14162 pg_fatal("could not find function definition for function with OID %u",
14163 transform->trftosql);
14164 }
14165
14170
14171 lanname = get_language_name(fout, transform->trflang);
14173
14174 appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
14176
14177 appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
14179
14180 if (!transform->trffromsql && !transform->trftosql)
14181 pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
14182
14183 if (transform->trffromsql)
14184 {
14185 if (fromsqlFuncInfo)
14186 {
14188
14189 /*
14190 * Always qualify the function name (format_function_signature
14191 * won't qualify it).
14192 */
14193 appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14194 fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14195 free(fsig);
14196 }
14197 else
14198 pg_log_warning("bogus value in pg_transform.trffromsql field");
14199 }
14200
14201 if (transform->trftosql)
14202 {
14203 if (transform->trffromsql)
14205
14206 if (tosqlFuncInfo)
14207 {
14209
14210 /*
14211 * Always qualify the function name (format_function_signature
14212 * won't qualify it).
14213 */
14214 appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14215 fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14216 free(fsig);
14217 }
14218 else
14219 pg_log_warning("bogus value in pg_transform.trftosql field");
14220 }
14221
14223
14224 appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14226
14227 appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14229
14230 if (dopt->binary_upgrade)
14232 "TRANSFORM", transformargs->data, NULL);
14233
14234 if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14235 ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14236 ARCHIVE_OPTS(.tag = labelq->data,
14237 .description = "TRANSFORM",
14238 .section = SECTION_PRE_DATA,
14239 .createStmt = defqry->data,
14240 .dropStmt = delqry->data,
14241 .deps = transform->dobj.dependencies,
14242 .nDeps = transform->dobj.nDeps));
14243
14244 /* Dump Transform Comments */
14245 if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14246 dumpComment(fout, "TRANSFORM", transformargs->data,
14247 NULL, "",
14248 transform->dobj.catId, 0, transform->dobj.dumpId);
14249
14250 free(lanname);
14255}
14256
14257
14258/*
14259 * dumpOpr
14260 * write out a single operator definition
14261 */
14262static void
14264{
14265 DumpOptions *dopt = fout->dopt;
14266 PQExpBuffer query;
14267 PQExpBuffer q;
14270 PQExpBuffer details;
14271 PGresult *res;
14272 int i_oprkind;
14273 int i_oprcode;
14274 int i_oprleft;
14275 int i_oprright;
14276 int i_oprcom;
14277 int i_oprnegate;
14278 int i_oprrest;
14279 int i_oprjoin;
14280 int i_oprcanmerge;
14281 int i_oprcanhash;
14282 char *oprkind;
14283 char *oprcode;
14284 char *oprleft;
14285 char *oprright;
14286 char *oprcom;
14287 char *oprnegate;
14288 char *oprrest;
14289 char *oprjoin;
14290 char *oprcanmerge;
14291 char *oprcanhash;
14292 char *oprregproc;
14293 char *oprref;
14294
14295 /* Do nothing if not dumping schema */
14296 if (!dopt->dumpSchema)
14297 return;
14298
14299 /*
14300 * some operators are invalid because they were the result of user
14301 * defining operators before commutators exist
14302 */
14303 if (!OidIsValid(oprinfo->oprcode))
14304 return;
14305
14306 query = createPQExpBuffer();
14307 q = createPQExpBuffer();
14310 details = createPQExpBuffer();
14311
14313 {
14314 /* Set up query for operator-specific details */
14316 "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14317 "SELECT oprkind, "
14318 "oprcode::pg_catalog.regprocedure, "
14319 "oprleft::pg_catalog.regtype, "
14320 "oprright::pg_catalog.regtype, "
14321 "oprcom, "
14322 "oprnegate, "
14323 "oprrest::pg_catalog.regprocedure, "
14324 "oprjoin::pg_catalog.regprocedure, "
14325 "oprcanmerge, oprcanhash "
14326 "FROM pg_catalog.pg_operator "
14327 "WHERE oid = $1");
14328
14329 ExecuteSqlStatement(fout, query->data);
14330
14332 }
14333
14334 printfPQExpBuffer(query,
14335 "EXECUTE dumpOpr('%u')",
14336 oprinfo->dobj.catId.oid);
14337
14338 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14339
14340 i_oprkind = PQfnumber(res, "oprkind");
14341 i_oprcode = PQfnumber(res, "oprcode");
14342 i_oprleft = PQfnumber(res, "oprleft");
14343 i_oprright = PQfnumber(res, "oprright");
14344 i_oprcom = PQfnumber(res, "oprcom");
14345 i_oprnegate = PQfnumber(res, "oprnegate");
14346 i_oprrest = PQfnumber(res, "oprrest");
14347 i_oprjoin = PQfnumber(res, "oprjoin");
14348 i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14349 i_oprcanhash = PQfnumber(res, "oprcanhash");
14350
14351 oprkind = PQgetvalue(res, 0, i_oprkind);
14352 oprcode = PQgetvalue(res, 0, i_oprcode);
14353 oprleft = PQgetvalue(res, 0, i_oprleft);
14354 oprright = PQgetvalue(res, 0, i_oprright);
14355 oprcom = PQgetvalue(res, 0, i_oprcom);
14356 oprnegate = PQgetvalue(res, 0, i_oprnegate);
14357 oprrest = PQgetvalue(res, 0, i_oprrest);
14358 oprjoin = PQgetvalue(res, 0, i_oprjoin);
14361
14362 /* In PG14 upwards postfix operator support does not exist anymore. */
14363 if (strcmp(oprkind, "r") == 0)
14364 pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14365 oprcode);
14366
14368 if (oprregproc)
14369 {
14370 appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14372 }
14373
14374 appendPQExpBuffer(oprid, "%s (",
14375 oprinfo->dobj.name);
14376
14377 /*
14378 * right unary means there's a left arg and left unary means there's a
14379 * right arg. (Although the "r" case is dead code for PG14 and later,
14380 * continue to support it in case we're dumping from an old server.)
14381 */
14382 if (strcmp(oprkind, "r") == 0 ||
14383 strcmp(oprkind, "b") == 0)
14384 {
14385 appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14386 appendPQExpBufferStr(oprid, oprleft);
14387 }
14388 else
14389 appendPQExpBufferStr(oprid, "NONE");
14390
14391 if (strcmp(oprkind, "l") == 0 ||
14392 strcmp(oprkind, "b") == 0)
14393 {
14394 appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14395 appendPQExpBuffer(oprid, ", %s)", oprright);
14396 }
14397 else
14398 appendPQExpBufferStr(oprid, ", NONE)");
14399
14401 if (oprref)
14402 {
14403 appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14404 free(oprref);
14405 }
14406
14408 if (oprref)
14409 {
14410 appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14411 free(oprref);
14412 }
14413
14414 if (strcmp(oprcanmerge, "t") == 0)
14415 appendPQExpBufferStr(details, ",\n MERGES");
14416
14417 if (strcmp(oprcanhash, "t") == 0)
14418 appendPQExpBufferStr(details, ",\n HASHES");
14419
14421 if (oprregproc)
14422 {
14423 appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14425 }
14426
14428 if (oprregproc)
14429 {
14430 appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14432 }
14433
14434 appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14435 fmtId(oprinfo->dobj.namespace->dobj.name),
14436 oprid->data);
14437
14438 appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14439 fmtId(oprinfo->dobj.namespace->dobj.name),
14440 oprinfo->dobj.name, details->data);
14441
14442 if (dopt->binary_upgrade)
14444 "OPERATOR", oprid->data,
14445 oprinfo->dobj.namespace->dobj.name);
14446
14447 if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14448 ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14449 ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14450 .namespace = oprinfo->dobj.namespace->dobj.name,
14451 .owner = oprinfo->rolname,
14452 .description = "OPERATOR",
14453 .section = SECTION_PRE_DATA,
14454 .createStmt = q->data,
14455 .dropStmt = delq->data));
14456
14457 /* Dump Operator Comments */
14458 if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14459 dumpComment(fout, "OPERATOR", oprid->data,
14460 oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14461 oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14462
14463 PQclear(res);
14464
14465 destroyPQExpBuffer(query);
14469 destroyPQExpBuffer(details);
14470}
14471
14472/*
14473 * Convert a function reference obtained from pg_operator
14474 *
14475 * Returns allocated string of what to print, or NULL if function references
14476 * is InvalidOid. Returned string is expected to be free'd by the caller.
14477 *
14478 * The input is a REGPROCEDURE display; we have to strip the argument-types
14479 * part.
14480 */
14481static char *
14482convertRegProcReference(const char *proc)
14483{
14484 char *name;
14485 char *paren;
14486 bool inquote;
14487
14488 /* In all cases "-" means a null reference */
14489 if (strcmp(proc, "-") == 0)
14490 return NULL;
14491
14492 name = pg_strdup(proc);
14493 /* find non-double-quoted left paren */
14494 inquote = false;
14495 for (paren = name; *paren; paren++)
14496 {
14497 if (*paren == '(' && !inquote)
14498 {
14499 *paren = '\0';
14500 break;
14501 }
14502 if (*paren == '"')
14503 inquote = !inquote;
14504 }
14505 return name;
14506}
14507
14508/*
14509 * getFormattedOperatorName - retrieve the operator name for the
14510 * given operator OID (presented in string form).
14511 *
14512 * Returns an allocated string, or NULL if the given OID is invalid.
14513 * Caller is responsible for free'ing result string.
14514 *
14515 * What we produce has the format "OPERATOR(schema.oprname)". This is only
14516 * useful in commands where the operator's argument types can be inferred from
14517 * context. We always schema-qualify the name, though. The predecessor to
14518 * this code tried to skip the schema qualification if possible, but that led
14519 * to wrong results in corner cases, such as if an operator and its negator
14520 * are in different schemas.
14521 */
14522static char *
14524{
14526
14527 /* In all cases "0" means a null reference */
14528 if (strcmp(oproid, "0") == 0)
14529 return NULL;
14530
14532 if (oprInfo == NULL)
14533 {
14534 pg_log_warning("could not find operator with OID %s",
14535 oproid);
14536 return NULL;
14537 }
14538
14539 return psprintf("OPERATOR(%s.%s)",
14540 fmtId(oprInfo->dobj.namespace->dobj.name),
14541 oprInfo->dobj.name);
14542}
14543
14544/*
14545 * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14546 *
14547 * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14548 * argument lists of these functions are predetermined. Note that the
14549 * caller should ensure we are in the proper schema, because the results
14550 * are search path dependent!
14551 */
14552static char *
14554{
14555 char *result;
14556 char query[128];
14557 PGresult *res;
14558
14559 snprintf(query, sizeof(query),
14560 "SELECT '%u'::pg_catalog.regproc", funcOid);
14561 res = ExecuteSqlQueryForSingleRow(fout, query);
14562
14563 result = pg_strdup(PQgetvalue(res, 0, 0));
14564
14565 PQclear(res);
14566
14567 return result;
14568}
14569
14570/*
14571 * dumpAccessMethod
14572 * write out a single access method definition
14573 */
14574static void
14576{
14577 DumpOptions *dopt = fout->dopt;
14578 PQExpBuffer q;
14580 char *qamname;
14581
14582 /* Do nothing if not dumping schema */
14583 if (!dopt->dumpSchema)
14584 return;
14585
14586 q = createPQExpBuffer();
14588
14589 qamname = pg_strdup(fmtId(aminfo->dobj.name));
14590
14591 appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14592
14593 switch (aminfo->amtype)
14594 {
14595 case AMTYPE_INDEX:
14596 appendPQExpBufferStr(q, "TYPE INDEX ");
14597 break;
14598 case AMTYPE_TABLE:
14599 appendPQExpBufferStr(q, "TYPE TABLE ");
14600 break;
14601 default:
14602 pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14603 aminfo->amtype, qamname);
14606 free(qamname);
14607 return;
14608 }
14609
14610 appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14611
14612 appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14613 qamname);
14614
14615 if (dopt->binary_upgrade)
14617 "ACCESS METHOD", qamname, NULL);
14618
14619 if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14620 ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14621 ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14622 .description = "ACCESS METHOD",
14623 .section = SECTION_PRE_DATA,
14624 .createStmt = q->data,
14625 .dropStmt = delq->data));
14626
14627 /* Dump Access Method Comments */
14628 if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14629 dumpComment(fout, "ACCESS METHOD", qamname,
14630 NULL, "",
14631 aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14632
14635 free(qamname);
14636}
14637
14638/*
14639 * dumpOpclass
14640 * write out a single operator class definition
14641 */
14642static void
14644{
14645 DumpOptions *dopt = fout->dopt;
14646 PQExpBuffer query;
14647 PQExpBuffer q;
14650 PGresult *res;
14651 int ntups;
14652 int i_opcintype;
14653 int i_opckeytype;
14654 int i_opcdefault;
14655 int i_opcfamily;
14656 int i_opcfamilyname;
14657 int i_opcfamilynsp;
14658 int i_amname;
14659 int i_amopstrategy;
14660 int i_amopopr;
14661 int i_sortfamily;
14662 int i_sortfamilynsp;
14663 int i_amprocnum;
14664 int i_amproc;
14665 int i_amproclefttype;
14667 char *opcintype;
14668 char *opckeytype;
14669 char *opcdefault;
14670 char *opcfamily;
14671 char *opcfamilyname;
14672 char *opcfamilynsp;
14673 char *amname;
14674 char *amopstrategy;
14675 char *amopopr;
14676 char *sortfamily;
14677 char *sortfamilynsp;
14678 char *amprocnum;
14679 char *amproc;
14680 char *amproclefttype;
14681 char *amprocrighttype;
14682 bool needComma;
14683 int i;
14684
14685 /* Do nothing if not dumping schema */
14686 if (!dopt->dumpSchema)
14687 return;
14688
14689 query = createPQExpBuffer();
14690 q = createPQExpBuffer();
14693
14694 /* Get additional fields from the pg_opclass row */
14695 appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14696 "opckeytype::pg_catalog.regtype, "
14697 "opcdefault, opcfamily, "
14698 "opfname AS opcfamilyname, "
14699 "nspname AS opcfamilynsp, "
14700 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14701 "FROM pg_catalog.pg_opclass c "
14702 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14703 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14704 "WHERE c.oid = '%u'::pg_catalog.oid",
14705 opcinfo->dobj.catId.oid);
14706
14707 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14708
14709 i_opcintype = PQfnumber(res, "opcintype");
14710 i_opckeytype = PQfnumber(res, "opckeytype");
14711 i_opcdefault = PQfnumber(res, "opcdefault");
14712 i_opcfamily = PQfnumber(res, "opcfamily");
14713 i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14714 i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14715 i_amname = PQfnumber(res, "amname");
14716
14717 /* opcintype may still be needed after we PQclear res */
14718 opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14721 /* opcfamily will still be needed after we PQclear res */
14722 opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14725 /* amname will still be needed after we PQclear res */
14726 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14727
14728 appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14730 appendPQExpBuffer(delq, " USING %s;\n",
14731 fmtId(amname));
14732
14733 /* Build the fixed portion of the CREATE command */
14734 appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14736 if (strcmp(opcdefault, "t") == 0)
14737 appendPQExpBufferStr(q, "DEFAULT ");
14738 appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14739 opcintype,
14740 fmtId(amname));
14741 if (strlen(opcfamilyname) > 0)
14742 {
14743 appendPQExpBufferStr(q, " FAMILY ");
14746 }
14747 appendPQExpBufferStr(q, " AS\n ");
14748
14749 needComma = false;
14750
14751 if (strcmp(opckeytype, "-") != 0)
14752 {
14753 appendPQExpBuffer(q, "STORAGE %s",
14754 opckeytype);
14755 needComma = true;
14756 }
14757
14758 PQclear(res);
14759
14760 /*
14761 * Now fetch and print the OPERATOR entries (pg_amop rows).
14762 *
14763 * Print only those opfamily members that are tied to the opclass by
14764 * pg_depend entries.
14765 */
14766 resetPQExpBuffer(query);
14767 appendPQExpBuffer(query, "SELECT amopstrategy, "
14768 "amopopr::pg_catalog.regoperator, "
14769 "opfname AS sortfamily, "
14770 "nspname AS sortfamilynsp "
14771 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14772 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14773 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14774 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14775 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14776 "AND refobjid = '%u'::pg_catalog.oid "
14777 "AND amopfamily = '%s'::pg_catalog.oid "
14778 "ORDER BY amopstrategy",
14779 opcinfo->dobj.catId.oid,
14780 opcfamily);
14781
14782 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14783
14784 ntups = PQntuples(res);
14785
14786 i_amopstrategy = PQfnumber(res, "amopstrategy");
14787 i_amopopr = PQfnumber(res, "amopopr");
14788 i_sortfamily = PQfnumber(res, "sortfamily");
14789 i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14790
14791 for (i = 0; i < ntups; i++)
14792 {
14794 amopopr = PQgetvalue(res, i, i_amopopr);
14795 sortfamily = PQgetvalue(res, i, i_sortfamily);
14797
14798 if (needComma)
14799 appendPQExpBufferStr(q, " ,\n ");
14800
14801 appendPQExpBuffer(q, "OPERATOR %s %s",
14803
14804 if (strlen(sortfamily) > 0)
14805 {
14806 appendPQExpBufferStr(q, " FOR ORDER BY ");
14808 appendPQExpBufferStr(q, fmtId(sortfamily));
14809 }
14810
14811 needComma = true;
14812 }
14813
14814 PQclear(res);
14815
14816 /*
14817 * Now fetch and print the FUNCTION entries (pg_amproc rows).
14818 *
14819 * Print only those opfamily members that are tied to the opclass by
14820 * pg_depend entries.
14821 *
14822 * We print the amproclefttype/amprocrighttype even though in most cases
14823 * the backend could deduce the right values, because of the corner case
14824 * of a btree sort support function for a cross-type comparison.
14825 */
14826 resetPQExpBuffer(query);
14827
14828 appendPQExpBuffer(query, "SELECT amprocnum, "
14829 "amproc::pg_catalog.regprocedure, "
14830 "amproclefttype::pg_catalog.regtype, "
14831 "amprocrighttype::pg_catalog.regtype "
14832 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14833 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14834 "AND refobjid = '%u'::pg_catalog.oid "
14835 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14836 "AND objid = ap.oid "
14837 "ORDER BY amprocnum",
14838 opcinfo->dobj.catId.oid);
14839
14840 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14841
14842 ntups = PQntuples(res);
14843
14844 i_amprocnum = PQfnumber(res, "amprocnum");
14845 i_amproc = PQfnumber(res, "amproc");
14846 i_amproclefttype = PQfnumber(res, "amproclefttype");
14847 i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14848
14849 for (i = 0; i < ntups; i++)
14850 {
14852 amproc = PQgetvalue(res, i, i_amproc);
14855
14856 if (needComma)
14857 appendPQExpBufferStr(q, " ,\n ");
14858
14859 appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14860
14863
14864 appendPQExpBuffer(q, " %s", amproc);
14865
14866 needComma = true;
14867 }
14868
14869 PQclear(res);
14870
14871 /*
14872 * If needComma is still false it means we haven't added anything after
14873 * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14874 * clause with the same datatype. This isn't sanctioned by the
14875 * documentation, but actually DefineOpClass will treat it as a no-op.
14876 */
14877 if (!needComma)
14878 appendPQExpBuffer(q, "STORAGE %s", opcintype);
14879
14880 appendPQExpBufferStr(q, ";\n");
14881
14883 appendPQExpBuffer(nameusing, " USING %s",
14884 fmtId(amname));
14885
14886 if (dopt->binary_upgrade)
14888 "OPERATOR CLASS", nameusing->data,
14889 opcinfo->dobj.namespace->dobj.name);
14890
14891 if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14892 ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14893 ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14894 .namespace = opcinfo->dobj.namespace->dobj.name,
14895 .owner = opcinfo->rolname,
14896 .description = "OPERATOR CLASS",
14897 .section = SECTION_PRE_DATA,
14898 .createStmt = q->data,
14899 .dropStmt = delq->data));
14900
14901 /* Dump Operator Class Comments */
14902 if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14903 dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14904 opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14905 opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14906
14907 free(opcintype);
14908 free(opcfamily);
14909 free(amname);
14910 destroyPQExpBuffer(query);
14914}
14915
14916/*
14917 * dumpOpfamily
14918 * write out a single operator family definition
14919 *
14920 * Note: this also dumps any "loose" operator members that aren't bound to a
14921 * specific opclass within the opfamily.
14922 */
14923static void
14925{
14926 DumpOptions *dopt = fout->dopt;
14927 PQExpBuffer query;
14928 PQExpBuffer q;
14931 PGresult *res;
14934 int ntups;
14935 int i_amname;
14936 int i_amopstrategy;
14937 int i_amopopr;
14938 int i_sortfamily;
14939 int i_sortfamilynsp;
14940 int i_amprocnum;
14941 int i_amproc;
14942 int i_amproclefttype;
14944 char *amname;
14945 char *amopstrategy;
14946 char *amopopr;
14947 char *sortfamily;
14948 char *sortfamilynsp;
14949 char *amprocnum;
14950 char *amproc;
14951 char *amproclefttype;
14952 char *amprocrighttype;
14953 bool needComma;
14954 int i;
14955
14956 /* Do nothing if not dumping schema */
14957 if (!dopt->dumpSchema)
14958 return;
14959
14960 query = createPQExpBuffer();
14961 q = createPQExpBuffer();
14964
14965 /*
14966 * Fetch only those opfamily members that are tied directly to the
14967 * opfamily by pg_depend entries.
14968 */
14969 appendPQExpBuffer(query, "SELECT amopstrategy, "
14970 "amopopr::pg_catalog.regoperator, "
14971 "opfname AS sortfamily, "
14972 "nspname AS sortfamilynsp "
14973 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14974 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14975 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14976 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14977 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14978 "AND refobjid = '%u'::pg_catalog.oid "
14979 "AND amopfamily = '%u'::pg_catalog.oid "
14980 "ORDER BY amopstrategy",
14981 opfinfo->dobj.catId.oid,
14982 opfinfo->dobj.catId.oid);
14983
14985
14986 resetPQExpBuffer(query);
14987
14988 appendPQExpBuffer(query, "SELECT amprocnum, "
14989 "amproc::pg_catalog.regprocedure, "
14990 "amproclefttype::pg_catalog.regtype, "
14991 "amprocrighttype::pg_catalog.regtype "
14992 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14993 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14994 "AND refobjid = '%u'::pg_catalog.oid "
14995 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14996 "AND objid = ap.oid "
14997 "ORDER BY amprocnum",
14998 opfinfo->dobj.catId.oid);
14999
15001
15002 /* Get additional fields from the pg_opfamily row */
15003 resetPQExpBuffer(query);
15004
15005 appendPQExpBuffer(query, "SELECT "
15006 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
15007 "FROM pg_catalog.pg_opfamily "
15008 "WHERE oid = '%u'::pg_catalog.oid",
15009 opfinfo->dobj.catId.oid);
15010
15011 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15012
15013 i_amname = PQfnumber(res, "amname");
15014
15015 /* amname will still be needed after we PQclear res */
15016 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
15017
15018 appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
15020 appendPQExpBuffer(delq, " USING %s;\n",
15021 fmtId(amname));
15022
15023 /* Build the fixed portion of the CREATE command */
15024 appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
15026 appendPQExpBuffer(q, " USING %s;\n",
15027 fmtId(amname));
15028
15029 PQclear(res);
15030
15031 /* Do we need an ALTER to add loose members? */
15032 if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
15033 {
15034 appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
15036 appendPQExpBuffer(q, " USING %s ADD\n ",
15037 fmtId(amname));
15038
15039 needComma = false;
15040
15041 /*
15042 * Now fetch and print the OPERATOR entries (pg_amop rows).
15043 */
15044 ntups = PQntuples(res_ops);
15045
15046 i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
15047 i_amopopr = PQfnumber(res_ops, "amopopr");
15048 i_sortfamily = PQfnumber(res_ops, "sortfamily");
15049 i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
15050
15051 for (i = 0; i < ntups; i++)
15052 {
15055 sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
15057
15058 if (needComma)
15059 appendPQExpBufferStr(q, " ,\n ");
15060
15061 appendPQExpBuffer(q, "OPERATOR %s %s",
15063
15064 if (strlen(sortfamily) > 0)
15065 {
15066 appendPQExpBufferStr(q, " FOR ORDER BY ");
15068 appendPQExpBufferStr(q, fmtId(sortfamily));
15069 }
15070
15071 needComma = true;
15072 }
15073
15074 /*
15075 * Now fetch and print the FUNCTION entries (pg_amproc rows).
15076 */
15077 ntups = PQntuples(res_procs);
15078
15079 i_amprocnum = PQfnumber(res_procs, "amprocnum");
15080 i_amproc = PQfnumber(res_procs, "amproc");
15081 i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
15082 i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
15083
15084 for (i = 0; i < ntups; i++)
15085 {
15090
15091 if (needComma)
15092 appendPQExpBufferStr(q, " ,\n ");
15093
15094 appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
15096 amproc);
15097
15098 needComma = true;
15099 }
15100
15101 appendPQExpBufferStr(q, ";\n");
15102 }
15103
15105 appendPQExpBuffer(nameusing, " USING %s",
15106 fmtId(amname));
15107
15108 if (dopt->binary_upgrade)
15110 "OPERATOR FAMILY", nameusing->data,
15111 opfinfo->dobj.namespace->dobj.name);
15112
15113 if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15114 ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
15115 ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
15116 .namespace = opfinfo->dobj.namespace->dobj.name,
15117 .owner = opfinfo->rolname,
15118 .description = "OPERATOR FAMILY",
15119 .section = SECTION_PRE_DATA,
15120 .createStmt = q->data,
15121 .dropStmt = delq->data));
15122
15123 /* Dump Operator Family Comments */
15124 if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15125 dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
15126 opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
15127 opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
15128
15129 free(amname);
15132 destroyPQExpBuffer(query);
15136}
15137
15138/*
15139 * dumpCollation
15140 * write out a single collation definition
15141 */
15142static void
15144{
15145 DumpOptions *dopt = fout->dopt;
15146 PQExpBuffer query;
15147 PQExpBuffer q;
15149 char *qcollname;
15150 PGresult *res;
15151 int i_collprovider;
15153 int i_collcollate;
15154 int i_collctype;
15155 int i_colllocale;
15156 int i_collicurules;
15157 const char *collprovider;
15158 const char *collcollate;
15159 const char *collctype;
15160 const char *colllocale;
15161 const char *collicurules;
15162
15163 /* Do nothing if not dumping schema */
15164 if (!dopt->dumpSchema)
15165 return;
15166
15167 query = createPQExpBuffer();
15168 q = createPQExpBuffer();
15170
15171 qcollname = pg_strdup(fmtId(collinfo->dobj.name));
15172
15173 /* Get collation-specific details */
15174 appendPQExpBufferStr(query, "SELECT ");
15175
15176 if (fout->remoteVersion >= 100000)
15178 "collprovider, "
15179 "collversion, ");
15180 else
15182 "'c' AS collprovider, "
15183 "NULL AS collversion, ");
15184
15185 if (fout->remoteVersion >= 120000)
15187 "collisdeterministic, ");
15188 else
15190 "true AS collisdeterministic, ");
15191
15192 if (fout->remoteVersion >= 170000)
15194 "colllocale, ");
15195 else if (fout->remoteVersion >= 150000)
15197 "colliculocale AS colllocale, ");
15198 else
15200 "NULL AS colllocale, ");
15201
15202 if (fout->remoteVersion >= 160000)
15204 "collicurules, ");
15205 else
15207 "NULL AS collicurules, ");
15208
15209 appendPQExpBuffer(query,
15210 "collcollate, "
15211 "collctype "
15212 "FROM pg_catalog.pg_collation c "
15213 "WHERE c.oid = '%u'::pg_catalog.oid",
15214 collinfo->dobj.catId.oid);
15215
15216 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15217
15218 i_collprovider = PQfnumber(res, "collprovider");
15219 i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15220 i_collcollate = PQfnumber(res, "collcollate");
15221 i_collctype = PQfnumber(res, "collctype");
15222 i_colllocale = PQfnumber(res, "colllocale");
15223 i_collicurules = PQfnumber(res, "collicurules");
15224
15226
15227 if (!PQgetisnull(res, 0, i_collcollate))
15229 else
15230 collcollate = NULL;
15231
15232 if (!PQgetisnull(res, 0, i_collctype))
15233 collctype = PQgetvalue(res, 0, i_collctype);
15234 else
15235 collctype = NULL;
15236
15237 /*
15238 * Before version 15, collcollate and collctype were of type NAME and
15239 * non-nullable. Treat empty strings as NULL for consistency.
15240 */
15241 if (fout->remoteVersion < 150000)
15242 {
15243 if (collcollate[0] == '\0')
15244 collcollate = NULL;
15245 if (collctype[0] == '\0')
15246 collctype = NULL;
15247 }
15248
15249 if (!PQgetisnull(res, 0, i_colllocale))
15251 else
15252 colllocale = NULL;
15253
15254 if (!PQgetisnull(res, 0, i_collicurules))
15256 else
15258
15259 appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15261
15262 appendPQExpBuffer(q, "CREATE COLLATION %s (",
15264
15265 appendPQExpBufferStr(q, "provider = ");
15266 if (collprovider[0] == 'b')
15267 appendPQExpBufferStr(q, "builtin");
15268 else if (collprovider[0] == 'c')
15269 appendPQExpBufferStr(q, "libc");
15270 else if (collprovider[0] == 'i')
15271 appendPQExpBufferStr(q, "icu");
15272 else if (collprovider[0] == 'd')
15273 /* to allow dumping pg_catalog; not accepted on input */
15274 appendPQExpBufferStr(q, "default");
15275 else
15276 pg_fatal("unrecognized collation provider: %s",
15277 collprovider);
15278
15279 if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15280 appendPQExpBufferStr(q, ", deterministic = false");
15281
15282 if (collprovider[0] == 'd')
15283 {
15285 pg_log_warning("invalid collation \"%s\"", qcollname);
15286
15287 /* no locale -- the default collation cannot be reloaded anyway */
15288 }
15289 else if (collprovider[0] == 'b')
15290 {
15292 pg_log_warning("invalid collation \"%s\"", qcollname);
15293
15294 appendPQExpBufferStr(q, ", locale = ");
15296 fout);
15297 }
15298 else if (collprovider[0] == 'i')
15299 {
15300 if (fout->remoteVersion >= 150000)
15301 {
15302 if (collcollate || collctype || !colllocale)
15303 pg_log_warning("invalid collation \"%s\"", qcollname);
15304
15305 appendPQExpBufferStr(q, ", locale = ");
15307 fout);
15308 }
15309 else
15310 {
15311 if (!collcollate || !collctype || colllocale ||
15313 pg_log_warning("invalid collation \"%s\"", qcollname);
15314
15315 appendPQExpBufferStr(q, ", locale = ");
15317 }
15318
15319 if (collicurules)
15320 {
15321 appendPQExpBufferStr(q, ", rules = ");
15323 }
15324 }
15325 else if (collprovider[0] == 'c')
15326 {
15328 pg_log_warning("invalid collation \"%s\"", qcollname);
15329
15331 {
15332 appendPQExpBufferStr(q, ", locale = ");
15334 }
15335 else
15336 {
15337 appendPQExpBufferStr(q, ", lc_collate = ");
15339 appendPQExpBufferStr(q, ", lc_ctype = ");
15341 }
15342 }
15343 else
15344 pg_fatal("unrecognized collation provider: %s", collprovider);
15345
15346 /*
15347 * For binary upgrade, carry over the collation version. For normal
15348 * dump/restore, omit the version, so that it is computed upon restore.
15349 */
15350 if (dopt->binary_upgrade)
15351 {
15352 int i_collversion;
15353
15354 i_collversion = PQfnumber(res, "collversion");
15355 if (!PQgetisnull(res, 0, i_collversion))
15356 {
15357 appendPQExpBufferStr(q, ", version = ");
15359 PQgetvalue(res, 0, i_collversion),
15360 fout);
15361 }
15362 }
15363
15364 appendPQExpBufferStr(q, ");\n");
15365
15366 if (dopt->binary_upgrade)
15368 "COLLATION", qcollname,
15369 collinfo->dobj.namespace->dobj.name);
15370
15371 if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15372 ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15373 ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15374 .namespace = collinfo->dobj.namespace->dobj.name,
15375 .owner = collinfo->rolname,
15376 .description = "COLLATION",
15377 .section = SECTION_PRE_DATA,
15378 .createStmt = q->data,
15379 .dropStmt = delq->data));
15380
15381 /* Dump Collation Comments */
15382 if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15383 dumpComment(fout, "COLLATION", qcollname,
15384 collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15385 collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15386
15387 PQclear(res);
15388
15389 destroyPQExpBuffer(query);
15392 free(qcollname);
15393}
15394
15395/*
15396 * dumpConversion
15397 * write out a single conversion definition
15398 */
15399static void
15401{
15402 DumpOptions *dopt = fout->dopt;
15403 PQExpBuffer query;
15404 PQExpBuffer q;
15406 char *qconvname;
15407 PGresult *res;
15408 int i_conforencoding;
15409 int i_contoencoding;
15410 int i_conproc;
15411 int i_condefault;
15412 const char *conforencoding;
15413 const char *contoencoding;
15414 const char *conproc;
15415 bool condefault;
15416
15417 /* Do nothing if not dumping schema */
15418 if (!dopt->dumpSchema)
15419 return;
15420
15421 query = createPQExpBuffer();
15422 q = createPQExpBuffer();
15424
15425 qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15426
15427 /* Get conversion-specific details */
15428 appendPQExpBuffer(query, "SELECT "
15429 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15430 "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15431 "conproc, condefault "
15432 "FROM pg_catalog.pg_conversion c "
15433 "WHERE c.oid = '%u'::pg_catalog.oid",
15434 convinfo->dobj.catId.oid);
15435
15436 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15437
15438 i_conforencoding = PQfnumber(res, "conforencoding");
15439 i_contoencoding = PQfnumber(res, "contoencoding");
15440 i_conproc = PQfnumber(res, "conproc");
15441 i_condefault = PQfnumber(res, "condefault");
15442
15445 conproc = PQgetvalue(res, 0, i_conproc);
15446 condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15447
15448 appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15450
15451 appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15452 (condefault) ? "DEFAULT " : "",
15455 appendPQExpBufferStr(q, " TO ");
15457 /* regproc output is already sufficiently quoted */
15458 appendPQExpBuffer(q, " FROM %s;\n", conproc);
15459
15460 if (dopt->binary_upgrade)
15462 "CONVERSION", qconvname,
15463 convinfo->dobj.namespace->dobj.name);
15464
15465 if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15466 ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15467 ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15468 .namespace = convinfo->dobj.namespace->dobj.name,
15469 .owner = convinfo->rolname,
15470 .description = "CONVERSION",
15471 .section = SECTION_PRE_DATA,
15472 .createStmt = q->data,
15473 .dropStmt = delq->data));
15474
15475 /* Dump Conversion Comments */
15476 if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15477 dumpComment(fout, "CONVERSION", qconvname,
15478 convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15479 convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15480
15481 PQclear(res);
15482
15483 destroyPQExpBuffer(query);
15486 free(qconvname);
15487}
15488
15489/*
15490 * format_aggregate_signature: generate aggregate name and argument list
15491 *
15492 * The argument type names are qualified if needed. The aggregate name
15493 * is never qualified.
15494 */
15495static char *
15497{
15499 int j;
15500
15502 if (honor_quotes)
15503 appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15504 else
15505 appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15506
15507 if (agginfo->aggfn.nargs == 0)
15508 appendPQExpBufferStr(&buf, "(*)");
15509 else
15510 {
15512 for (j = 0; j < agginfo->aggfn.nargs; j++)
15513 appendPQExpBuffer(&buf, "%s%s",
15514 (j > 0) ? ", " : "",
15516 agginfo->aggfn.argtypes[j],
15517 zeroIsError));
15519 }
15520 return buf.data;
15521}
15522
15523/*
15524 * dumpAgg
15525 * write out a single aggregate definition
15526 */
15527static void
15529{
15530 DumpOptions *dopt = fout->dopt;
15531 PQExpBuffer query;
15532 PQExpBuffer q;
15534 PQExpBuffer details;
15535 char *aggsig; /* identity signature */
15536 char *aggfullsig = NULL; /* full signature */
15537 char *aggsig_tag;
15538 PGresult *res;
15539 int i_agginitval;
15540 int i_aggminitval;
15541 const char *aggtransfn;
15542 const char *aggfinalfn;
15543 const char *aggcombinefn;
15544 const char *aggserialfn;
15545 const char *aggdeserialfn;
15546 const char *aggmtransfn;
15547 const char *aggminvtransfn;
15548 const char *aggmfinalfn;
15549 bool aggfinalextra;
15550 bool aggmfinalextra;
15551 char aggfinalmodify;
15552 char aggmfinalmodify;
15553 const char *aggsortop;
15554 char *aggsortconvop;
15555 char aggkind;
15556 const char *aggtranstype;
15557 const char *aggtransspace;
15558 const char *aggmtranstype;
15559 const char *aggmtransspace;
15560 const char *agginitval;
15561 const char *aggminitval;
15562 const char *proparallel;
15563 char defaultfinalmodify;
15564
15565 /* Do nothing if not dumping schema */
15566 if (!dopt->dumpSchema)
15567 return;
15568
15569 query = createPQExpBuffer();
15570 q = createPQExpBuffer();
15572 details = createPQExpBuffer();
15573
15575 {
15576 /* Set up query for aggregate-specific details */
15578 "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15579
15581 "SELECT "
15582 "aggtransfn,\n"
15583 "aggfinalfn,\n"
15584 "aggtranstype::pg_catalog.regtype,\n"
15585 "agginitval,\n"
15586 "aggsortop,\n"
15587 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15588 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15589
15590 if (fout->remoteVersion >= 90400)
15592 "aggkind,\n"
15593 "aggmtransfn,\n"
15594 "aggminvtransfn,\n"
15595 "aggmfinalfn,\n"
15596 "aggmtranstype::pg_catalog.regtype,\n"
15597 "aggfinalextra,\n"
15598 "aggmfinalextra,\n"
15599 "aggtransspace,\n"
15600 "aggmtransspace,\n"
15601 "aggminitval,\n");
15602 else
15604 "'n' AS aggkind,\n"
15605 "'-' AS aggmtransfn,\n"
15606 "'-' AS aggminvtransfn,\n"
15607 "'-' AS aggmfinalfn,\n"
15608 "0 AS aggmtranstype,\n"
15609 "false AS aggfinalextra,\n"
15610 "false AS aggmfinalextra,\n"
15611 "0 AS aggtransspace,\n"
15612 "0 AS aggmtransspace,\n"
15613 "NULL AS aggminitval,\n");
15614
15615 if (fout->remoteVersion >= 90600)
15617 "aggcombinefn,\n"
15618 "aggserialfn,\n"
15619 "aggdeserialfn,\n"
15620 "proparallel,\n");
15621 else
15623 "'-' AS aggcombinefn,\n"
15624 "'-' AS aggserialfn,\n"
15625 "'-' AS aggdeserialfn,\n"
15626 "'u' AS proparallel,\n");
15627
15628 if (fout->remoteVersion >= 110000)
15630 "aggfinalmodify,\n"
15631 "aggmfinalmodify\n");
15632 else
15634 "'0' AS aggfinalmodify,\n"
15635 "'0' AS aggmfinalmodify\n");
15636
15638 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15639 "WHERE a.aggfnoid = p.oid "
15640 "AND p.oid = $1");
15641
15642 ExecuteSqlStatement(fout, query->data);
15643
15645 }
15646
15647 printfPQExpBuffer(query,
15648 "EXECUTE dumpAgg('%u')",
15649 agginfo->aggfn.dobj.catId.oid);
15650
15651 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15652
15653 i_agginitval = PQfnumber(res, "agginitval");
15654 i_aggminitval = PQfnumber(res, "aggminitval");
15655
15656 aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15657 aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15658 aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15659 aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15660 aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15661 aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15662 aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15663 aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15664 aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15665 aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15666 aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15667 aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15668 aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15669 aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15670 aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15671 aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15672 aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15673 aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15676 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15677
15678 {
15679 char *funcargs;
15680 char *funciargs;
15681
15682 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15683 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15686 }
15687
15689
15690 /* identify default modify flag for aggkind (must match DefineAggregate) */
15692 /* replace omitted flags for old versions */
15693 if (aggfinalmodify == '0')
15695 if (aggmfinalmodify == '0')
15697
15698 /* regproc and regtype output is already sufficiently quoted */
15699 appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15700 aggtransfn, aggtranstype);
15701
15702 if (strcmp(aggtransspace, "0") != 0)
15703 {
15704 appendPQExpBuffer(details, ",\n SSPACE = %s",
15705 aggtransspace);
15706 }
15707
15708 if (!PQgetisnull(res, 0, i_agginitval))
15709 {
15710 appendPQExpBufferStr(details, ",\n INITCOND = ");
15712 }
15713
15714 if (strcmp(aggfinalfn, "-") != 0)
15715 {
15716 appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15717 aggfinalfn);
15718 if (aggfinalextra)
15719 appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15721 {
15722 switch (aggfinalmodify)
15723 {
15725 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15726 break;
15728 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15729 break;
15731 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15732 break;
15733 default:
15734 pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15735 agginfo->aggfn.dobj.name);
15736 break;
15737 }
15738 }
15739 }
15740
15741 if (strcmp(aggcombinefn, "-") != 0)
15742 appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15743
15744 if (strcmp(aggserialfn, "-") != 0)
15745 appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15746
15747 if (strcmp(aggdeserialfn, "-") != 0)
15748 appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15749
15750 if (strcmp(aggmtransfn, "-") != 0)
15751 {
15752 appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15756 }
15757
15758 if (strcmp(aggmtransspace, "0") != 0)
15759 {
15760 appendPQExpBuffer(details, ",\n MSSPACE = %s",
15762 }
15763
15764 if (!PQgetisnull(res, 0, i_aggminitval))
15765 {
15766 appendPQExpBufferStr(details, ",\n MINITCOND = ");
15768 }
15769
15770 if (strcmp(aggmfinalfn, "-") != 0)
15771 {
15772 appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15773 aggmfinalfn);
15774 if (aggmfinalextra)
15775 appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15777 {
15778 switch (aggmfinalmodify)
15779 {
15781 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15782 break;
15784 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15785 break;
15787 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15788 break;
15789 default:
15790 pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15791 agginfo->aggfn.dobj.name);
15792 break;
15793 }
15794 }
15795 }
15796
15798 if (aggsortconvop)
15799 {
15800 appendPQExpBuffer(details, ",\n SORTOP = %s",
15803 }
15804
15806 appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15807
15809 {
15810 if (proparallel[0] == PROPARALLEL_SAFE)
15811 appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15812 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15813 appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15814 else if (proparallel[0] != PROPARALLEL_UNSAFE)
15815 pg_fatal("unrecognized proparallel value for function \"%s\"",
15816 agginfo->aggfn.dobj.name);
15817 }
15818
15819 appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15820 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15821 aggsig);
15822
15823 appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15824 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15825 aggfullsig ? aggfullsig : aggsig, details->data);
15826
15827 if (dopt->binary_upgrade)
15829 "AGGREGATE", aggsig,
15830 agginfo->aggfn.dobj.namespace->dobj.name);
15831
15832 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15833 ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15834 agginfo->aggfn.dobj.dumpId,
15835 ARCHIVE_OPTS(.tag = aggsig_tag,
15836 .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15837 .owner = agginfo->aggfn.rolname,
15838 .description = "AGGREGATE",
15839 .section = SECTION_PRE_DATA,
15840 .createStmt = q->data,
15841 .dropStmt = delq->data));
15842
15843 /* Dump Aggregate Comments */
15844 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15845 dumpComment(fout, "AGGREGATE", aggsig,
15846 agginfo->aggfn.dobj.namespace->dobj.name,
15847 agginfo->aggfn.rolname,
15848 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15849
15850 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15851 dumpSecLabel(fout, "AGGREGATE", aggsig,
15852 agginfo->aggfn.dobj.namespace->dobj.name,
15853 agginfo->aggfn.rolname,
15854 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15855
15856 /*
15857 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15858 * command look like a function's GRANT; in particular this affects the
15859 * syntax for zero-argument aggregates and ordered-set aggregates.
15860 */
15861 free(aggsig);
15862
15863 aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15864
15865 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15866 dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15867 "FUNCTION", aggsig, NULL,
15868 agginfo->aggfn.dobj.namespace->dobj.name,
15869 NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15870
15871 free(aggsig);
15874
15875 PQclear(res);
15876
15877 destroyPQExpBuffer(query);
15880 destroyPQExpBuffer(details);
15881}
15882
15883/*
15884 * dumpTSParser
15885 * write out a single text search parser
15886 */
15887static void
15889{
15890 DumpOptions *dopt = fout->dopt;
15891 PQExpBuffer q;
15893 char *qprsname;
15894
15895 /* Do nothing if not dumping schema */
15896 if (!dopt->dumpSchema)
15897 return;
15898
15899 q = createPQExpBuffer();
15901
15902 qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15903
15904 appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15906
15907 appendPQExpBuffer(q, " START = %s,\n",
15908 convertTSFunction(fout, prsinfo->prsstart));
15909 appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15910 convertTSFunction(fout, prsinfo->prstoken));
15911 appendPQExpBuffer(q, " END = %s,\n",
15912 convertTSFunction(fout, prsinfo->prsend));
15913 if (prsinfo->prsheadline != InvalidOid)
15914 appendPQExpBuffer(q, " HEADLINE = %s,\n",
15915 convertTSFunction(fout, prsinfo->prsheadline));
15916 appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15917 convertTSFunction(fout, prsinfo->prslextype));
15918
15919 appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15921
15922 if (dopt->binary_upgrade)
15924 "TEXT SEARCH PARSER", qprsname,
15925 prsinfo->dobj.namespace->dobj.name);
15926
15927 if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15928 ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15929 ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15930 .namespace = prsinfo->dobj.namespace->dobj.name,
15931 .description = "TEXT SEARCH PARSER",
15932 .section = SECTION_PRE_DATA,
15933 .createStmt = q->data,
15934 .dropStmt = delq->data));
15935
15936 /* Dump Parser Comments */
15937 if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15938 dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15939 prsinfo->dobj.namespace->dobj.name, "",
15940 prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15941
15944 free(qprsname);
15945}
15946
15947/*
15948 * dumpTSDictionary
15949 * write out a single text search dictionary
15950 */
15951static void
15953{
15954 DumpOptions *dopt = fout->dopt;
15955 PQExpBuffer q;
15957 PQExpBuffer query;
15958 char *qdictname;
15959 PGresult *res;
15960 char *nspname;
15961 char *tmplname;
15962
15963 /* Do nothing if not dumping schema */
15964 if (!dopt->dumpSchema)
15965 return;
15966
15967 q = createPQExpBuffer();
15969 query = createPQExpBuffer();
15970
15971 qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15972
15973 /* Fetch name and namespace of the dictionary's template */
15974 appendPQExpBuffer(query, "SELECT nspname, tmplname "
15975 "FROM pg_ts_template p, pg_namespace n "
15976 "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15977 dictinfo->dicttemplate);
15978 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15979 nspname = PQgetvalue(res, 0, 0);
15980 tmplname = PQgetvalue(res, 0, 1);
15981
15982 appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15984
15985 appendPQExpBufferStr(q, " TEMPLATE = ");
15986 appendPQExpBuffer(q, "%s.", fmtId(nspname));
15988
15989 PQclear(res);
15990
15991 /* the dictinitoption can be dumped straight into the command */
15992 if (dictinfo->dictinitoption)
15993 appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15994
15995 appendPQExpBufferStr(q, " );\n");
15996
15997 appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15999
16000 if (dopt->binary_upgrade)
16002 "TEXT SEARCH DICTIONARY", qdictname,
16003 dictinfo->dobj.namespace->dobj.name);
16004
16005 if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16006 ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
16007 ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
16008 .namespace = dictinfo->dobj.namespace->dobj.name,
16009 .owner = dictinfo->rolname,
16010 .description = "TEXT SEARCH DICTIONARY",
16011 .section = SECTION_PRE_DATA,
16012 .createStmt = q->data,
16013 .dropStmt = delq->data));
16014
16015 /* Dump Dictionary Comments */
16016 if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16017 dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
16018 dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
16019 dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
16020
16023 destroyPQExpBuffer(query);
16024 free(qdictname);
16025}
16026
16027/*
16028 * dumpTSTemplate
16029 * write out a single text search template
16030 */
16031static void
16033{
16034 DumpOptions *dopt = fout->dopt;
16035 PQExpBuffer q;
16037 char *qtmplname;
16038
16039 /* Do nothing if not dumping schema */
16040 if (!dopt->dumpSchema)
16041 return;
16042
16043 q = createPQExpBuffer();
16045
16046 qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
16047
16048 appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
16050
16051 if (tmplinfo->tmplinit != InvalidOid)
16052 appendPQExpBuffer(q, " INIT = %s,\n",
16053 convertTSFunction(fout, tmplinfo->tmplinit));
16054 appendPQExpBuffer(q, " LEXIZE = %s );\n",
16055 convertTSFunction(fout, tmplinfo->tmpllexize));
16056
16057 appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
16059
16060 if (dopt->binary_upgrade)
16062 "TEXT SEARCH TEMPLATE", qtmplname,
16063 tmplinfo->dobj.namespace->dobj.name);
16064
16065 if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16066 ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
16067 ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
16068 .namespace = tmplinfo->dobj.namespace->dobj.name,
16069 .description = "TEXT SEARCH TEMPLATE",
16070 .section = SECTION_PRE_DATA,
16071 .createStmt = q->data,
16072 .dropStmt = delq->data));
16073
16074 /* Dump Template Comments */
16075 if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16076 dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
16077 tmplinfo->dobj.namespace->dobj.name, "",
16078 tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
16079
16082 free(qtmplname);
16083}
16084
16085/*
16086 * dumpTSConfig
16087 * write out a single text search configuration
16088 */
16089static void
16091{
16092 DumpOptions *dopt = fout->dopt;
16093 PQExpBuffer q;
16095 PQExpBuffer query;
16096 char *qcfgname;
16097 PGresult *res;
16098 char *nspname;
16099 char *prsname;
16100 int ntups,
16101 i;
16102 int i_tokenname;
16103 int i_dictname;
16104
16105 /* Do nothing if not dumping schema */
16106 if (!dopt->dumpSchema)
16107 return;
16108
16109 q = createPQExpBuffer();
16111 query = createPQExpBuffer();
16112
16113 qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
16114
16115 /* Fetch name and namespace of the config's parser */
16116 appendPQExpBuffer(query, "SELECT nspname, prsname "
16117 "FROM pg_ts_parser p, pg_namespace n "
16118 "WHERE p.oid = '%u' AND n.oid = prsnamespace",
16119 cfginfo->cfgparser);
16120 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16121 nspname = PQgetvalue(res, 0, 0);
16122 prsname = PQgetvalue(res, 0, 1);
16123
16124 appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
16126
16127 appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
16128 appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
16129
16130 PQclear(res);
16131
16132 resetPQExpBuffer(query);
16133 appendPQExpBuffer(query,
16134 "SELECT\n"
16135 " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
16136 " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
16137 " m.mapdict::pg_catalog.regdictionary AS dictname\n"
16138 "FROM pg_catalog.pg_ts_config_map AS m\n"
16139 "WHERE m.mapcfg = '%u'\n"
16140 "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
16141 cfginfo->cfgparser, cfginfo->dobj.catId.oid);
16142
16143 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16144 ntups = PQntuples(res);
16145
16146 i_tokenname = PQfnumber(res, "tokenname");
16147 i_dictname = PQfnumber(res, "dictname");
16148
16149 for (i = 0; i < ntups; i++)
16150 {
16151 char *tokenname = PQgetvalue(res, i, i_tokenname);
16152 char *dictname = PQgetvalue(res, i, i_dictname);
16153
16154 if (i == 0 ||
16155 strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
16156 {
16157 /* starting a new token type, so start a new command */
16158 if (i > 0)
16159 appendPQExpBufferStr(q, ";\n");
16160 appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
16162 /* tokenname needs quoting, dictname does NOT */
16163 appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
16164 fmtId(tokenname), dictname);
16165 }
16166 else
16167 appendPQExpBuffer(q, ", %s", dictname);
16168 }
16169
16170 if (ntups > 0)
16171 appendPQExpBufferStr(q, ";\n");
16172
16173 PQclear(res);
16174
16175 appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
16177
16178 if (dopt->binary_upgrade)
16180 "TEXT SEARCH CONFIGURATION", qcfgname,
16181 cfginfo->dobj.namespace->dobj.name);
16182
16183 if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16184 ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
16185 ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
16186 .namespace = cfginfo->dobj.namespace->dobj.name,
16187 .owner = cfginfo->rolname,
16188 .description = "TEXT SEARCH CONFIGURATION",
16189 .section = SECTION_PRE_DATA,
16190 .createStmt = q->data,
16191 .dropStmt = delq->data));
16192
16193 /* Dump Configuration Comments */
16194 if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16195 dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
16196 cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
16197 cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
16198
16201 destroyPQExpBuffer(query);
16202 free(qcfgname);
16203}
16204
16205/*
16206 * dumpForeignDataWrapper
16207 * write out a single foreign-data wrapper definition
16208 */
16209static void
16211{
16212 DumpOptions *dopt = fout->dopt;
16213 PQExpBuffer q;
16215 char *qfdwname;
16216
16217 /* Do nothing if not dumping schema */
16218 if (!dopt->dumpSchema)
16219 return;
16220
16221 q = createPQExpBuffer();
16223
16224 qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16225
16226 appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16227 qfdwname);
16228
16229 if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16230 appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16231
16232 if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16233 appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16234
16235 if (strcmp(fdwinfo->fdwconnection, "-") != 0)
16236 appendPQExpBuffer(q, " CONNECTION %s", fdwinfo->fdwconnection);
16237
16238 if (strlen(fdwinfo->fdwoptions) > 0)
16239 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16240
16241 appendPQExpBufferStr(q, ";\n");
16242
16243 appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16244 qfdwname);
16245
16246 if (dopt->binary_upgrade)
16248 "FOREIGN DATA WRAPPER", qfdwname,
16249 NULL);
16250
16251 if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16252 ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16253 ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16254 .owner = fdwinfo->rolname,
16255 .description = "FOREIGN DATA WRAPPER",
16256 .section = SECTION_PRE_DATA,
16257 .createStmt = q->data,
16258 .dropStmt = delq->data));
16259
16260 /* Dump Foreign Data Wrapper Comments */
16261 if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16262 dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16263 NULL, fdwinfo->rolname,
16264 fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16265
16266 /* Handle the ACL */
16267 if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16268 dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16269 "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16270 NULL, fdwinfo->rolname, &fdwinfo->dacl);
16271
16272 free(qfdwname);
16273
16276}
16277
16278/*
16279 * dumpForeignServer
16280 * write out a foreign server definition
16281 */
16282static void
16284{
16285 DumpOptions *dopt = fout->dopt;
16286 PQExpBuffer q;
16288 PQExpBuffer query;
16289 PGresult *res;
16290 char *qsrvname;
16291 char *fdwname;
16292
16293 /* Do nothing if not dumping schema */
16294 if (!dopt->dumpSchema)
16295 return;
16296
16297 q = createPQExpBuffer();
16299 query = createPQExpBuffer();
16300
16301 qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16302
16303 /* look up the foreign-data wrapper */
16304 appendPQExpBuffer(query, "SELECT fdwname "
16305 "FROM pg_foreign_data_wrapper w "
16306 "WHERE w.oid = '%u'",
16307 srvinfo->srvfdw);
16308 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16309 fdwname = PQgetvalue(res, 0, 0);
16310
16311 appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16312 if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16313 {
16314 appendPQExpBufferStr(q, " TYPE ");
16315 appendStringLiteralAH(q, srvinfo->srvtype, fout);
16316 }
16317 if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16318 {
16319 appendPQExpBufferStr(q, " VERSION ");
16320 appendStringLiteralAH(q, srvinfo->srvversion, fout);
16321 }
16322
16323 appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16324 appendPQExpBufferStr(q, fmtId(fdwname));
16325
16326 if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16327 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16328
16329 appendPQExpBufferStr(q, ";\n");
16330
16331 appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16332 qsrvname);
16333
16334 if (dopt->binary_upgrade)
16336 "SERVER", qsrvname, NULL);
16337
16338 if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16339 ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16340 ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16341 .owner = srvinfo->rolname,
16342 .description = "SERVER",
16343 .section = SECTION_PRE_DATA,
16344 .createStmt = q->data,
16345 .dropStmt = delq->data));
16346
16347 /* Dump Foreign Server Comments */
16348 if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16349 dumpComment(fout, "SERVER", qsrvname,
16350 NULL, srvinfo->rolname,
16351 srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16352
16353 /* Handle the ACL */
16354 if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16355 dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16356 "FOREIGN SERVER", qsrvname, NULL, NULL,
16357 NULL, srvinfo->rolname, &srvinfo->dacl);
16358
16359 /* Dump user mappings */
16360 if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16362 srvinfo->dobj.name, NULL,
16363 srvinfo->rolname,
16364 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16365
16366 PQclear(res);
16367
16368 free(qsrvname);
16369
16372 destroyPQExpBuffer(query);
16373}
16374
16375/*
16376 * dumpUserMappings
16377 *
16378 * This routine is used to dump any user mappings associated with the
16379 * server handed to this routine. Should be called after ArchiveEntry()
16380 * for the server.
16381 */
16382static void
16384 const char *servername, const char *namespace,
16385 const char *owner,
16386 CatalogId catalogId, DumpId dumpId)
16387{
16388 PQExpBuffer q;
16390 PQExpBuffer query;
16391 PQExpBuffer tag;
16392 PGresult *res;
16393 int ntups;
16394 int i_usename;
16395 int i_umoptions;
16396 int i;
16397
16398 q = createPQExpBuffer();
16399 tag = createPQExpBuffer();
16401 query = createPQExpBuffer();
16402
16403 /*
16404 * We read from the publicly accessible view pg_user_mappings, so as not
16405 * to fail if run by a non-superuser. Note that the view will show
16406 * umoptions as null if the user hasn't got privileges for the associated
16407 * server; this means that pg_dump will dump such a mapping, but with no
16408 * OPTIONS clause. A possible alternative is to skip such mappings
16409 * altogether, but it's not clear that that's an improvement.
16410 */
16411 appendPQExpBuffer(query,
16412 "SELECT usename, "
16413 "array_to_string(ARRAY("
16414 "SELECT quote_ident(option_name) || ' ' || "
16415 "quote_literal(option_value) "
16416 "FROM pg_options_to_table(umoptions) "
16417 "ORDER BY option_name"
16418 "), E',\n ') AS umoptions "
16419 "FROM pg_user_mappings "
16420 "WHERE srvid = '%u' "
16421 "ORDER BY usename",
16422 catalogId.oid);
16423
16424 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16425
16426 ntups = PQntuples(res);
16427 i_usename = PQfnumber(res, "usename");
16428 i_umoptions = PQfnumber(res, "umoptions");
16429
16430 for (i = 0; i < ntups; i++)
16431 {
16432 char *usename;
16433 char *umoptions;
16434
16435 usename = PQgetvalue(res, i, i_usename);
16437
16439 appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16440 appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16441
16442 if (umoptions && strlen(umoptions) > 0)
16443 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16444
16445 appendPQExpBufferStr(q, ";\n");
16446
16448 appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16449 appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16450
16451 resetPQExpBuffer(tag);
16452 appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16453 usename, servername);
16454
16456 ARCHIVE_OPTS(.tag = tag->data,
16457 .namespace = namespace,
16458 .owner = owner,
16459 .description = "USER MAPPING",
16460 .section = SECTION_PRE_DATA,
16461 .createStmt = q->data,
16462 .dropStmt = delq->data));
16463 }
16464
16465 PQclear(res);
16466
16467 destroyPQExpBuffer(query);
16469 destroyPQExpBuffer(tag);
16471}
16472
16473/*
16474 * Write out default privileges information
16475 */
16476static void
16478{
16479 DumpOptions *dopt = fout->dopt;
16480 PQExpBuffer q;
16481 PQExpBuffer tag;
16482 const char *type;
16483
16484 /* Do nothing if not dumping schema, or if we're skipping ACLs */
16485 if (!dopt->dumpSchema || dopt->aclsSkip)
16486 return;
16487
16488 q = createPQExpBuffer();
16489 tag = createPQExpBuffer();
16490
16491 switch (daclinfo->defaclobjtype)
16492 {
16493 case DEFACLOBJ_RELATION:
16494 type = "TABLES";
16495 break;
16496 case DEFACLOBJ_SEQUENCE:
16497 type = "SEQUENCES";
16498 break;
16499 case DEFACLOBJ_FUNCTION:
16500 type = "FUNCTIONS";
16501 break;
16502 case DEFACLOBJ_TYPE:
16503 type = "TYPES";
16504 break;
16506 type = "SCHEMAS";
16507 break;
16509 type = "LARGE OBJECTS";
16510 break;
16511 default:
16512 /* shouldn't get here */
16513 pg_fatal("unrecognized object type in default privileges: %d",
16514 (int) daclinfo->defaclobjtype);
16515 type = ""; /* keep compiler quiet */
16516 }
16517
16518 appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16519
16520 /* build the actual command(s) for this tuple */
16522 daclinfo->dobj.namespace != NULL ?
16523 daclinfo->dobj.namespace->dobj.name : NULL,
16524 daclinfo->dacl.acl,
16525 daclinfo->dacl.acldefault,
16526 daclinfo->defaclrole,
16527 fout->remoteVersion,
16528 q))
16529 pg_fatal("could not parse default ACL list (%s)",
16530 daclinfo->dacl.acl);
16531
16532 if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16533 ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16534 ARCHIVE_OPTS(.tag = tag->data,
16535 .namespace = daclinfo->dobj.namespace ?
16536 daclinfo->dobj.namespace->dobj.name : NULL,
16537 .owner = daclinfo->defaclrole,
16538 .description = "DEFAULT ACL",
16539 .section = SECTION_POST_DATA,
16540 .createStmt = q->data));
16541
16542 destroyPQExpBuffer(tag);
16544}
16545
16546/*----------
16547 * Write out grant/revoke information
16548 *
16549 * 'objDumpId' is the dump ID of the underlying object.
16550 * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16551 * or InvalidDumpId if there is no need for a second dependency.
16552 * 'type' must be one of
16553 * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16554 * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16555 * 'name' is the formatted name of the object. Must be quoted etc. already.
16556 * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16557 * (Currently we assume that subname is only provided for table columns.)
16558 * 'nspname' is the namespace the object is in (NULL if none).
16559 * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16560 * to use the default for the object type.
16561 * 'owner' is the owner, NULL if there is no owner (for languages).
16562 * 'dacl' is the DumpableAcl struct for the object.
16563 *
16564 * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16565 * no ACL entry was created.
16566 *----------
16567 */
16568static DumpId
16570 const char *type, const char *name, const char *subname,
16571 const char *nspname, const char *tag, const char *owner,
16572 const DumpableAcl *dacl)
16573{
16575 DumpOptions *dopt = fout->dopt;
16576 const char *acls = dacl->acl;
16577 const char *acldefault = dacl->acldefault;
16578 char privtype = dacl->privtype;
16579 const char *initprivs = dacl->initprivs;
16580 const char *baseacls;
16581 PQExpBuffer sql;
16582
16583 /* Do nothing if ACL dump is not enabled */
16584 if (dopt->aclsSkip)
16585 return InvalidDumpId;
16586
16587 /* --data-only skips ACLs *except* large object ACLs */
16588 if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16589 return InvalidDumpId;
16590
16591 sql = createPQExpBuffer();
16592
16593 /*
16594 * In binary upgrade mode, we don't run an extension's script but instead
16595 * dump out the objects independently and then recreate them. To preserve
16596 * any initial privileges which were set on extension objects, we need to
16597 * compute the set of GRANT and REVOKE commands necessary to get from the
16598 * default privileges of an object to its initial privileges as recorded
16599 * in pg_init_privs.
16600 *
16601 * At restore time, we apply these commands after having called
16602 * binary_upgrade_set_record_init_privs(true). That tells the backend to
16603 * copy the results into pg_init_privs. This is how we preserve the
16604 * contents of that catalog across binary upgrades.
16605 */
16606 if (dopt->binary_upgrade && privtype == 'e' &&
16607 initprivs && *initprivs != '\0')
16608 {
16609 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16610 if (!buildACLCommands(name, subname, nspname, type,
16611 initprivs, acldefault, owner,
16612 "", fout->remoteVersion, sql))
16613 pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16614 initprivs, acldefault, name, type);
16615 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16616 }
16617
16618 /*
16619 * Now figure the GRANT and REVOKE commands needed to get to the object's
16620 * actual current ACL, starting from the initprivs if given, else from the
16621 * object-type-specific default. Also, while buildACLCommands will assume
16622 * that a NULL/empty acls string means it needn't do anything, what that
16623 * actually represents is the object-type-specific default; so we need to
16624 * substitute the acldefault string to get the right results in that case.
16625 */
16626 if (initprivs && *initprivs != '\0')
16627 {
16628 baseacls = initprivs;
16629 if (acls == NULL || *acls == '\0')
16630 acls = acldefault;
16631 }
16632 else
16634
16635 if (!buildACLCommands(name, subname, nspname, type,
16636 acls, baseacls, owner,
16637 "", fout->remoteVersion, sql))
16638 pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16639 acls, baseacls, name, type);
16640
16641 if (sql->len > 0)
16642 {
16644 DumpId aclDeps[2];
16645 int nDeps = 0;
16646
16647 if (tag)
16649 else if (subname)
16650 appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16651 else
16652 appendPQExpBuffer(tagbuf, "%s %s", type, name);
16653
16654 aclDeps[nDeps++] = objDumpId;
16655 if (altDumpId != InvalidDumpId)
16656 aclDeps[nDeps++] = altDumpId;
16657
16659
16661 ARCHIVE_OPTS(.tag = tagbuf->data,
16662 .namespace = nspname,
16663 .owner = owner,
16664 .description = "ACL",
16665 .section = SECTION_NONE,
16666 .createStmt = sql->data,
16667 .deps = aclDeps,
16668 .nDeps = nDeps));
16669
16671 }
16672
16673 destroyPQExpBuffer(sql);
16674
16675 return aclDumpId;
16676}
16677
16678/*
16679 * dumpSecLabel
16680 *
16681 * This routine is used to dump any security labels associated with the
16682 * object handed to this routine. The routine takes the object type
16683 * and object name (ready to print, except for schema decoration), plus
16684 * the namespace and owner of the object (for labeling the ArchiveEntry),
16685 * plus catalog ID and subid which are the lookup key for pg_seclabel,
16686 * plus the dump ID for the object (for setting a dependency).
16687 * If a matching pg_seclabel entry is found, it is dumped.
16688 *
16689 * Note: although this routine takes a dumpId for dependency purposes,
16690 * that purpose is just to mark the dependency in the emitted dump file
16691 * for possible future use by pg_restore. We do NOT use it for determining
16692 * ordering of the label in the dump file, because this routine is called
16693 * after dependency sorting occurs. This routine should be called just after
16694 * calling ArchiveEntry() for the specified object.
16695 */
16696static void
16697dumpSecLabel(Archive *fout, const char *type, const char *name,
16698 const char *namespace, const char *owner,
16699 CatalogId catalogId, int subid, DumpId dumpId)
16700{
16701 DumpOptions *dopt = fout->dopt;
16702 SecLabelItem *labels;
16703 int nlabels;
16704 int i;
16705 PQExpBuffer query;
16706
16707 /* do nothing, if --no-security-labels is supplied */
16708 if (dopt->no_security_labels)
16709 return;
16710
16711 /*
16712 * Security labels are schema not data ... except large object labels are
16713 * data
16714 */
16715 if (strcmp(type, "LARGE OBJECT") != 0)
16716 {
16717 if (!dopt->dumpSchema)
16718 return;
16719 }
16720 else
16721 {
16722 /* We do dump large object security labels in binary-upgrade mode */
16723 if (!dopt->dumpData && !dopt->binary_upgrade)
16724 return;
16725 }
16726
16727 /* Search for security labels associated with catalogId, using table */
16728 nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16729
16730 query = createPQExpBuffer();
16731
16732 for (i = 0; i < nlabels; i++)
16733 {
16734 /*
16735 * Ignore label entries for which the subid doesn't match.
16736 */
16737 if (labels[i].objsubid != subid)
16738 continue;
16739
16740 appendPQExpBuffer(query,
16741 "SECURITY LABEL FOR %s ON %s ",
16742 fmtId(labels[i].provider), type);
16743 if (namespace && *namespace)
16744 appendPQExpBuffer(query, "%s.", fmtId(namespace));
16745 appendPQExpBuffer(query, "%s IS ", name);
16746 appendStringLiteralAH(query, labels[i].label, fout);
16747 appendPQExpBufferStr(query, ";\n");
16748 }
16749
16750 if (query->len > 0)
16751 {
16753
16754 appendPQExpBuffer(tag, "%s %s", type, name);
16756 ARCHIVE_OPTS(.tag = tag->data,
16757 .namespace = namespace,
16758 .owner = owner,
16759 .description = "SECURITY LABEL",
16760 .section = SECTION_NONE,
16761 .createStmt = query->data,
16762 .deps = &dumpId,
16763 .nDeps = 1));
16764 destroyPQExpBuffer(tag);
16765 }
16766
16767 destroyPQExpBuffer(query);
16768}
16769
16770/*
16771 * dumpTableSecLabel
16772 *
16773 * As above, but dump security label for both the specified table (or view)
16774 * and its columns.
16775 */
16776static void
16778{
16779 DumpOptions *dopt = fout->dopt;
16780 SecLabelItem *labels;
16781 int nlabels;
16782 int i;
16783 PQExpBuffer query;
16784 PQExpBuffer target;
16785
16786 /* do nothing, if --no-security-labels is supplied */
16787 if (dopt->no_security_labels)
16788 return;
16789
16790 /* SecLabel are SCHEMA not data */
16791 if (!dopt->dumpSchema)
16792 return;
16793
16794 /* Search for comments associated with relation, using table */
16795 nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16796 tbinfo->dobj.catId.oid,
16797 &labels);
16798
16799 /* If security labels exist, build SECURITY LABEL statements */
16800 if (nlabels <= 0)
16801 return;
16802
16803 query = createPQExpBuffer();
16804 target = createPQExpBuffer();
16805
16806 for (i = 0; i < nlabels; i++)
16807 {
16808 const char *colname;
16809 const char *provider = labels[i].provider;
16810 const char *label = labels[i].label;
16811 int objsubid = labels[i].objsubid;
16812
16813 resetPQExpBuffer(target);
16814 if (objsubid == 0)
16815 {
16816 appendPQExpBuffer(target, "%s %s", reltypename,
16818 }
16819 else
16820 {
16821 colname = getAttrName(objsubid, tbinfo);
16822 /* first fmtXXX result must be consumed before calling again */
16823 appendPQExpBuffer(target, "COLUMN %s",
16825 appendPQExpBuffer(target, ".%s", fmtId(colname));
16826 }
16827 appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16828 fmtId(provider), target->data);
16830 appendPQExpBufferStr(query, ";\n");
16831 }
16832 if (query->len > 0)
16833 {
16834 resetPQExpBuffer(target);
16835 appendPQExpBuffer(target, "%s %s", reltypename,
16836 fmtId(tbinfo->dobj.name));
16838 ARCHIVE_OPTS(.tag = target->data,
16839 .namespace = tbinfo->dobj.namespace->dobj.name,
16840 .owner = tbinfo->rolname,
16841 .description = "SECURITY LABEL",
16842 .section = SECTION_NONE,
16843 .createStmt = query->data,
16844 .deps = &(tbinfo->dobj.dumpId),
16845 .nDeps = 1));
16846 }
16847 destroyPQExpBuffer(query);
16848 destroyPQExpBuffer(target);
16849}
16850
16851/*
16852 * findSecLabels
16853 *
16854 * Find the security label(s), if any, associated with the given object.
16855 * All the objsubid values associated with the given classoid/objoid are
16856 * found with one search.
16857 */
16858static int
16859findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16860{
16862 SecLabelItem *low;
16863 SecLabelItem *high;
16864 int nmatch;
16865
16866 if (nseclabels <= 0) /* no labels, so no match is possible */
16867 {
16868 *items = NULL;
16869 return 0;
16870 }
16871
16872 /*
16873 * Do binary search to find some item matching the object.
16874 */
16875 low = &seclabels[0];
16876 high = &seclabels[nseclabels - 1];
16877 while (low <= high)
16878 {
16879 middle = low + (high - low) / 2;
16880
16881 if (classoid < middle->classoid)
16882 high = middle - 1;
16883 else if (classoid > middle->classoid)
16884 low = middle + 1;
16885 else if (objoid < middle->objoid)
16886 high = middle - 1;
16887 else if (objoid > middle->objoid)
16888 low = middle + 1;
16889 else
16890 break; /* found a match */
16891 }
16892
16893 if (low > high) /* no matches */
16894 {
16895 *items = NULL;
16896 return 0;
16897 }
16898
16899 /*
16900 * Now determine how many items match the object. The search loop
16901 * invariant still holds: only items between low and high inclusive could
16902 * match.
16903 */
16904 nmatch = 1;
16905 while (middle > low)
16906 {
16907 if (classoid != middle[-1].classoid ||
16908 objoid != middle[-1].objoid)
16909 break;
16910 middle--;
16911 nmatch++;
16912 }
16913
16914 *items = middle;
16915
16916 middle += nmatch;
16917 while (middle <= high)
16918 {
16919 if (classoid != middle->classoid ||
16920 objoid != middle->objoid)
16921 break;
16922 middle++;
16923 nmatch++;
16924 }
16925
16926 return nmatch;
16927}
16928
16929/*
16930 * collectSecLabels
16931 *
16932 * Construct a table of all security labels available for database objects;
16933 * also set the has-seclabel component flag for each relevant object.
16934 *
16935 * The table is sorted by classoid/objid/objsubid for speed in lookup.
16936 */
16937static void
16939{
16940 PGresult *res;
16941 PQExpBuffer query;
16942 int i_label;
16943 int i_provider;
16944 int i_classoid;
16945 int i_objoid;
16946 int i_objsubid;
16947 int ntups;
16948 int i;
16949 DumpableObject *dobj;
16950
16951 query = createPQExpBuffer();
16952
16954 "SELECT label, provider, classoid, objoid, objsubid "
16955 "FROM pg_catalog.pg_seclabels "
16956 "ORDER BY classoid, objoid, objsubid");
16957
16958 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16959
16960 /* Construct lookup table containing OIDs in numeric form */
16961 i_label = PQfnumber(res, "label");
16962 i_provider = PQfnumber(res, "provider");
16963 i_classoid = PQfnumber(res, "classoid");
16964 i_objoid = PQfnumber(res, "objoid");
16965 i_objsubid = PQfnumber(res, "objsubid");
16966
16967 ntups = PQntuples(res);
16968
16970 nseclabels = 0;
16971 dobj = NULL;
16972
16973 for (i = 0; i < ntups; i++)
16974 {
16975 CatalogId objId;
16976 int subid;
16977
16978 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16979 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16980 subid = atoi(PQgetvalue(res, i, i_objsubid));
16981
16982 /* We needn't remember labels that don't match any dumpable object */
16983 if (dobj == NULL ||
16984 dobj->catId.tableoid != objId.tableoid ||
16985 dobj->catId.oid != objId.oid)
16986 dobj = findObjectByCatalogId(objId);
16987 if (dobj == NULL)
16988 continue;
16989
16990 /*
16991 * Labels on columns of composite types are linked to the type's
16992 * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16993 * in the type's own DumpableObject.
16994 */
16995 if (subid != 0 && dobj->objType == DO_TABLE &&
16996 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16997 {
16999
17000 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
17001 if (cTypeInfo)
17002 cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
17003 }
17004 else
17005 dobj->components |= DUMP_COMPONENT_SECLABEL;
17006
17010 seclabels[nseclabels].objoid = objId.oid;
17011 seclabels[nseclabels].objsubid = subid;
17012 nseclabels++;
17013 }
17014
17015 PQclear(res);
17016 destroyPQExpBuffer(query);
17017}
17018
17019/*
17020 * dumpTable
17021 * write out to fout the declarations (not data) of a user-defined table
17022 */
17023static void
17025{
17026 DumpOptions *dopt = fout->dopt;
17028 char *namecopy;
17029
17030 /* Do nothing if not dumping schema */
17031 if (!dopt->dumpSchema)
17032 return;
17033
17034 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17035 {
17036 if (tbinfo->relkind == RELKIND_SEQUENCE)
17038 else
17040 }
17041
17042 /* Handle the ACL here */
17043 namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
17044 if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
17045 {
17046 const char *objtype;
17047
17048 switch (tbinfo->relkind)
17049 {
17050 case RELKIND_SEQUENCE:
17051 objtype = "SEQUENCE";
17052 break;
17053 case RELKIND_PROPGRAPH:
17054 objtype = "PROPERTY GRAPH";
17055 break;
17056 default:
17057 objtype = "TABLE";
17058 break;
17059 }
17060
17062 dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
17063 objtype, namecopy, NULL,
17064 tbinfo->dobj.namespace->dobj.name,
17065 NULL, tbinfo->rolname, &tbinfo->dacl);
17066 }
17067
17068 /*
17069 * Handle column ACLs, if any. Note: we pull these with a separate query
17070 * rather than trying to fetch them during getTableAttrs, so that we won't
17071 * miss ACLs on system columns. Doing it this way also allows us to dump
17072 * ACLs for catalogs that we didn't mark "interesting" back in getTables.
17073 */
17074 if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
17075 {
17077 PGresult *res;
17078 int i;
17079
17081 {
17082 /* Set up query for column ACLs */
17084 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
17085
17086 if (fout->remoteVersion >= 90600)
17087 {
17088 /*
17089 * In principle we should call acldefault('c', relowner) to
17090 * get the default ACL for a column. However, we don't
17091 * currently store the numeric OID of the relowner in
17092 * TableInfo. We could convert the owner name using regrole,
17093 * but that creates a risk of failure due to concurrent role
17094 * renames. Given that the default ACL for columns is empty
17095 * and is likely to stay that way, it's not worth extra cycles
17096 * and risk to avoid hard-wiring that knowledge here.
17097 */
17099 "SELECT at.attname, "
17100 "at.attacl, "
17101 "'{}' AS acldefault, "
17102 "pip.privtype, pip.initprivs "
17103 "FROM pg_catalog.pg_attribute at "
17104 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
17105 "(at.attrelid = pip.objoid "
17106 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
17107 "AND at.attnum = pip.objsubid) "
17108 "WHERE at.attrelid = $1 AND "
17109 "NOT at.attisdropped "
17110 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
17111 "ORDER BY at.attnum");
17112 }
17113 else
17114 {
17116 "SELECT attname, attacl, '{}' AS acldefault, "
17117 "NULL AS privtype, NULL AS initprivs "
17118 "FROM pg_catalog.pg_attribute "
17119 "WHERE attrelid = $1 AND NOT attisdropped "
17120 "AND attacl IS NOT NULL "
17121 "ORDER BY attnum");
17122 }
17123
17124 ExecuteSqlStatement(fout, query->data);
17125
17127 }
17128
17129 printfPQExpBuffer(query,
17130 "EXECUTE getColumnACLs('%u')",
17131 tbinfo->dobj.catId.oid);
17132
17133 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17134
17135 for (i = 0; i < PQntuples(res); i++)
17136 {
17137 char *attname = PQgetvalue(res, i, 0);
17138 char *attacl = PQgetvalue(res, i, 1);
17139 char *acldefault = PQgetvalue(res, i, 2);
17140 char privtype = *(PQgetvalue(res, i, 3));
17141 char *initprivs = PQgetvalue(res, i, 4);
17143 char *attnamecopy;
17144
17145 coldacl.acl = attacl;
17146 coldacl.acldefault = acldefault;
17147 coldacl.privtype = privtype;
17148 coldacl.initprivs = initprivs;
17150
17151 /*
17152 * Column's GRANT type is always TABLE. Each column ACL depends
17153 * on the table-level ACL, since we can restore column ACLs in
17154 * parallel but the table-level ACL has to be done first.
17155 */
17156 dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
17157 "TABLE", namecopy, attnamecopy,
17158 tbinfo->dobj.namespace->dobj.name,
17159 NULL, tbinfo->rolname, &coldacl);
17161 }
17162 PQclear(res);
17163 destroyPQExpBuffer(query);
17164 }
17165
17166 free(namecopy);
17167}
17168
17169/*
17170 * Create the AS clause for a view or materialized view. The semicolon is
17171 * stripped because a materialized view must add a WITH NO DATA clause.
17172 *
17173 * This returns a new buffer which must be freed by the caller.
17174 */
17175static PQExpBuffer
17177{
17180 PGresult *res;
17181 int len;
17182
17183 /* Fetch the view definition */
17184 appendPQExpBuffer(query,
17185 "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
17186 tbinfo->dobj.catId.oid);
17187
17188 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17189
17190 if (PQntuples(res) != 1)
17191 {
17192 if (PQntuples(res) < 1)
17193 pg_fatal("query to obtain definition of view \"%s\" returned no data",
17194 tbinfo->dobj.name);
17195 else
17196 pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
17197 tbinfo->dobj.name);
17198 }
17199
17200 len = PQgetlength(res, 0, 0);
17201
17202 if (len == 0)
17203 pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17204 tbinfo->dobj.name);
17205
17206 /* Strip off the trailing semicolon so that other things may follow. */
17207 Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17208 appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17209
17210 PQclear(res);
17211 destroyPQExpBuffer(query);
17212
17213 return result;
17214}
17215
17216/*
17217 * Create a dummy AS clause for a view. This is used when the real view
17218 * definition has to be postponed because of circular dependencies.
17219 * We must duplicate the view's external properties -- column names and types
17220 * (including collation) -- so that it works for subsequent references.
17221 *
17222 * This returns a new buffer which must be freed by the caller.
17223 */
17224static PQExpBuffer
17226{
17228 int j;
17229
17230 appendPQExpBufferStr(result, "SELECT");
17231
17232 for (j = 0; j < tbinfo->numatts; j++)
17233 {
17234 if (j > 0)
17237
17238 appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17239
17240 /*
17241 * Must add collation if not default for the type, because CREATE OR
17242 * REPLACE VIEW won't change it
17243 */
17244 if (OidIsValid(tbinfo->attcollation[j]))
17245 {
17246 CollInfo *coll;
17247
17248 coll = findCollationByOid(tbinfo->attcollation[j]);
17249 if (coll)
17250 appendPQExpBuffer(result, " COLLATE %s",
17251 fmtQualifiedDumpable(coll));
17252 }
17253
17254 appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17255 }
17256
17257 return result;
17258}
17259
17260/*
17261 * dumpTableSchema
17262 * write the declaration (not data) of one user-defined table or view
17263 */
17264static void
17266{
17267 DumpOptions *dopt = fout->dopt;
17271 char *qrelname;
17272 char *qualrelname;
17273 int numParents;
17274 TableInfo **parents;
17275 int actual_atts; /* number of attrs in this CREATE statement */
17276 const char *reltypename;
17277 char *storage;
17278 int j,
17279 k;
17280
17281 /* We had better have loaded per-column details about this table */
17282 Assert(tbinfo->interesting);
17283
17284 qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17286
17287 if (tbinfo->hasoids)
17288 pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17289 qrelname);
17290
17291 if (dopt->binary_upgrade)
17293
17294 /* Is it a table or a view? */
17295 if (tbinfo->relkind == RELKIND_VIEW)
17296 {
17298
17299 /*
17300 * Note: keep this code in sync with the is_view case in dumpRule()
17301 */
17302
17303 reltypename = "VIEW";
17304
17305 if (dopt->binary_upgrade)
17307 tbinfo->dobj.catId.oid);
17308
17309 appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17310
17311 if (tbinfo->dummy_view)
17313 else
17314 {
17315 if (nonemptyReloptions(tbinfo->reloptions))
17316 {
17317 appendPQExpBufferStr(q, " WITH (");
17318 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17319 appendPQExpBufferChar(q, ')');
17320 }
17322 }
17323 appendPQExpBuffer(q, " AS\n%s", result->data);
17325
17326 if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17327 appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17328 appendPQExpBufferStr(q, ";\n");
17329 }
17330 else if (tbinfo->relkind == RELKIND_PROPGRAPH)
17331 {
17333 PGresult *res;
17334 int len;
17335
17336 reltypename = "PROPERTY GRAPH";
17337
17338 if (dopt->binary_upgrade)
17340 tbinfo->dobj.catId.oid);
17341
17342 appendPQExpBuffer(query,
17343 "SELECT pg_catalog.pg_get_propgraphdef('%u'::pg_catalog.oid) AS pgdef",
17344 tbinfo->dobj.catId.oid);
17345
17346 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17347
17348 if (PQntuples(res) != 1)
17349 {
17350 if (PQntuples(res) < 1)
17351 pg_fatal("query to obtain definition of property graph \"%s\" returned no data",
17352 tbinfo->dobj.name);
17353 else
17354 pg_fatal("query to obtain definition of property graph \"%s\" returned more than one definition",
17355 tbinfo->dobj.name);
17356 }
17357
17358 len = PQgetlength(res, 0, 0);
17359
17360 if (len == 0)
17361 pg_fatal("definition of property graph \"%s\" appears to be empty (length zero)",
17362 tbinfo->dobj.name);
17363
17364 appendPQExpBufferStr(q, PQgetvalue(res, 0, 0));
17365
17366 PQclear(res);
17367 destroyPQExpBuffer(query);
17368
17369 appendPQExpBufferStr(q, ";\n");
17370 }
17371 else
17372 {
17373 char *partkeydef = NULL;
17374 char *ftoptions = NULL;
17375 char *srvname = NULL;
17376 const char *foreign = "";
17377
17378 /*
17379 * Set reltypename, and collect any relkind-specific data that we
17380 * didn't fetch during getTables().
17381 */
17382 switch (tbinfo->relkind)
17383 {
17385 {
17387 PGresult *res;
17388
17389 reltypename = "TABLE";
17390
17391 /* retrieve partition key definition */
17392 appendPQExpBuffer(query,
17393 "SELECT pg_get_partkeydef('%u')",
17394 tbinfo->dobj.catId.oid);
17395 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17396 partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17397 PQclear(res);
17398 destroyPQExpBuffer(query);
17399 break;
17400 }
17402 {
17404 PGresult *res;
17405 int i_srvname;
17406 int i_ftoptions;
17407
17408 reltypename = "FOREIGN TABLE";
17409
17410 /* retrieve name of foreign server and generic options */
17411 appendPQExpBuffer(query,
17412 "SELECT fs.srvname, "
17413 "pg_catalog.array_to_string(ARRAY("
17414 "SELECT pg_catalog.quote_ident(option_name) || "
17415 "' ' || pg_catalog.quote_literal(option_value) "
17416 "FROM pg_catalog.pg_options_to_table(ftoptions) "
17417 "ORDER BY option_name"
17418 "), E',\n ') AS ftoptions "
17419 "FROM pg_catalog.pg_foreign_table ft "
17420 "JOIN pg_catalog.pg_foreign_server fs "
17421 "ON (fs.oid = ft.ftserver) "
17422 "WHERE ft.ftrelid = '%u'",
17423 tbinfo->dobj.catId.oid);
17424 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17425 i_srvname = PQfnumber(res, "srvname");
17426 i_ftoptions = PQfnumber(res, "ftoptions");
17429 PQclear(res);
17430 destroyPQExpBuffer(query);
17431
17432 foreign = "FOREIGN ";
17433 break;
17434 }
17435 case RELKIND_MATVIEW:
17436 reltypename = "MATERIALIZED VIEW";
17437 break;
17438 default:
17439 reltypename = "TABLE";
17440 break;
17441 }
17442
17443 numParents = tbinfo->numParents;
17444 parents = tbinfo->parents;
17445
17446 if (dopt->binary_upgrade)
17448 tbinfo->dobj.catId.oid);
17449
17450 /*
17451 * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17452 * ignore it when dumping if it was set in this case.
17453 */
17454 appendPQExpBuffer(q, "CREATE %s%s %s",
17455 (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17456 tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17457 "UNLOGGED " : "",
17459 qualrelname);
17460
17461 /*
17462 * Attach to type, if reloftype; except in case of a binary upgrade,
17463 * we dump the table normally and attach it to the type afterward.
17464 */
17465 if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17466 appendPQExpBuffer(q, " OF %s",
17467 getFormattedTypeName(fout, tbinfo->reloftype,
17468 zeroIsError));
17469
17470 if (tbinfo->relkind != RELKIND_MATVIEW)
17471 {
17472 /* Dump the attributes */
17473 actual_atts = 0;
17474 for (j = 0; j < tbinfo->numatts; j++)
17475 {
17476 /*
17477 * Normally, dump if it's locally defined in this table, and
17478 * not dropped. But for binary upgrade, we'll dump all the
17479 * columns, and then fix up the dropped and nonlocal cases
17480 * below.
17481 */
17482 if (shouldPrintColumn(dopt, tbinfo, j))
17483 {
17484 bool print_default;
17485 bool print_notnull;
17486
17487 /*
17488 * Default value --- suppress if to be printed separately
17489 * or not at all.
17490 */
17491 print_default = (tbinfo->attrdefs[j] != NULL &&
17492 tbinfo->attrdefs[j]->dobj.dump &&
17493 !tbinfo->attrdefs[j]->separate);
17494
17495 /*
17496 * Not Null constraint --- print it if it is locally
17497 * defined, or if binary upgrade. (In the latter case, we
17498 * reset conislocal below.)
17499 */
17500 print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17501 (tbinfo->notnull_islocal[j] ||
17502 dopt->binary_upgrade ||
17503 tbinfo->ispartition));
17504
17505 /*
17506 * Skip column if fully defined by reloftype, except in
17507 * binary upgrade
17508 */
17509 if (OidIsValid(tbinfo->reloftype) &&
17511 !dopt->binary_upgrade)
17512 continue;
17513
17514 /* Format properly if not first attr */
17515 if (actual_atts == 0)
17516 appendPQExpBufferStr(q, " (");
17517 else
17518 appendPQExpBufferChar(q, ',');
17519 appendPQExpBufferStr(q, "\n ");
17520 actual_atts++;
17521
17522 /* Attribute name */
17523 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17524
17525 if (tbinfo->attisdropped[j])
17526 {
17527 /*
17528 * ALTER TABLE DROP COLUMN clears
17529 * pg_attribute.atttypid, so we will not have gotten a
17530 * valid type name; insert INTEGER as a stopgap. We'll
17531 * clean things up later.
17532 */
17533 appendPQExpBufferStr(q, " INTEGER /* dummy */");
17534 /* and skip to the next column */
17535 continue;
17536 }
17537
17538 /*
17539 * Attribute type; print it except when creating a typed
17540 * table ('OF type_name'), but in binary-upgrade mode,
17541 * print it in that case too.
17542 */
17543 if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17544 {
17545 appendPQExpBuffer(q, " %s",
17546 tbinfo->atttypnames[j]);
17547 }
17548
17549 if (print_default)
17550 {
17551 if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17552 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17553 tbinfo->attrdefs[j]->adef_expr);
17554 else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17555 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17556 tbinfo->attrdefs[j]->adef_expr);
17557 else
17558 appendPQExpBuffer(q, " DEFAULT %s",
17559 tbinfo->attrdefs[j]->adef_expr);
17560 }
17561
17562 if (print_notnull)
17563 {
17564 if (tbinfo->notnull_constrs[j][0] == '\0')
17565 appendPQExpBufferStr(q, " NOT NULL");
17566 else
17567 appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17568 fmtId(tbinfo->notnull_constrs[j]));
17569
17570 if (tbinfo->notnull_noinh[j])
17571 appendPQExpBufferStr(q, " NO INHERIT");
17572 }
17573
17574 /* Add collation if not default for the type */
17575 if (OidIsValid(tbinfo->attcollation[j]))
17576 {
17577 CollInfo *coll;
17578
17579 coll = findCollationByOid(tbinfo->attcollation[j]);
17580 if (coll)
17581 appendPQExpBuffer(q, " COLLATE %s",
17582 fmtQualifiedDumpable(coll));
17583 }
17584 }
17585
17586 /*
17587 * On the other hand, if we choose not to print a column
17588 * (likely because it is created by inheritance), but the
17589 * column has a locally-defined not-null constraint, we need
17590 * to dump the constraint as a standalone object.
17591 *
17592 * This syntax isn't SQL-conforming, but if you wanted
17593 * standard output you wouldn't be creating non-standard
17594 * objects to begin with.
17595 */
17596 if (!shouldPrintColumn(dopt, tbinfo, j) &&
17597 !tbinfo->attisdropped[j] &&
17598 tbinfo->notnull_constrs[j] != NULL &&
17599 tbinfo->notnull_islocal[j])
17600 {
17601 /* Format properly if not first attr */
17602 if (actual_atts == 0)
17603 appendPQExpBufferStr(q, " (");
17604 else
17605 appendPQExpBufferChar(q, ',');
17606 appendPQExpBufferStr(q, "\n ");
17607 actual_atts++;
17608
17609 if (tbinfo->notnull_constrs[j][0] == '\0')
17610 appendPQExpBuffer(q, "NOT NULL %s",
17611 fmtId(tbinfo->attnames[j]));
17612 else
17613 appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17614 tbinfo->notnull_constrs[j],
17615 fmtId(tbinfo->attnames[j]));
17616
17617 if (tbinfo->notnull_noinh[j])
17618 appendPQExpBufferStr(q, " NO INHERIT");
17619 }
17620 }
17621
17622 /*
17623 * Add non-inherited CHECK constraints, if any.
17624 *
17625 * For partitions, we need to include check constraints even if
17626 * they're not defined locally, because the ALTER TABLE ATTACH
17627 * PARTITION that we'll emit later expects the constraint to be
17628 * there. (No need to fix conislocal: ATTACH PARTITION does that)
17629 */
17630 for (j = 0; j < tbinfo->ncheck; j++)
17631 {
17632 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17633
17634 if (constr->separate ||
17635 (!constr->conislocal && !tbinfo->ispartition))
17636 continue;
17637
17638 if (actual_atts == 0)
17639 appendPQExpBufferStr(q, " (\n ");
17640 else
17641 appendPQExpBufferStr(q, ",\n ");
17642
17643 appendPQExpBuffer(q, "CONSTRAINT %s ",
17644 fmtId(constr->dobj.name));
17645 appendPQExpBufferStr(q, constr->condef);
17646
17647 actual_atts++;
17648 }
17649
17650 if (actual_atts)
17651 appendPQExpBufferStr(q, "\n)");
17652 else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17653 {
17654 /*
17655 * No attributes? we must have a parenthesized attribute list,
17656 * even though empty, when not using the OF TYPE syntax.
17657 */
17658 appendPQExpBufferStr(q, " (\n)");
17659 }
17660
17661 /*
17662 * Emit the INHERITS clause (not for partitions), except in
17663 * binary-upgrade mode.
17664 */
17665 if (numParents > 0 && !tbinfo->ispartition &&
17666 !dopt->binary_upgrade)
17667 {
17668 appendPQExpBufferStr(q, "\nINHERITS (");
17669 for (k = 0; k < numParents; k++)
17670 {
17671 TableInfo *parentRel = parents[k];
17672
17673 if (k > 0)
17674 appendPQExpBufferStr(q, ", ");
17676 }
17677 appendPQExpBufferChar(q, ')');
17678 }
17679
17680 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17681 appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17682
17683 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17684 appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17685 }
17686
17687 if (nonemptyReloptions(tbinfo->reloptions) ||
17688 nonemptyReloptions(tbinfo->toast_reloptions))
17689 {
17690 bool addcomma = false;
17691
17692 appendPQExpBufferStr(q, "\nWITH (");
17693 if (nonemptyReloptions(tbinfo->reloptions))
17694 {
17695 addcomma = true;
17696 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17697 }
17698 if (nonemptyReloptions(tbinfo->toast_reloptions))
17699 {
17700 if (addcomma)
17701 appendPQExpBufferStr(q, ", ");
17702 appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17703 fout);
17704 }
17705 appendPQExpBufferChar(q, ')');
17706 }
17707
17708 /* Dump generic options if any */
17709 if (ftoptions && ftoptions[0])
17710 appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17711
17712 /*
17713 * For materialized views, create the AS clause just like a view. At
17714 * this point, we always mark the view as not populated.
17715 */
17716 if (tbinfo->relkind == RELKIND_MATVIEW)
17717 {
17719
17721 appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17722 result->data);
17724 }
17725 else
17726 appendPQExpBufferStr(q, ";\n");
17727
17728 /* Materialized views can depend on extensions */
17729 if (tbinfo->relkind == RELKIND_MATVIEW)
17731 "pg_catalog.pg_class",
17732 "MATERIALIZED VIEW",
17733 qualrelname);
17734
17735 /*
17736 * in binary upgrade mode, update the catalog with any missing values
17737 * that might be present.
17738 */
17739 if (dopt->binary_upgrade)
17740 {
17741 for (j = 0; j < tbinfo->numatts; j++)
17742 {
17743 if (tbinfo->attmissingval[j][0] != '\0')
17744 {
17745 appendPQExpBufferStr(q, "\n-- set missing value.\n");
17747 "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17749 appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17750 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17751 appendPQExpBufferChar(q, ',');
17752 appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17753 appendPQExpBufferStr(q, ");\n\n");
17754 }
17755 }
17756 }
17757
17758 /*
17759 * To create binary-compatible heap files, we have to ensure the same
17760 * physical column order, including dropped columns, as in the
17761 * original. Therefore, we create dropped columns above and drop them
17762 * here, also updating their attlen/attalign values so that the
17763 * dropped column can be skipped properly. (We do not bother with
17764 * restoring the original attbyval setting.) Also, inheritance
17765 * relationships are set up by doing ALTER TABLE INHERIT rather than
17766 * using an INHERITS clause --- the latter would possibly mess up the
17767 * column order. That also means we have to take care about setting
17768 * attislocal correctly, plus fix up any inherited CHECK constraints.
17769 * Analogously, we set up typed tables using ALTER TABLE / OF here.
17770 *
17771 * We process foreign and partitioned tables here, even though they
17772 * lack heap storage, because they can participate in inheritance
17773 * relationships and we want this stuff to be consistent across the
17774 * inheritance tree. We can exclude indexes, toast tables, sequences
17775 * and matviews, even though they have storage, because we don't
17776 * support altering or dropping columns in them, nor can they be part
17777 * of inheritance trees.
17778 */
17779 if (dopt->binary_upgrade &&
17780 (tbinfo->relkind == RELKIND_RELATION ||
17781 tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17782 tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17783 {
17784 bool firstitem;
17785 bool firstitem_extra;
17786
17787 /*
17788 * Drop any dropped columns. Merge the pg_attribute manipulations
17789 * into a single SQL command, so that we don't cause repeated
17790 * relcache flushes on the target table. Otherwise we risk O(N^2)
17791 * relcache bloat while dropping N columns.
17792 */
17793 resetPQExpBuffer(extra);
17794 firstitem = true;
17795 for (j = 0; j < tbinfo->numatts; j++)
17796 {
17797 if (tbinfo->attisdropped[j])
17798 {
17799 if (firstitem)
17800 {
17801 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17802 "UPDATE pg_catalog.pg_attribute\n"
17803 "SET attlen = v.dlen, "
17804 "attalign = v.dalign, "
17805 "attbyval = false\n"
17806 "FROM (VALUES ");
17807 firstitem = false;
17808 }
17809 else
17810 appendPQExpBufferStr(q, ",\n ");
17811 appendPQExpBufferChar(q, '(');
17812 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17813 appendPQExpBuffer(q, ", %d, '%c')",
17814 tbinfo->attlen[j],
17815 tbinfo->attalign[j]);
17816 /* The ALTER ... DROP COLUMN commands must come after */
17817 appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17819 appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17820 fmtId(tbinfo->attnames[j]));
17821 }
17822 }
17823 if (!firstitem)
17824 {
17825 appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17826 "WHERE attrelid = ");
17828 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17829 " AND attname = v.dname;\n");
17830 /* Now we can issue the actual DROP COLUMN commands */
17831 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17832 }
17833
17834 /*
17835 * Fix up inherited columns. As above, do the pg_attribute
17836 * manipulations in a single SQL command.
17837 */
17838 firstitem = true;
17839 for (j = 0; j < tbinfo->numatts; j++)
17840 {
17841 if (!tbinfo->attisdropped[j] &&
17842 !tbinfo->attislocal[j])
17843 {
17844 if (firstitem)
17845 {
17846 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17847 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17848 "SET attislocal = false\n"
17849 "WHERE attrelid = ");
17851 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17852 " AND attname IN (");
17853 firstitem = false;
17854 }
17855 else
17856 appendPQExpBufferStr(q, ", ");
17857 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17858 }
17859 }
17860 if (!firstitem)
17861 appendPQExpBufferStr(q, ");\n");
17862
17863 /*
17864 * Fix up not-null constraints that come from inheritance. As
17865 * above, do the pg_constraint manipulations in a single SQL
17866 * command. (Actually, two in special cases, if we're doing an
17867 * upgrade from < 18).
17868 */
17869 firstitem = true;
17870 firstitem_extra = true;
17871 resetPQExpBuffer(extra);
17872 for (j = 0; j < tbinfo->numatts; j++)
17873 {
17874 /*
17875 * If a not-null constraint comes from inheritance, reset
17876 * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17877 * below. Special hack: in versions < 18, columns with no
17878 * local definition need their constraint to be matched by
17879 * column number in conkeys instead of by constraint name,
17880 * because the latter is not available. (We distinguish the
17881 * case because the constraint name is the empty string.)
17882 */
17883 if (tbinfo->notnull_constrs[j] != NULL &&
17884 !tbinfo->notnull_islocal[j])
17885 {
17886 if (tbinfo->notnull_constrs[j][0] != '\0')
17887 {
17888 if (firstitem)
17889 {
17890 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17891 "SET conislocal = false\n"
17892 "WHERE contype = 'n' AND conrelid = ");
17894 appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17895 "conname IN (");
17896 firstitem = false;
17897 }
17898 else
17899 appendPQExpBufferStr(q, ", ");
17900 appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17901 }
17902 else
17903 {
17904 if (firstitem_extra)
17905 {
17906 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17907 "SET conislocal = false\n"
17908 "WHERE contype = 'n' AND conrelid = ");
17910 appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17911 "conkey IN (");
17912 firstitem_extra = false;
17913 }
17914 else
17915 appendPQExpBufferStr(extra, ", ");
17916 appendPQExpBuffer(extra, "'{%d}'", j + 1);
17917 }
17918 }
17919 }
17920 if (!firstitem)
17921 appendPQExpBufferStr(q, ");\n");
17922 if (!firstitem_extra)
17923 appendPQExpBufferStr(extra, ");\n");
17924
17925 if (extra->len > 0)
17926 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17927
17928 /*
17929 * Add inherited CHECK constraints, if any.
17930 *
17931 * For partitions, they were already dumped, and conislocal
17932 * doesn't need fixing.
17933 *
17934 * As above, issue only one direct manipulation of pg_constraint.
17935 * Although it is tempting to merge the ALTER ADD CONSTRAINT
17936 * commands into one as well, refrain for now due to concern about
17937 * possible backend memory bloat if there are many such
17938 * constraints.
17939 */
17940 resetPQExpBuffer(extra);
17941 firstitem = true;
17942 for (k = 0; k < tbinfo->ncheck; k++)
17943 {
17944 ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17945
17946 if (constr->separate || constr->conislocal || tbinfo->ispartition)
17947 continue;
17948
17949 if (firstitem)
17950 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17951 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17953 fmtId(constr->dobj.name),
17954 constr->condef);
17955 /* Update pg_constraint after all the ALTER TABLEs */
17956 if (firstitem)
17957 {
17958 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17959 "SET conislocal = false\n"
17960 "WHERE contype = 'c' AND conrelid = ");
17962 appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17963 appendPQExpBufferStr(extra, " AND conname IN (");
17964 firstitem = false;
17965 }
17966 else
17967 appendPQExpBufferStr(extra, ", ");
17968 appendStringLiteralAH(extra, constr->dobj.name, fout);
17969 }
17970 if (!firstitem)
17971 {
17972 appendPQExpBufferStr(extra, ");\n");
17973 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17974 }
17975
17976 if (numParents > 0 && !tbinfo->ispartition)
17977 {
17978 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17979 for (k = 0; k < numParents; k++)
17980 {
17981 TableInfo *parentRel = parents[k];
17982
17983 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17986 }
17987 }
17988
17989 if (OidIsValid(tbinfo->reloftype))
17990 {
17991 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17992 appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17994 getFormattedTypeName(fout, tbinfo->reloftype,
17995 zeroIsError));
17996 }
17997 }
17998
17999 /*
18000 * In binary_upgrade mode, arrange to restore the old relfrozenxid and
18001 * relminmxid of all vacuumable relations. (While vacuum.c processes
18002 * TOAST tables semi-independently, here we see them only as children
18003 * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
18004 * child toast table is handled below.)
18005 */
18006 if (dopt->binary_upgrade &&
18007 (tbinfo->relkind == RELKIND_RELATION ||
18008 tbinfo->relkind == RELKIND_MATVIEW))
18009 {
18010 appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
18011 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
18012 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
18013 "WHERE oid = ",
18014 tbinfo->frozenxid, tbinfo->minmxid);
18016 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
18017
18018 if (tbinfo->toast_oid)
18019 {
18020 /*
18021 * The toast table will have the same OID at restore, so we
18022 * can safely target it by OID.
18023 */
18024 appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
18025 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
18026 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
18027 "WHERE oid = '%u';\n",
18028 tbinfo->toast_frozenxid,
18029 tbinfo->toast_minmxid, tbinfo->toast_oid);
18030 }
18031 }
18032
18033 /*
18034 * In binary_upgrade mode, restore matviews' populated status by
18035 * poking pg_class directly. This is pretty ugly, but we can't use
18036 * REFRESH MATERIALIZED VIEW since it's possible that some underlying
18037 * matview is not populated even though this matview is; in any case,
18038 * we want to transfer the matview's heap storage, not run REFRESH.
18039 */
18040 if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
18041 tbinfo->relispopulated)
18042 {
18043 appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
18044 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
18045 "SET relispopulated = 't'\n"
18046 "WHERE oid = ");
18048 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
18049 }
18050
18051 /*
18052 * Dump additional per-column properties that we can't handle in the
18053 * main CREATE TABLE command.
18054 */
18055 for (j = 0; j < tbinfo->numatts; j++)
18056 {
18057 /* None of this applies to dropped columns */
18058 if (tbinfo->attisdropped[j])
18059 continue;
18060
18061 /*
18062 * Dump per-column statistics information. We only issue an ALTER
18063 * TABLE statement if the attstattarget entry for this column is
18064 * not the default value.
18065 */
18066 if (tbinfo->attstattarget[j] >= 0)
18067 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
18069 fmtId(tbinfo->attnames[j]),
18070 tbinfo->attstattarget[j]);
18071
18072 /*
18073 * Dump per-column storage information. The statement is only
18074 * dumped if the storage has been changed from the type's default.
18075 */
18076 if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
18077 {
18078 switch (tbinfo->attstorage[j])
18079 {
18080 case TYPSTORAGE_PLAIN:
18081 storage = "PLAIN";
18082 break;
18084 storage = "EXTERNAL";
18085 break;
18087 storage = "EXTENDED";
18088 break;
18089 case TYPSTORAGE_MAIN:
18090 storage = "MAIN";
18091 break;
18092 default:
18093 storage = NULL;
18094 }
18095
18096 /*
18097 * Only dump the statement if it's a storage type we recognize
18098 */
18099 if (storage != NULL)
18100 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
18102 fmtId(tbinfo->attnames[j]),
18103 storage);
18104 }
18105
18106 /*
18107 * Dump per-column compression, if it's been set.
18108 */
18109 if (!dopt->no_toast_compression)
18110 {
18111 const char *cmname;
18112
18113 switch (tbinfo->attcompression[j])
18114 {
18115 case 'p':
18116 cmname = "pglz";
18117 break;
18118 case 'l':
18119 cmname = "lz4";
18120 break;
18121 default:
18122 cmname = NULL;
18123 break;
18124 }
18125
18126 if (cmname != NULL)
18127 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
18129 fmtId(tbinfo->attnames[j]),
18130 cmname);
18131 }
18132
18133 /*
18134 * Dump per-column attributes.
18135 */
18136 if (tbinfo->attoptions[j][0] != '\0')
18137 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
18139 fmtId(tbinfo->attnames[j]),
18140 tbinfo->attoptions[j]);
18141
18142 /*
18143 * Dump per-column fdw options.
18144 */
18145 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
18146 tbinfo->attfdwoptions[j][0] != '\0')
18148 "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
18149 " %s\n"
18150 ");\n",
18152 fmtId(tbinfo->attnames[j]),
18153 tbinfo->attfdwoptions[j]);
18154 } /* end loop over columns */
18155
18157 free(ftoptions);
18158 free(srvname);
18159 }
18160
18161 /*
18162 * dump properties we only have ALTER TABLE syntax for
18163 */
18164 if ((tbinfo->relkind == RELKIND_RELATION ||
18165 tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
18166 tbinfo->relkind == RELKIND_MATVIEW) &&
18167 tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
18168 {
18169 if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
18170 {
18171 /* nothing to do, will be set when the index is dumped */
18172 }
18173 else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
18174 {
18175 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
18176 qualrelname);
18177 }
18178 else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
18179 {
18180 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
18181 qualrelname);
18182 }
18183 }
18184
18185 if (tbinfo->forcerowsec)
18186 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
18187 qualrelname);
18188
18189 appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
18190
18191 if (dopt->binary_upgrade)
18194 tbinfo->dobj.namespace->dobj.name);
18195
18196 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18197 {
18198 char *tablespace = NULL;
18199 char *tableam = NULL;
18200
18201 /*
18202 * _selectTablespace() relies on tablespace-enabled objects in the
18203 * default tablespace to have a tablespace of "" (empty string) versus
18204 * non-tablespace-enabled objects to have a tablespace of NULL.
18205 * getTables() sets tbinfo->reltablespace to "" for the default
18206 * tablespace (not NULL).
18207 */
18208 if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
18209 tablespace = tbinfo->reltablespace;
18210
18211 if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
18213 tableam = tbinfo->amname;
18214
18215 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
18216 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18217 .namespace = tbinfo->dobj.namespace->dobj.name,
18218 .tablespace = tablespace,
18219 .tableam = tableam,
18220 .relkind = tbinfo->relkind,
18221 .owner = tbinfo->rolname,
18222 .description = reltypename,
18223 .section = tbinfo->postponed_def ?
18225 .createStmt = q->data,
18226 .dropStmt = delq->data));
18227 }
18228
18229 /* Dump Table Comments */
18230 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18232
18233 /* Dump Table Security Labels */
18234 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18236
18237 /*
18238 * Dump comments for not-null constraints that aren't to be dumped
18239 * separately (those are processed by collectComments/dumpComment).
18240 */
18241 if (!fout->dopt->no_comments && dopt->dumpSchema &&
18242 fout->remoteVersion >= 180000)
18243 {
18245 PQExpBuffer tag = NULL;
18246
18247 for (j = 0; j < tbinfo->numatts; j++)
18248 {
18249 if (tbinfo->notnull_constrs[j] != NULL &&
18250 tbinfo->notnull_comment[j] != NULL)
18251 {
18252 if (comment == NULL)
18253 {
18255 tag = createPQExpBuffer();
18256 }
18257 else
18258 {
18260 resetPQExpBuffer(tag);
18261 }
18262
18263 appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18264 fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18265 appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
18267
18268 appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18269 fmtId(tbinfo->notnull_constrs[j]), qrelname);
18270
18272 ARCHIVE_OPTS(.tag = tag->data,
18273 .namespace = tbinfo->dobj.namespace->dobj.name,
18274 .owner = tbinfo->rolname,
18275 .description = "COMMENT",
18276 .section = SECTION_NONE,
18277 .createStmt = comment->data,
18278 .deps = &(tbinfo->dobj.dumpId),
18279 .nDeps = 1));
18280 }
18281 }
18282
18284 destroyPQExpBuffer(tag);
18285 }
18286
18287 /* Dump comments on inlined table constraints */
18288 for (j = 0; j < tbinfo->ncheck; j++)
18289 {
18290 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18291
18292 if (constr->separate || !constr->conislocal)
18293 continue;
18294
18295 if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18297 }
18298
18301 destroyPQExpBuffer(extra);
18302 free(qrelname);
18304}
18305
18306/*
18307 * dumpTableAttach
18308 * write to fout the commands to attach a child partition
18309 *
18310 * Child partitions are always made by creating them separately
18311 * and then using ATTACH PARTITION, rather than using
18312 * CREATE TABLE ... PARTITION OF. This is important for preserving
18313 * any possible discrepancy in column layout, to allow assigning the
18314 * correct tablespace if different, and so that it's possible to restore
18315 * a partition without restoring its parent. (You'll get an error from
18316 * the ATTACH PARTITION command, but that can be ignored, or skipped
18317 * using "pg_restore -L" if you prefer.) The last point motivates
18318 * treating ATTACH PARTITION as a completely separate ArchiveEntry
18319 * rather than emitting it within the child partition's ArchiveEntry.
18320 */
18321static void
18323{
18324 DumpOptions *dopt = fout->dopt;
18325 PQExpBuffer q;
18326 PGresult *res;
18327 char *partbound;
18328
18329 /* Do nothing if not dumping schema */
18330 if (!dopt->dumpSchema)
18331 return;
18332
18333 q = createPQExpBuffer();
18334
18336 {
18337 /* Set up query for partbound details */
18339 "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18340
18342 "SELECT pg_get_expr(c.relpartbound, c.oid) "
18343 "FROM pg_class c "
18344 "WHERE c.oid = $1");
18345
18347
18349 }
18350
18352 "EXECUTE dumpTableAttach('%u')",
18353 attachinfo->partitionTbl->dobj.catId.oid);
18354
18356 partbound = PQgetvalue(res, 0, 0);
18357
18358 /* Perform ALTER TABLE on the parent */
18360 "ALTER TABLE ONLY %s ",
18361 fmtQualifiedDumpable(attachinfo->parentTbl));
18363 "ATTACH PARTITION %s %s;\n",
18364 fmtQualifiedDumpable(attachinfo->partitionTbl),
18365 partbound);
18366
18367 /*
18368 * There is no point in creating a drop query as the drop is done by table
18369 * drop. (If you think to change this, see also _printTocEntry().)
18370 * Although this object doesn't really have ownership as such, set the
18371 * owner field anyway to ensure that the command is run by the correct
18372 * role at restore time.
18373 */
18374 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18375 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18376 .namespace = attachinfo->dobj.namespace->dobj.name,
18377 .owner = attachinfo->partitionTbl->rolname,
18378 .description = "TABLE ATTACH",
18379 .section = SECTION_PRE_DATA,
18380 .createStmt = q->data));
18381
18382 PQclear(res);
18384}
18385
18386/*
18387 * dumpAttrDef --- dump an attribute's default-value declaration
18388 */
18389static void
18391{
18392 DumpOptions *dopt = fout->dopt;
18393 TableInfo *tbinfo = adinfo->adtable;
18394 int adnum = adinfo->adnum;
18395 PQExpBuffer q;
18397 char *qualrelname;
18398 char *tag;
18399 char *foreign;
18400
18401 /* Do nothing if not dumping schema */
18402 if (!dopt->dumpSchema)
18403 return;
18404
18405 /* Skip if not "separate"; it was dumped in the table's definition */
18406 if (!adinfo->separate)
18407 return;
18408
18409 q = createPQExpBuffer();
18411
18413
18414 foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18415
18417 "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18418 foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18419 adinfo->adef_expr);
18420
18421 appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18423 fmtId(tbinfo->attnames[adnum - 1]));
18424
18425 tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18426
18427 if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18428 ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18429 ARCHIVE_OPTS(.tag = tag,
18430 .namespace = tbinfo->dobj.namespace->dobj.name,
18431 .owner = tbinfo->rolname,
18432 .description = "DEFAULT",
18433 .section = SECTION_PRE_DATA,
18434 .createStmt = q->data,
18435 .dropStmt = delq->data));
18436
18437 free(tag);
18441}
18442
18443/*
18444 * getAttrName: extract the correct name for an attribute
18445 *
18446 * The array tblInfo->attnames[] only provides names of user attributes;
18447 * if a system attribute number is supplied, we have to fake it.
18448 * We also do a little bit of bounds checking for safety's sake.
18449 */
18450static const char *
18451getAttrName(int attrnum, const TableInfo *tblInfo)
18452{
18453 if (attrnum > 0 && attrnum <= tblInfo->numatts)
18454 return tblInfo->attnames[attrnum - 1];
18455 switch (attrnum)
18456 {
18458 return "ctid";
18460 return "xmin";
18462 return "cmin";
18464 return "xmax";
18466 return "cmax";
18468 return "tableoid";
18469 }
18470 pg_fatal("invalid column number %d for table \"%s\"",
18471 attrnum, tblInfo->dobj.name);
18472 return NULL; /* keep compiler quiet */
18473}
18474
18475/*
18476 * dumpIndex
18477 * write out to fout a user-defined index
18478 */
18479static void
18481{
18482 DumpOptions *dopt = fout->dopt;
18483 TableInfo *tbinfo = indxinfo->indextable;
18484 bool is_constraint = (indxinfo->indexconstraint != 0);
18485 PQExpBuffer q;
18487 char *qindxname;
18488 char *qqindxname;
18489
18490 /* Do nothing if not dumping schema */
18491 if (!dopt->dumpSchema)
18492 return;
18493
18494 q = createPQExpBuffer();
18496
18497 qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18499
18500 /*
18501 * If there's an associated constraint, don't dump the index per se, but
18502 * do dump any comment for it. (This is safe because dependency ordering
18503 * will have ensured the constraint is emitted first.) Note that the
18504 * emitted comment has to be shown as depending on the constraint, not the
18505 * index, in such cases.
18506 */
18507 if (!is_constraint)
18508 {
18509 char *indstatcols = indxinfo->indstatcols;
18510 char *indstatvals = indxinfo->indstatvals;
18511 char **indstatcolsarray = NULL;
18512 char **indstatvalsarray = NULL;
18513 int nstatcols = 0;
18514 int nstatvals = 0;
18515
18516 if (dopt->binary_upgrade)
18518 indxinfo->dobj.catId.oid);
18519
18520 /* Plain secondary index */
18521 appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18522
18523 /*
18524 * Append ALTER TABLE commands as needed to set properties that we
18525 * only have ALTER TABLE syntax for. Keep this in sync with the
18526 * similar code in dumpConstraint!
18527 */
18528
18529 /* If the index is clustered, we need to record that. */
18530 if (indxinfo->indisclustered)
18531 {
18532 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18534 /* index name is not qualified in this syntax */
18535 appendPQExpBuffer(q, " ON %s;\n",
18536 qindxname);
18537 }
18538
18539 /*
18540 * If the index has any statistics on some of its columns, generate
18541 * the associated ALTER INDEX queries.
18542 */
18543 if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18544 {
18545 int j;
18546
18547 if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18548 pg_fatal("could not parse index statistic columns");
18549 if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18550 pg_fatal("could not parse index statistic values");
18551 if (nstatcols != nstatvals)
18552 pg_fatal("mismatched number of columns and values for index statistics");
18553
18554 for (j = 0; j < nstatcols; j++)
18555 {
18556 appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18557
18558 /*
18559 * Note that this is a column number, so no quotes should be
18560 * used.
18561 */
18562 appendPQExpBuffer(q, "ALTER COLUMN %s ",
18564 appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18566 }
18567 }
18568
18569 /* Indexes can depend on extensions */
18571 "pg_catalog.pg_class",
18572 "INDEX", qqindxname);
18573
18574 /* If the index defines identity, we need to record that. */
18575 if (indxinfo->indisreplident)
18576 {
18577 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18579 /* index name is not qualified in this syntax */
18580 appendPQExpBuffer(q, " INDEX %s;\n",
18581 qindxname);
18582 }
18583
18584 /*
18585 * If this index is a member of a partitioned index, the backend will
18586 * not allow us to drop it separately, so don't try. It will go away
18587 * automatically when we drop either the index's table or the
18588 * partitioned index. (If, in a selective restore with --clean, we
18589 * drop neither of those, then this index will not be dropped either.
18590 * But that's fine, and even if you think it's not, the backend won't
18591 * let us do differently.)
18592 */
18593 if (indxinfo->parentidx == 0)
18594 appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18595
18596 if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18597 ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18598 ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18599 .namespace = tbinfo->dobj.namespace->dobj.name,
18600 .tablespace = indxinfo->tablespace,
18601 .owner = tbinfo->rolname,
18602 .description = "INDEX",
18603 .section = SECTION_POST_DATA,
18604 .createStmt = q->data,
18605 .dropStmt = delq->data));
18606
18609 }
18610
18611 /* Dump Index Comments */
18612 if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18613 dumpComment(fout, "INDEX", qindxname,
18614 tbinfo->dobj.namespace->dobj.name,
18615 tbinfo->rolname,
18616 indxinfo->dobj.catId, 0,
18617 is_constraint ? indxinfo->indexconstraint :
18618 indxinfo->dobj.dumpId);
18619
18622 free(qindxname);
18624}
18625
18626/*
18627 * dumpIndexAttach
18628 * write out to fout a partitioned-index attachment clause
18629 */
18630static void
18632{
18633 /* Do nothing if not dumping schema */
18634 if (!fout->dopt->dumpSchema)
18635 return;
18636
18637 if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18638 {
18640
18641 appendPQExpBuffer(q, "ALTER INDEX %s ",
18642 fmtQualifiedDumpable(attachinfo->parentIdx));
18643 appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18644 fmtQualifiedDumpable(attachinfo->partitionIdx));
18645
18646 /*
18647 * There is no need for a dropStmt since the drop is done implicitly
18648 * when we drop either the index's table or the partitioned index.
18649 * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18650 * there's no way to do it anyway. (If you think to change this,
18651 * consider also what to do with --if-exists.)
18652 *
18653 * Although this object doesn't really have ownership as such, set the
18654 * owner field anyway to ensure that the command is run by the correct
18655 * role at restore time.
18656 */
18657 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18658 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18659 .namespace = attachinfo->dobj.namespace->dobj.name,
18660 .owner = attachinfo->parentIdx->indextable->rolname,
18661 .description = "INDEX ATTACH",
18662 .section = SECTION_POST_DATA,
18663 .createStmt = q->data));
18664
18666 }
18667}
18668
18669/*
18670 * dumpStatisticsExt
18671 * write out to fout an extended statistics object
18672 */
18673static void
18675{
18676 DumpOptions *dopt = fout->dopt;
18677 PQExpBuffer q;
18679 PQExpBuffer query;
18680 char *qstatsextname;
18681 PGresult *res;
18682 char *stxdef;
18683
18684 /* Do nothing if not dumping schema */
18685 if (!dopt->dumpSchema)
18686 return;
18687
18688 q = createPQExpBuffer();
18690 query = createPQExpBuffer();
18691
18693
18694 appendPQExpBuffer(query, "SELECT "
18695 "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18696 statsextinfo->dobj.catId.oid);
18697
18698 res = ExecuteSqlQueryForSingleRow(fout, query->data);
18699
18700 stxdef = PQgetvalue(res, 0, 0);
18701
18702 /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18703 appendPQExpBuffer(q, "%s;\n", stxdef);
18704
18705 /*
18706 * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18707 * for this statistics object is not the default value.
18708 */
18709 if (statsextinfo->stattarget >= 0)
18710 {
18711 appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18713 appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18714 statsextinfo->stattarget);
18715 }
18716
18717 appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18719
18720 if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18721 ArchiveEntry(fout, statsextinfo->dobj.catId,
18722 statsextinfo->dobj.dumpId,
18723 ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18724 .namespace = statsextinfo->dobj.namespace->dobj.name,
18725 .owner = statsextinfo->rolname,
18726 .description = "STATISTICS",
18727 .section = SECTION_POST_DATA,
18728 .createStmt = q->data,
18729 .dropStmt = delq->data));
18730
18731 /* Dump Statistics Comments */
18732 if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18733 dumpComment(fout, "STATISTICS", qstatsextname,
18734 statsextinfo->dobj.namespace->dobj.name,
18735 statsextinfo->rolname,
18736 statsextinfo->dobj.catId, 0,
18737 statsextinfo->dobj.dumpId);
18738
18739 PQclear(res);
18742 destroyPQExpBuffer(query);
18744}
18745
18746/*
18747 * dumpStatisticsExtStats
18748 * write out to fout the stats for an extended statistics object
18749 */
18750static void
18752{
18753 DumpOptions *dopt = fout->dopt;
18754 PQExpBuffer query;
18755 PGresult *res;
18756 int nstats;
18757
18758 /* Do nothing if not dumping statistics */
18759 if (!dopt->dumpStatistics)
18760 return;
18761
18763 {
18765
18766 /*---------
18767 * Set up query for details about extended statistics objects.
18768 *
18769 * The query depends on the backend version:
18770 * - In v19 and newer versions, query directly the pg_stats_ext*
18771 * catalogs.
18772 * - In v18 and older versions, ndistinct and dependencies have a
18773 * different format that needs translation.
18774 * - In v14 and older versions, inherited does not exist.
18775 * - In v11 and older versions, there is no pg_stats_ext, hence
18776 * the logic joins pg_statistic_ext and pg_namespace.
18777 *---------
18778 */
18779
18781 "PREPARE getExtStatsStats(pg_catalog.name, pg_catalog.name) AS\n"
18782 "SELECT ");
18783
18784 /*
18785 * Versions 15 and newer have inherited stats.
18786 *
18787 * Create this column in all versions because we need to order by it
18788 * later.
18789 */
18790 if (fout->remoteVersion >= 150000)
18791 appendPQExpBufferStr(pq, "e.inherited, ");
18792 else
18793 appendPQExpBufferStr(pq, "false AS inherited, ");
18794
18795 /*--------
18796 * The ndistinct and dependencies formats changed in v19, so
18797 * everything before that needs to be translated.
18798 *
18799 * The ndistinct translation converts this kind of data:
18800 * {"3, 4": 11, "3, 6": 11, "4, 6": 11, "3, 4, 6": 11}
18801 *
18802 * to this:
18803 * [ {"attributes": [3,4], "ndistinct": 11},
18804 * {"attributes": [3,6], "ndistinct": 11},
18805 * {"attributes": [4,6], "ndistinct": 11},
18806 * {"attributes": [3,4,6], "ndistinct": 11} ]
18807 *
18808 * The dependencies translation converts this kind of data:
18809 * {"3 => 4": 1.000000, "3 => 6": 1.000000,
18810 * "4 => 6": 1.000000, "3, 4 => 6": 1.000000,
18811 * "3, 6 => 4": 1.000000}
18812 *
18813 * to this:
18814 * [ {"attributes": [3], "dependency": 4, "degree": 1.000000},
18815 * {"attributes": [3], "dependency": 6, "degree": 1.000000},
18816 * {"attributes": [4], "dependency": 6, "degree": 1.000000},
18817 * {"attributes": [3,4], "dependency": 6, "degree": 1.000000},
18818 * {"attributes": [3,6], "dependency": 4, "degree": 1.000000} ]
18819 *--------
18820 */
18821 if (fout->remoteVersion >= 190000)
18822 appendPQExpBufferStr(pq, "e.n_distinct, e.dependencies, ");
18823 else
18825 "( "
18826 "SELECT json_agg( "
18827 " json_build_object( "
18829 " string_to_array(kv.key, ', ')::integer[], "
18831 " kv.value::bigint )) "
18832 "FROM json_each_text(e.n_distinct::text::json) AS kv"
18833 ") AS n_distinct, "
18834 "( "
18835 "SELECT json_agg( "
18836 " json_build_object( "
18838 " string_to_array( "
18839 " split_part(kv.key, ' => ', 1), "
18840 " ', ')::integer[], "
18842 " split_part(kv.key, ' => ', 2)::integer, "
18844 " kv.value::double precision )) "
18845 "FROM json_each_text(e.dependencies::text::json) AS kv "
18846 ") AS dependencies, ");
18847
18848 /* MCV was introduced v13 */
18849 if (fout->remoteVersion >= 130000)
18851 "e.most_common_vals, e.most_common_freqs, "
18852 "e.most_common_base_freqs, ");
18853 else
18855 "NULL AS most_common_vals, NULL AS most_common_freqs, "
18856 "NULL AS most_common_base_freqs, ");
18857
18858 /* Expressions were introduced in v14 */
18859 if (fout->remoteVersion >= 140000)
18860 {
18861 /*
18862 * There is no ordering column in pg_stats_ext_exprs. However, we
18863 * can rely on the unnesting of pg_statistic_ext_data.stxdexpr to
18864 * maintain the desired order of expression elements.
18865 */
18867 "( "
18868 "SELECT jsonb_pretty(jsonb_agg("
18869 "nullif(j.obj, '{}'::jsonb))) "
18870 "FROM pg_stats_ext_exprs AS ee "
18871 "CROSS JOIN LATERAL jsonb_strip_nulls("
18872 " jsonb_build_object( "
18873 " 'null_frac', ee.null_frac::text, "
18874 " 'avg_width', ee.avg_width::text, "
18875 " 'n_distinct', ee.n_distinct::text, "
18876 " 'most_common_vals', ee.most_common_vals::text, "
18877 " 'most_common_freqs', ee.most_common_freqs::text, "
18878 " 'histogram_bounds', ee.histogram_bounds::text, "
18879 " 'correlation', ee.correlation::text, "
18880 " 'most_common_elems', ee.most_common_elems::text, "
18881 " 'most_common_elem_freqs', ee.most_common_elem_freqs::text, "
18882 " 'elem_count_histogram', ee.elem_count_histogram::text");
18883
18884 /* These three have been added to pg_stats_ext_exprs in v19. */
18885 if (fout->remoteVersion >= 190000)
18887 ", "
18888 " 'range_length_histogram', ee.range_length_histogram::text, "
18889 " 'range_empty_frac', ee.range_empty_frac::text, "
18890 " 'range_bounds_histogram', ee.range_bounds_histogram::text");
18891
18893 " )) AS j(obj)"
18894 "WHERE ee.statistics_schemaname = $1 "
18895 "AND ee.statistics_name = $2 ");
18896 /* Inherited expressions introduced in v15 */
18897 if (fout->remoteVersion >= 150000)
18898 appendPQExpBufferStr(pq, "AND ee.inherited = e.inherited");
18899
18900 appendPQExpBufferStr(pq, ") AS exprs ");
18901 }
18902 else
18903 appendPQExpBufferStr(pq, "NULL AS exprs ");
18904
18905 /* pg_stats_ext introduced in v12 */
18906 if (fout->remoteVersion >= 120000)
18908 "FROM pg_catalog.pg_stats_ext AS e "
18909 "WHERE e.statistics_schemaname = $1 "
18910 "AND e.statistics_name = $2 ");
18911 else
18913 "FROM ( "
18914 "SELECT s.stxndistinct AS n_distinct, "
18915 " s.stxdependencies AS dependencies "
18916 "FROM pg_catalog.pg_statistic_ext AS s "
18917 "JOIN pg_catalog.pg_namespace AS n "
18918 "ON n.oid = s.stxnamespace "
18919 "WHERE n.nspname = $1 "
18920 "AND s.stxname = $2 "
18921 ") AS e ");
18922
18923 /* we always have an inherited column, but it may be a constant */
18924 appendPQExpBufferStr(pq, "ORDER BY inherited");
18925
18926 ExecuteSqlStatement(fout, pq->data);
18927
18929
18931 }
18932
18933 query = createPQExpBuffer();
18934
18935 appendPQExpBufferStr(query, "EXECUTE getExtStatsStats(");
18936 appendStringLiteralAH(query, statsextinfo->dobj.namespace->dobj.name, fout);
18937 appendPQExpBufferStr(query, "::pg_catalog.name, ");
18938 appendStringLiteralAH(query, statsextinfo->dobj.name, fout);
18939 appendPQExpBufferStr(query, "::pg_catalog.name)");
18940
18941 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18942
18943 destroyPQExpBuffer(query);
18944
18945 nstats = PQntuples(res);
18946
18947 if (nstats > 0)
18948 {
18950
18951 int i_inherited = PQfnumber(res, "inherited");
18952 int i_ndistinct = PQfnumber(res, "n_distinct");
18953 int i_dependencies = PQfnumber(res, "dependencies");
18954 int i_mcv = PQfnumber(res, "most_common_vals");
18955 int i_mcf = PQfnumber(res, "most_common_freqs");
18956 int i_mcbf = PQfnumber(res, "most_common_base_freqs");
18957 int i_exprs = PQfnumber(res, "exprs");
18958
18959 for (int i = 0; i < nstats; i++)
18960 {
18961 TableInfo *tbinfo = statsextinfo->stattable;
18962
18963 if (PQgetisnull(res, i, i_inherited))
18964 pg_fatal("inherited cannot be NULL");
18965
18967 "SELECT * FROM pg_catalog.pg_restore_extended_stats(\n");
18968 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
18970
18971 /* Relation information */
18972 appendPQExpBufferStr(out, "\t'schemaname', ");
18973 appendStringLiteralAH(out, tbinfo->dobj.namespace->dobj.name, fout);
18974 appendPQExpBufferStr(out, ",\n\t'relname', ");
18975 appendStringLiteralAH(out, tbinfo->dobj.name, fout);
18976
18977 /* Extended statistics information */
18978 appendPQExpBufferStr(out, ",\n\t'statistics_schemaname', ");
18979 appendStringLiteralAH(out, statsextinfo->dobj.namespace->dobj.name, fout);
18980 appendPQExpBufferStr(out, ",\n\t'statistics_name', ");
18981 appendStringLiteralAH(out, statsextinfo->dobj.name, fout);
18982 appendNamedArgument(out, fout, "inherited", "boolean",
18983 PQgetvalue(res, i, i_inherited));
18984
18985 if (!PQgetisnull(res, i, i_ndistinct))
18986 appendNamedArgument(out, fout, "n_distinct", "pg_ndistinct",
18987 PQgetvalue(res, i, i_ndistinct));
18988
18989 if (!PQgetisnull(res, i, i_dependencies))
18990 appendNamedArgument(out, fout, "dependencies", "pg_dependencies",
18991 PQgetvalue(res, i, i_dependencies));
18992
18993 if (!PQgetisnull(res, i, i_mcv))
18994 appendNamedArgument(out, fout, "most_common_vals", "text[]",
18995 PQgetvalue(res, i, i_mcv));
18996
18997 if (!PQgetisnull(res, i, i_mcf))
18998 appendNamedArgument(out, fout, "most_common_freqs", "double precision[]",
18999 PQgetvalue(res, i, i_mcf));
19000
19001 if (!PQgetisnull(res, i, i_mcbf))
19002 appendNamedArgument(out, fout, "most_common_base_freqs", "double precision[]",
19003 PQgetvalue(res, i, i_mcbf));
19004
19005 if (!PQgetisnull(res, i, i_exprs))
19006 appendNamedArgument(out, fout, "exprs", "jsonb",
19007 PQgetvalue(res, i, i_exprs));
19008
19009 appendPQExpBufferStr(out, "\n);\n");
19010 }
19011
19013 ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
19014 .namespace = statsextinfo->dobj.namespace->dobj.name,
19015 .owner = statsextinfo->rolname,
19016 .description = "EXTENDED STATISTICS DATA",
19017 .section = SECTION_POST_DATA,
19018 .createStmt = out->data,
19019 .deps = &statsextinfo->dobj.dumpId,
19020 .nDeps = 1));
19021 destroyPQExpBuffer(out);
19022 }
19023 PQclear(res);
19024}
19025
19026/*
19027 * dumpConstraint
19028 * write out to fout a user-defined constraint
19029 */
19030static void
19032{
19033 DumpOptions *dopt = fout->dopt;
19034 TableInfo *tbinfo = coninfo->contable;
19035 PQExpBuffer q;
19037 char *tag = NULL;
19038 char *foreign;
19039
19040 /* Do nothing if not dumping schema */
19041 if (!dopt->dumpSchema)
19042 return;
19043
19044 q = createPQExpBuffer();
19046
19047 foreign = tbinfo &&
19048 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
19049
19050 if (coninfo->contype == 'p' ||
19051 coninfo->contype == 'u' ||
19052 coninfo->contype == 'x')
19053 {
19054 /* Index-related constraint */
19056 int k;
19057
19059
19060 if (indxinfo == NULL)
19061 pg_fatal("missing index for constraint \"%s\"",
19062 coninfo->dobj.name);
19063
19064 if (dopt->binary_upgrade)
19066 indxinfo->dobj.catId.oid);
19067
19068 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
19070 appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
19071 fmtId(coninfo->dobj.name));
19072
19073 if (coninfo->condef)
19074 {
19075 /* pg_get_constraintdef should have provided everything */
19076 appendPQExpBuffer(q, "%s;\n", coninfo->condef);
19077 }
19078 else
19079 {
19081 coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
19082
19083 /*
19084 * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
19085 * indexes. Being able to create this was fixed, but we need to
19086 * make the index distinct in order to be able to restore the
19087 * dump.
19088 */
19089 if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
19090 appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
19091 appendPQExpBufferStr(q, " (");
19092 for (k = 0; k < indxinfo->indnkeyattrs; k++)
19093 {
19094 int indkey = (int) indxinfo->indkeys[k];
19095 const char *attname;
19096
19098 break;
19100
19101 appendPQExpBuffer(q, "%s%s",
19102 (k == 0) ? "" : ", ",
19103 fmtId(attname));
19104 }
19105 if (coninfo->conperiod)
19106 appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
19107
19108 if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
19109 appendPQExpBufferStr(q, ") INCLUDE (");
19110
19111 for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
19112 {
19113 int indkey = (int) indxinfo->indkeys[k];
19114 const char *attname;
19115
19117 break;
19119
19120 appendPQExpBuffer(q, "%s%s",
19121 (k == indxinfo->indnkeyattrs) ? "" : ", ",
19122 fmtId(attname));
19123 }
19124
19125 appendPQExpBufferChar(q, ')');
19126
19127 if (nonemptyReloptions(indxinfo->indreloptions))
19128 {
19129 appendPQExpBufferStr(q, " WITH (");
19130 appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
19131 appendPQExpBufferChar(q, ')');
19132 }
19133
19134 if (coninfo->condeferrable)
19135 {
19136 appendPQExpBufferStr(q, " DEFERRABLE");
19137 if (coninfo->condeferred)
19138 appendPQExpBufferStr(q, " INITIALLY DEFERRED");
19139 }
19140
19141 appendPQExpBufferStr(q, ";\n");
19142 }
19143
19144 /*
19145 * Append ALTER TABLE commands as needed to set properties that we
19146 * only have ALTER TABLE syntax for. Keep this in sync with the
19147 * similar code in dumpIndex!
19148 */
19149
19150 /* If the index is clustered, we need to record that. */
19151 if (indxinfo->indisclustered)
19152 {
19153 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
19155 /* index name is not qualified in this syntax */
19156 appendPQExpBuffer(q, " ON %s;\n",
19157 fmtId(indxinfo->dobj.name));
19158 }
19159
19160 /* If the index defines identity, we need to record that. */
19161 if (indxinfo->indisreplident)
19162 {
19163 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
19165 /* index name is not qualified in this syntax */
19166 appendPQExpBuffer(q, " INDEX %s;\n",
19167 fmtId(indxinfo->dobj.name));
19168 }
19169
19170 /* Indexes can depend on extensions */
19172 "pg_catalog.pg_class", "INDEX",
19174
19175 appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
19177 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19178 fmtId(coninfo->dobj.name));
19179
19180 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19181
19182 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19183 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19184 ARCHIVE_OPTS(.tag = tag,
19185 .namespace = tbinfo->dobj.namespace->dobj.name,
19186 .tablespace = indxinfo->tablespace,
19187 .owner = tbinfo->rolname,
19188 .description = "CONSTRAINT",
19189 .section = SECTION_POST_DATA,
19190 .createStmt = q->data,
19191 .dropStmt = delq->data));
19192 }
19193 else if (coninfo->contype == 'f')
19194 {
19195 char *only;
19196
19197 /*
19198 * Foreign keys on partitioned tables are always declared as
19199 * inheriting to partitions; for all other cases, emit them as
19200 * applying ONLY directly to the named table, because that's how they
19201 * work for regular inherited tables.
19202 */
19203 only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
19204
19205 /*
19206 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
19207 * current table data is not processed
19208 */
19209 appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
19211 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19212 fmtId(coninfo->dobj.name),
19213 coninfo->condef);
19214
19215 appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
19217 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19218 fmtId(coninfo->dobj.name));
19219
19220 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19221
19222 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19223 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19224 ARCHIVE_OPTS(.tag = tag,
19225 .namespace = tbinfo->dobj.namespace->dobj.name,
19226 .owner = tbinfo->rolname,
19227 .description = "FK CONSTRAINT",
19228 .section = SECTION_POST_DATA,
19229 .createStmt = q->data,
19230 .dropStmt = delq->data));
19231 }
19232 else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
19233 {
19234 /* CHECK or invalid not-null constraint on a table */
19235
19236 /* Ignore if not to be dumped separately, or if it was inherited */
19237 if (coninfo->separate && coninfo->conislocal)
19238 {
19239 const char *keyword;
19240
19241 if (coninfo->contype == 'c')
19242 keyword = "CHECK CONSTRAINT";
19243 else
19244 keyword = "CONSTRAINT";
19245
19246 /* not ONLY since we want it to propagate to children */
19247 appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
19249 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19250 fmtId(coninfo->dobj.name),
19251 coninfo->condef);
19252
19253 appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
19255 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19256 fmtId(coninfo->dobj.name));
19257
19258 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19259
19260 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19261 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19262 ARCHIVE_OPTS(.tag = tag,
19263 .namespace = tbinfo->dobj.namespace->dobj.name,
19264 .owner = tbinfo->rolname,
19265 .description = keyword,
19266 .section = SECTION_POST_DATA,
19267 .createStmt = q->data,
19268 .dropStmt = delq->data));
19269 }
19270 }
19271 else if (tbinfo == NULL)
19272 {
19273 /* CHECK, NOT NULL constraint on a domain */
19274 TypeInfo *tyinfo = coninfo->condomain;
19275
19276 Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
19277
19278 /* Ignore if not to be dumped separately */
19279 if (coninfo->separate)
19280 {
19281 const char *keyword;
19282
19283 if (coninfo->contype == 'c')
19284 keyword = "CHECK CONSTRAINT";
19285 else
19286 keyword = "CONSTRAINT";
19287
19288 appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
19290 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19291 fmtId(coninfo->dobj.name),
19292 coninfo->condef);
19293
19294 appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
19296 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19297 fmtId(coninfo->dobj.name));
19298
19299 tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
19300
19301 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19302 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19303 ARCHIVE_OPTS(.tag = tag,
19304 .namespace = tyinfo->dobj.namespace->dobj.name,
19305 .owner = tyinfo->rolname,
19306 .description = keyword,
19307 .section = SECTION_POST_DATA,
19308 .createStmt = q->data,
19309 .dropStmt = delq->data));
19310
19311 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19312 {
19314 char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
19315
19316 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
19317 fmtId(coninfo->dobj.name));
19318
19320 tyinfo->dobj.namespace->dobj.name,
19321 tyinfo->rolname,
19322 coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
19324 free(qtypname);
19325 }
19326 }
19327 }
19328 else
19329 {
19330 pg_fatal("unrecognized constraint type: %c",
19331 coninfo->contype);
19332 }
19333
19334 /* Dump Constraint Comments --- only works for table constraints */
19335 if (tbinfo && coninfo->separate &&
19336 coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19338
19339 free(tag);
19342}
19343
19344/*
19345 * dumpTableConstraintComment --- dump a constraint's comment if any
19346 *
19347 * This is split out because we need the function in two different places
19348 * depending on whether the constraint is dumped as part of CREATE TABLE
19349 * or as a separate ALTER command.
19350 */
19351static void
19353{
19354 TableInfo *tbinfo = coninfo->contable;
19356 char *qtabname;
19357
19358 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19359
19360 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
19361 fmtId(coninfo->dobj.name));
19362
19363 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19365 tbinfo->dobj.namespace->dobj.name,
19366 tbinfo->rolname,
19367 coninfo->dobj.catId, 0,
19368 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
19369
19371 free(qtabname);
19372}
19373
19374static inline SeqType
19375parse_sequence_type(const char *name)
19376{
19377 for (int i = 0; i < lengthof(SeqTypeNames); i++)
19378 {
19379 if (strcmp(SeqTypeNames[i], name) == 0)
19380 return (SeqType) i;
19381 }
19382
19383 pg_fatal("unrecognized sequence type: %s", name);
19384 return (SeqType) 0; /* keep compiler quiet */
19385}
19386
19387/*
19388 * bsearch() comparator for SequenceItem
19389 */
19390static int
19391SequenceItemCmp(const void *p1, const void *p2)
19392{
19393 SequenceItem v1 = *((const SequenceItem *) p1);
19394 SequenceItem v2 = *((const SequenceItem *) p2);
19395
19396 return pg_cmp_u32(v1.oid, v2.oid);
19397}
19398
19399/*
19400 * collectSequences
19401 *
19402 * Construct a table of sequence information. This table is sorted by OID for
19403 * speed in lookup.
19404 */
19405static void
19407{
19408 PGresult *res;
19409 const char *query;
19410
19411 /*
19412 * Before Postgres 10, sequence metadata is in the sequence itself. With
19413 * some extra effort, we might be able to use the sorted table for those
19414 * versions, but for now it seems unlikely to be worth it.
19415 *
19416 * Since version 18, we can gather the sequence data in this query with
19417 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
19418 */
19419 if (fout->remoteVersion < 100000)
19420 return;
19421 else if (fout->remoteVersion < 180000 ||
19423 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19424 "seqstart, seqincrement, "
19425 "seqmax, seqmin, "
19426 "seqcache, seqcycle, "
19427 "NULL, 'f' "
19428 "FROM pg_catalog.pg_sequence "
19429 "ORDER BY seqrelid";
19430 else
19431 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19432 "seqstart, seqincrement, "
19433 "seqmax, seqmin, "
19434 "seqcache, seqcycle, "
19435 "last_value, is_called "
19436 "FROM pg_catalog.pg_sequence, "
19437 "pg_get_sequence_data(seqrelid) "
19438 "ORDER BY seqrelid;";
19439
19440 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
19441
19442 nsequences = PQntuples(res);
19444
19445 for (int i = 0; i < nsequences; i++)
19446 {
19447 sequences[i].oid = atooid(PQgetvalue(res, i, 0));
19449 sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
19450 sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
19451 sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
19452 sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
19453 sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
19454 sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
19455 sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
19456 sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
19457 sequences[i].null_seqtuple = (PQgetisnull(res, i, 8) || PQgetisnull(res, i, 9));
19458 }
19459
19460 PQclear(res);
19461}
19462
19463/*
19464 * dumpSequence
19465 * write the declaration (not data) of one user-defined sequence
19466 */
19467static void
19469{
19470 DumpOptions *dopt = fout->dopt;
19472 bool is_ascending;
19477 char *qseqname;
19478 TableInfo *owning_tab = NULL;
19479
19480 qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
19481
19482 /*
19483 * For versions >= 10, the sequence information is gathered in a sorted
19484 * table before any calls to dumpSequence(). See collectSequences() for
19485 * more information.
19486 */
19487 if (fout->remoteVersion >= 100000)
19488 {
19489 SequenceItem key = {0};
19490
19492
19493 key.oid = tbinfo->dobj.catId.oid;
19495 sizeof(SequenceItem), SequenceItemCmp);
19496 }
19497 else
19498 {
19499 PGresult *res;
19500
19501 /*
19502 * Before PostgreSQL 10, sequence metadata is in the sequence itself.
19503 *
19504 * Note: it might seem that 'bigint' potentially needs to be
19505 * schema-qualified, but actually that's a keyword.
19506 */
19507 appendPQExpBuffer(query,
19508 "SELECT 'bigint' AS sequence_type, "
19509 "start_value, increment_by, max_value, min_value, "
19510 "cache_value, is_cycled FROM %s",
19512
19513 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19514
19515 if (PQntuples(res) != 1)
19516 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19517 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19518 PQntuples(res)),
19519 tbinfo->dobj.name, PQntuples(res));
19520
19522 seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
19523 seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
19524 seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
19525 seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
19526 seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
19527 seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
19528 seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
19529
19530 PQclear(res);
19531 }
19532
19533 /* Calculate default limits for a sequence of this type */
19534 is_ascending = (seq->incby >= 0);
19535 if (seq->seqtype == SEQTYPE_SMALLINT)
19536 {
19539 }
19540 else if (seq->seqtype == SEQTYPE_INTEGER)
19541 {
19544 }
19545 else if (seq->seqtype == SEQTYPE_BIGINT)
19546 {
19549 }
19550 else
19551 {
19552 pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19553 default_minv = default_maxv = 0; /* keep compiler quiet */
19554 }
19555
19556 /*
19557 * Identity sequences are not to be dropped separately.
19558 */
19559 if (!tbinfo->is_identity_sequence)
19560 {
19561 appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19563 }
19564
19565 resetPQExpBuffer(query);
19566
19567 if (dopt->binary_upgrade)
19568 {
19570 tbinfo->dobj.catId.oid);
19571
19572 /*
19573 * In older PG versions a sequence will have a pg_type entry, but v14
19574 * and up don't use that, so don't attempt to preserve the type OID.
19575 */
19576 }
19577
19578 if (tbinfo->is_identity_sequence)
19579 {
19580 owning_tab = findTableByOid(tbinfo->owning_tab);
19581
19582 appendPQExpBuffer(query,
19583 "ALTER TABLE %s ",
19584 fmtQualifiedDumpable(owning_tab));
19585 appendPQExpBuffer(query,
19586 "ALTER COLUMN %s ADD GENERATED ",
19587 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19588 if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19589 appendPQExpBufferStr(query, "ALWAYS");
19590 else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19591 appendPQExpBufferStr(query, "BY DEFAULT");
19592 appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19594
19595 /*
19596 * Emit persistence option only if it's different from the owning
19597 * table's. This avoids using this new syntax unnecessarily.
19598 */
19599 if (tbinfo->relpersistence != owning_tab->relpersistence)
19600 appendPQExpBuffer(query, " %s\n",
19601 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19602 "UNLOGGED" : "LOGGED");
19603 }
19604 else
19605 {
19606 appendPQExpBuffer(query,
19607 "CREATE %sSEQUENCE %s\n",
19608 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19609 "UNLOGGED " : "",
19611
19612 if (seq->seqtype != SEQTYPE_BIGINT)
19613 appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19614 }
19615
19616 appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19617
19618 appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19619
19620 if (seq->minv != default_minv)
19621 appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19622 else
19623 appendPQExpBufferStr(query, " NO MINVALUE\n");
19624
19625 if (seq->maxv != default_maxv)
19626 appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19627 else
19628 appendPQExpBufferStr(query, " NO MAXVALUE\n");
19629
19630 appendPQExpBuffer(query,
19631 " CACHE " INT64_FORMAT "%s",
19632 seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19633
19634 if (tbinfo->is_identity_sequence)
19635 appendPQExpBufferStr(query, "\n);\n");
19636 else
19637 appendPQExpBufferStr(query, ";\n");
19638
19639 /* binary_upgrade: no need to clear TOAST table oid */
19640
19641 if (dopt->binary_upgrade)
19643 "SEQUENCE", qseqname,
19644 tbinfo->dobj.namespace->dobj.name);
19645
19646 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19647 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19648 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19649 .namespace = tbinfo->dobj.namespace->dobj.name,
19650 .owner = tbinfo->rolname,
19651 .description = "SEQUENCE",
19652 .section = SECTION_PRE_DATA,
19653 .createStmt = query->data,
19654 .dropStmt = delqry->data));
19655
19656 /*
19657 * If the sequence is owned by a table column, emit the ALTER for it as a
19658 * separate TOC entry immediately following the sequence's own entry. It's
19659 * OK to do this rather than using full sorting logic, because the
19660 * dependency that tells us it's owned will have forced the table to be
19661 * created first. We can't just include the ALTER in the TOC entry
19662 * because it will fail if we haven't reassigned the sequence owner to
19663 * match the table's owner.
19664 *
19665 * We need not schema-qualify the table reference because both sequence
19666 * and table must be in the same schema.
19667 */
19668 if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19669 {
19670 owning_tab = findTableByOid(tbinfo->owning_tab);
19671
19672 if (owning_tab == NULL)
19673 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19674 tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19675
19676 if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19677 {
19678 resetPQExpBuffer(query);
19679 appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19681 appendPQExpBuffer(query, " OWNED BY %s",
19682 fmtQualifiedDumpable(owning_tab));
19683 appendPQExpBuffer(query, ".%s;\n",
19684 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19685
19686 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19688 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19689 .namespace = tbinfo->dobj.namespace->dobj.name,
19690 .owner = tbinfo->rolname,
19691 .description = "SEQUENCE OWNED BY",
19692 .section = SECTION_PRE_DATA,
19693 .createStmt = query->data,
19694 .deps = &(tbinfo->dobj.dumpId),
19695 .nDeps = 1));
19696 }
19697 }
19698
19699 /* Dump Sequence Comments and Security Labels */
19700 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19701 dumpComment(fout, "SEQUENCE", qseqname,
19702 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19703 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19704
19705 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19706 dumpSecLabel(fout, "SEQUENCE", qseqname,
19707 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19708 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19709
19710 if (fout->remoteVersion < 100000)
19711 pg_free(seq);
19712 destroyPQExpBuffer(query);
19714 free(qseqname);
19715}
19716
19717/*
19718 * dumpSequenceData
19719 * write the data of one user-defined sequence
19720 */
19721static void
19723{
19724 TableInfo *tbinfo = tdinfo->tdtable;
19725 int64 last;
19726 bool called;
19727 PQExpBuffer query;
19728
19729 /* needn't bother if not dumping sequence data */
19730 if (!fout->dopt->dumpData && !fout->dopt->sequence_data)
19731 return;
19732
19733 query = createPQExpBuffer();
19734
19735 /*
19736 * For versions >= 18, the sequence information is gathered in the sorted
19737 * array before any calls to dumpSequenceData(). See collectSequences()
19738 * for more information.
19739 *
19740 * For older versions, we have to query the sequence relations
19741 * individually.
19742 */
19743 if (fout->remoteVersion < 180000)
19744 {
19745 PGresult *res;
19746
19747 appendPQExpBuffer(query,
19748 "SELECT last_value, is_called FROM %s",
19750
19751 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19752
19753 if (PQntuples(res) != 1)
19754 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19755 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19756 PQntuples(res)),
19757 tbinfo->dobj.name, PQntuples(res));
19758
19759 last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19760 called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19761
19762 PQclear(res);
19763 }
19764 else
19765 {
19766 SequenceItem key = {0};
19767 SequenceItem *entry;
19768
19770 Assert(tbinfo->dobj.catId.oid);
19771
19772 key.oid = tbinfo->dobj.catId.oid;
19773 entry = bsearch(&key, sequences, nsequences,
19774 sizeof(SequenceItem), SequenceItemCmp);
19775
19776 if (entry->null_seqtuple)
19777 pg_fatal("failed to get data for sequence \"%s\"; user may lack "
19778 "SELECT privilege on the sequence or the sequence may "
19779 "have been concurrently dropped",
19780 tbinfo->dobj.name);
19781
19782 last = entry->last_value;
19783 called = entry->is_called;
19784 }
19785
19786 resetPQExpBuffer(query);
19787 appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19789 appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19790 last, (called ? "true" : "false"));
19791
19792 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19794 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19795 .namespace = tbinfo->dobj.namespace->dobj.name,
19796 .owner = tbinfo->rolname,
19797 .description = "SEQUENCE SET",
19798 .section = SECTION_DATA,
19799 .createStmt = query->data,
19800 .deps = &(tbinfo->dobj.dumpId),
19801 .nDeps = 1));
19802
19803 destroyPQExpBuffer(query);
19804}
19805
19806/*
19807 * dumpTrigger
19808 * write the declaration of one user-defined table trigger
19809 */
19810static void
19812{
19813 DumpOptions *dopt = fout->dopt;
19814 TableInfo *tbinfo = tginfo->tgtable;
19815 PQExpBuffer query;
19819 char *qtabname;
19820 char *tag;
19821
19822 /* Do nothing if not dumping schema */
19823 if (!dopt->dumpSchema)
19824 return;
19825
19826 query = createPQExpBuffer();
19830
19831 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19832
19833 appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19835
19836 appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19837 appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19838
19839 /* Triggers can depend on extensions */
19841 "pg_catalog.pg_trigger", "TRIGGER",
19842 trigidentity->data);
19843
19844 if (tginfo->tgispartition)
19845 {
19846 Assert(tbinfo->ispartition);
19847
19848 /*
19849 * Partition triggers only appear here because their 'tgenabled' flag
19850 * differs from its parent's. The trigger is created already, so
19851 * remove the CREATE and replace it with an ALTER. (Clear out the
19852 * DROP query too, so that pg_dump --create does not cause errors.)
19853 */
19854 resetPQExpBuffer(query);
19856 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19857 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19859 switch (tginfo->tgenabled)
19860 {
19861 case 'f':
19862 case 'D':
19863 appendPQExpBufferStr(query, "DISABLE");
19864 break;
19865 case 't':
19866 case 'O':
19867 appendPQExpBufferStr(query, "ENABLE");
19868 break;
19869 case 'R':
19870 appendPQExpBufferStr(query, "ENABLE REPLICA");
19871 break;
19872 case 'A':
19873 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19874 break;
19875 }
19876 appendPQExpBuffer(query, " TRIGGER %s;\n",
19877 fmtId(tginfo->dobj.name));
19878 }
19879 else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19880 {
19881 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19882 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19884 switch (tginfo->tgenabled)
19885 {
19886 case 'D':
19887 case 'f':
19888 appendPQExpBufferStr(query, "DISABLE");
19889 break;
19890 case 'A':
19891 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19892 break;
19893 case 'R':
19894 appendPQExpBufferStr(query, "ENABLE REPLICA");
19895 break;
19896 default:
19897 appendPQExpBufferStr(query, "ENABLE");
19898 break;
19899 }
19900 appendPQExpBuffer(query, " TRIGGER %s;\n",
19901 fmtId(tginfo->dobj.name));
19902 }
19903
19904 appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19905 fmtId(tginfo->dobj.name));
19906
19907 tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19908
19909 if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19910 ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19911 ARCHIVE_OPTS(.tag = tag,
19912 .namespace = tbinfo->dobj.namespace->dobj.name,
19913 .owner = tbinfo->rolname,
19914 .description = "TRIGGER",
19915 .section = SECTION_POST_DATA,
19916 .createStmt = query->data,
19917 .dropStmt = delqry->data));
19918
19919 if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19921 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19922 tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19923
19924 free(tag);
19925 destroyPQExpBuffer(query);
19929 free(qtabname);
19930}
19931
19932/*
19933 * dumpEventTrigger
19934 * write the declaration of one user-defined event trigger
19935 */
19936static void
19938{
19939 DumpOptions *dopt = fout->dopt;
19940 PQExpBuffer query;
19942 char *qevtname;
19943
19944 /* Do nothing if not dumping schema */
19945 if (!dopt->dumpSchema)
19946 return;
19947
19948 query = createPQExpBuffer();
19950
19951 qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19952
19953 appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19955 appendPQExpBufferStr(query, " ON ");
19956 appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19957
19958 if (strcmp("", evtinfo->evttags) != 0)
19959 {
19960 appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19961 appendPQExpBufferStr(query, evtinfo->evttags);
19962 appendPQExpBufferChar(query, ')');
19963 }
19964
19965 appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19966 appendPQExpBufferStr(query, evtinfo->evtfname);
19967 appendPQExpBufferStr(query, "();\n");
19968
19969 if (evtinfo->evtenabled != 'O')
19970 {
19971 appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19972 qevtname);
19973 switch (evtinfo->evtenabled)
19974 {
19975 case 'D':
19976 appendPQExpBufferStr(query, "DISABLE");
19977 break;
19978 case 'A':
19979 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19980 break;
19981 case 'R':
19982 appendPQExpBufferStr(query, "ENABLE REPLICA");
19983 break;
19984 default:
19985 appendPQExpBufferStr(query, "ENABLE");
19986 break;
19987 }
19988 appendPQExpBufferStr(query, ";\n");
19989 }
19990
19991 appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19992 qevtname);
19993
19994 if (dopt->binary_upgrade)
19996 "EVENT TRIGGER", qevtname, NULL);
19997
19998 if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19999 ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
20000 ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
20001 .owner = evtinfo->evtowner,
20002 .description = "EVENT TRIGGER",
20003 .section = SECTION_POST_DATA,
20004 .createStmt = query->data,
20005 .dropStmt = delqry->data));
20006
20007 if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
20008 dumpComment(fout, "EVENT TRIGGER", qevtname,
20009 NULL, evtinfo->evtowner,
20010 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
20011
20012 if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
20013 dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
20014 NULL, evtinfo->evtowner,
20015 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
20016
20017 destroyPQExpBuffer(query);
20019 free(qevtname);
20020}
20021
20022/*
20023 * dumpRule
20024 * Dump a rule
20025 */
20026static void
20027dumpRule(Archive *fout, const RuleInfo *rinfo)
20028{
20029 DumpOptions *dopt = fout->dopt;
20030 TableInfo *tbinfo = rinfo->ruletable;
20031 bool is_view;
20032 PQExpBuffer query;
20033 PQExpBuffer cmd;
20036 char *qtabname;
20037 PGresult *res;
20038 char *tag;
20039
20040 /* Do nothing if not dumping schema */
20041 if (!dopt->dumpSchema)
20042 return;
20043
20044 /*
20045 * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
20046 * we do not want to dump it as a separate object.
20047 */
20048 if (!rinfo->separate)
20049 return;
20050
20051 /*
20052 * If it's an ON SELECT rule, we want to print it as a view definition,
20053 * instead of a rule.
20054 */
20055 is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
20056
20057 query = createPQExpBuffer();
20058 cmd = createPQExpBuffer();
20061
20062 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
20063
20064 if (is_view)
20065 {
20067
20068 /*
20069 * We need OR REPLACE here because we'll be replacing a dummy view.
20070 * Otherwise this should look largely like the regular view dump code.
20071 */
20072 appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
20074 if (nonemptyReloptions(tbinfo->reloptions))
20075 {
20076 appendPQExpBufferStr(cmd, " WITH (");
20077 appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
20078 appendPQExpBufferChar(cmd, ')');
20079 }
20081 appendPQExpBuffer(cmd, " AS\n%s", result->data);
20083 if (tbinfo->checkoption != NULL)
20084 appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
20085 tbinfo->checkoption);
20086 appendPQExpBufferStr(cmd, ";\n");
20087 }
20088 else
20089 {
20090 /* In the rule case, just print pg_get_ruledef's result verbatim */
20091 appendPQExpBuffer(query,
20092 "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
20093 rinfo->dobj.catId.oid);
20094
20095 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20096
20097 if (PQntuples(res) != 1)
20098 pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
20099 rinfo->dobj.name, tbinfo->dobj.name);
20100
20101 printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
20102
20103 PQclear(res);
20104 }
20105
20106 /*
20107 * Add the command to alter the rules replication firing semantics if it
20108 * differs from the default.
20109 */
20110 if (rinfo->ev_enabled != 'O')
20111 {
20112 appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
20113 switch (rinfo->ev_enabled)
20114 {
20115 case 'A':
20116 appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
20117 fmtId(rinfo->dobj.name));
20118 break;
20119 case 'R':
20120 appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
20121 fmtId(rinfo->dobj.name));
20122 break;
20123 case 'D':
20124 appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
20125 fmtId(rinfo->dobj.name));
20126 break;
20127 }
20128 }
20129
20130 if (is_view)
20131 {
20132 /*
20133 * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
20134 * REPLACE VIEW to replace the rule with something with minimal
20135 * dependencies.
20136 */
20138
20139 appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
20142 appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
20144 }
20145 else
20146 {
20147 appendPQExpBuffer(delcmd, "DROP RULE %s ",
20148 fmtId(rinfo->dobj.name));
20149 appendPQExpBuffer(delcmd, "ON %s;\n",
20151 }
20152
20153 appendPQExpBuffer(ruleprefix, "RULE %s ON",
20154 fmtId(rinfo->dobj.name));
20155
20156 tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
20157
20158 if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
20159 ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
20160 ARCHIVE_OPTS(.tag = tag,
20161 .namespace = tbinfo->dobj.namespace->dobj.name,
20162 .owner = tbinfo->rolname,
20163 .description = "RULE",
20164 .section = SECTION_POST_DATA,
20165 .createStmt = cmd->data,
20166 .dropStmt = delcmd->data));
20167
20168 /* Dump rule comments */
20169 if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
20171 tbinfo->dobj.namespace->dobj.name,
20172 tbinfo->rolname,
20173 rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
20174
20175 free(tag);
20176 destroyPQExpBuffer(query);
20177 destroyPQExpBuffer(cmd);
20180 free(qtabname);
20181}
20182
20183/*
20184 * getExtensionMembership --- obtain extension membership data
20185 *
20186 * We need to identify objects that are extension members as soon as they're
20187 * loaded, so that we can correctly determine whether they need to be dumped.
20188 * Generally speaking, extension member objects will get marked as *not* to
20189 * be dumped, as they will be recreated by the single CREATE EXTENSION
20190 * command. However, in binary upgrade mode we still need to dump the members
20191 * individually.
20192 */
20193void
20195 int numExtensions)
20196{
20197 PQExpBuffer query;
20198 PGresult *res;
20199 int ntups,
20200 i;
20201 int i_classid,
20202 i_objid,
20203 i_refobjid;
20204 ExtensionInfo *ext;
20205
20206 /* Nothing to do if no extensions */
20207 if (numExtensions == 0)
20208 return;
20209
20210 query = createPQExpBuffer();
20211
20212 /* refclassid constraint is redundant but may speed the search */
20213 appendPQExpBufferStr(query, "SELECT "
20214 "classid, objid, refobjid "
20215 "FROM pg_depend "
20216 "WHERE refclassid = 'pg_extension'::regclass "
20217 "AND deptype = 'e' "
20218 "ORDER BY 3");
20219
20220 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20221
20222 ntups = PQntuples(res);
20223
20224 i_classid = PQfnumber(res, "classid");
20225 i_objid = PQfnumber(res, "objid");
20226 i_refobjid = PQfnumber(res, "refobjid");
20227
20228 /*
20229 * Since we ordered the SELECT by referenced ID, we can expect that
20230 * multiple entries for the same extension will appear together; this
20231 * saves on searches.
20232 */
20233 ext = NULL;
20234
20235 for (i = 0; i < ntups; i++)
20236 {
20237 CatalogId objId;
20238 Oid extId;
20239
20240 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20241 objId.oid = atooid(PQgetvalue(res, i, i_objid));
20243
20244 if (ext == NULL ||
20245 ext->dobj.catId.oid != extId)
20247
20248 if (ext == NULL)
20249 {
20250 /* shouldn't happen */
20251 pg_log_warning("could not find referenced extension %u", extId);
20252 continue;
20253 }
20254
20255 recordExtensionMembership(objId, ext);
20256 }
20257
20258 PQclear(res);
20259
20260 destroyPQExpBuffer(query);
20261}
20262
20263/*
20264 * processExtensionTables --- deal with extension configuration tables
20265 *
20266 * There are two parts to this process:
20267 *
20268 * 1. Identify and create dump records for extension configuration tables.
20269 *
20270 * Extensions can mark tables as "configuration", which means that the user
20271 * is able and expected to modify those tables after the extension has been
20272 * loaded. For these tables, we dump out only the data- the structure is
20273 * expected to be handled at CREATE EXTENSION time, including any indexes or
20274 * foreign keys, which brings us to-
20275 *
20276 * 2. Record FK dependencies between configuration tables.
20277 *
20278 * Due to the FKs being created at CREATE EXTENSION time and therefore before
20279 * the data is loaded, we have to work out what the best order for reloading
20280 * the data is, to avoid FK violations when the tables are restored. This is
20281 * not perfect- we can't handle circular dependencies and if any exist they
20282 * will cause an invalid dump to be produced (though at least all of the data
20283 * is included for a user to manually restore). This is currently documented
20284 * but perhaps we can provide a better solution in the future.
20285 */
20286void
20288 int numExtensions)
20289{
20290 DumpOptions *dopt = fout->dopt;
20291 PQExpBuffer query;
20292 PGresult *res;
20293 int ntups,
20294 i;
20295 int i_conrelid,
20297
20298 /* Nothing to do if no extensions */
20299 if (numExtensions == 0)
20300 return;
20301
20302 /*
20303 * Identify extension configuration tables and create TableDataInfo
20304 * objects for them, ensuring their data will be dumped even though the
20305 * tables themselves won't be.
20306 *
20307 * Note that we create TableDataInfo objects even in schema-only mode, ie,
20308 * user data in a configuration table is treated like schema data. This
20309 * seems appropriate since system data in a config table would get
20310 * reloaded by CREATE EXTENSION. If the extension is not listed in the
20311 * list of extensions to be included, none of its data is dumped.
20312 */
20313 for (i = 0; i < numExtensions; i++)
20314 {
20316 char *extconfig = curext->extconfig;
20317 char *extcondition = curext->extcondition;
20318 char **extconfigarray = NULL;
20319 char **extconditionarray = NULL;
20320 int nconfigitems = 0;
20321 int nconditionitems = 0;
20322
20323 /*
20324 * Check if this extension is listed as to include in the dump. If
20325 * not, any table data associated with it is discarded.
20326 */
20329 curext->dobj.catId.oid))
20330 continue;
20331
20332 /*
20333 * Check if this extension is listed as to exclude in the dump. If
20334 * yes, any table data associated with it is discarded.
20335 */
20338 curext->dobj.catId.oid))
20339 continue;
20340
20341 if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
20342 {
20343 int j;
20344
20345 if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
20346 pg_fatal("could not parse %s array", "extconfig");
20347 if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
20348 pg_fatal("could not parse %s array", "extcondition");
20350 pg_fatal("mismatched number of configurations and conditions for extension");
20351
20352 for (j = 0; j < nconfigitems; j++)
20353 {
20356 bool dumpobj =
20357 curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
20358
20360 if (configtbl == NULL)
20361 continue;
20362
20363 /*
20364 * Tables of not-to-be-dumped extensions shouldn't be dumped
20365 * unless the table or its schema is explicitly included
20366 */
20367 if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
20368 {
20369 /* check table explicitly requested */
20370 if (table_include_oids.head != NULL &&
20372 configtbloid))
20373 dumpobj = true;
20374
20375 /* check table's schema explicitly requested */
20376 if (configtbl->dobj.namespace->dobj.dump &
20378 dumpobj = true;
20379 }
20380
20381 /* check table excluded by an exclusion switch */
20382 if (table_exclude_oids.head != NULL &&
20384 configtbloid))
20385 dumpobj = false;
20386
20387 /* check schema excluded by an exclusion switch */
20389 configtbl->dobj.namespace->dobj.catId.oid))
20390 dumpobj = false;
20391
20392 if (dumpobj)
20393 {
20395 if (configtbl->dataObj != NULL)
20396 {
20397 if (strlen(extconditionarray[j]) > 0)
20398 configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
20399 }
20400 }
20401 }
20402 }
20403 if (extconfigarray)
20407 }
20408
20409 /*
20410 * Now that all the TableDataInfo objects have been created for all the
20411 * extensions, check their FK dependencies and register them to try and
20412 * dump the data out in an order that they can be restored in.
20413 *
20414 * Note that this is not a problem for user tables as their FKs are
20415 * recreated after the data has been loaded.
20416 */
20417
20418 query = createPQExpBuffer();
20419
20420 printfPQExpBuffer(query,
20421 "SELECT conrelid, confrelid "
20422 "FROM pg_constraint "
20423 "JOIN pg_depend ON (objid = confrelid) "
20424 "WHERE contype = 'f' "
20425 "AND refclassid = 'pg_extension'::regclass "
20426 "AND classid = 'pg_class'::regclass;");
20427
20428 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20429 ntups = PQntuples(res);
20430
20431 i_conrelid = PQfnumber(res, "conrelid");
20432 i_confrelid = PQfnumber(res, "confrelid");
20433
20434 /* Now get the dependencies and register them */
20435 for (i = 0; i < ntups; i++)
20436 {
20437 Oid conrelid,
20438 confrelid;
20440 *contable;
20441
20442 conrelid = atooid(PQgetvalue(res, i, i_conrelid));
20443 confrelid = atooid(PQgetvalue(res, i, i_confrelid));
20444 contable = findTableByOid(conrelid);
20445 reftable = findTableByOid(confrelid);
20446
20447 if (reftable == NULL ||
20448 reftable->dataObj == NULL ||
20449 contable == NULL ||
20450 contable->dataObj == NULL)
20451 continue;
20452
20453 /*
20454 * Make referencing TABLE_DATA object depend on the referenced table's
20455 * TABLE_DATA object.
20456 */
20457 addObjectDependency(&contable->dataObj->dobj,
20458 reftable->dataObj->dobj.dumpId);
20459 }
20460 PQclear(res);
20461 destroyPQExpBuffer(query);
20462}
20463
20464/*
20465 * getDependencies --- obtain available dependency data
20466 */
20467static void
20469{
20470 PQExpBuffer query;
20471 PGresult *res;
20472 int ntups,
20473 i;
20474 int i_classid,
20475 i_objid,
20477 i_refobjid,
20478 i_deptype;
20479 DumpableObject *dobj,
20480 *refdobj;
20481
20482 pg_log_info("reading dependency data");
20483
20484 query = createPQExpBuffer();
20485
20486 /*
20487 * Messy query to collect the dependency data we need. Note that we
20488 * ignore the sub-object column, so that dependencies of or on a column
20489 * look the same as dependencies of or on a whole table.
20490 *
20491 * PIN dependencies aren't interesting, and EXTENSION dependencies were
20492 * already processed by getExtensionMembership.
20493 */
20494 appendPQExpBufferStr(query, "SELECT "
20495 "classid, objid, refclassid, refobjid, deptype "
20496 "FROM pg_depend "
20497 "WHERE deptype != 'p' AND deptype != 'e'\n");
20498
20499 /*
20500 * Since we don't treat pg_amop entries as separate DumpableObjects, we
20501 * have to translate their dependencies into dependencies of their parent
20502 * opfamily. Ignore internal dependencies though, as those will point to
20503 * their parent opclass, which we needn't consider here (and if we did,
20504 * it'd just result in circular dependencies). Also, "loose" opfamily
20505 * entries will have dependencies on their parent opfamily, which we
20506 * should drop since they'd likewise become useless self-dependencies.
20507 * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
20508 */
20509 appendPQExpBufferStr(query, "UNION ALL\n"
20510 "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
20511 "FROM pg_depend d, pg_amop o "
20512 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20513 "classid = 'pg_amop'::regclass AND objid = o.oid "
20514 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
20515
20516 /* Likewise for pg_amproc entries */
20517 appendPQExpBufferStr(query, "UNION ALL\n"
20518 "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
20519 "FROM pg_depend d, pg_amproc p "
20520 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20521 "classid = 'pg_amproc'::regclass AND objid = p.oid "
20522 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
20523
20524 /*
20525 * Translate dependencies of pg_propgraph_element entries into
20526 * dependencies of their parent pg_class entry.
20527 */
20528 if (fout->remoteVersion >= 190000)
20529 appendPQExpBufferStr(query, "UNION ALL\n"
20530 "SELECT 'pg_class'::regclass AS classid, pgepgid AS objid, refclassid, refobjid, deptype "
20531 "FROM pg_depend d, pg_propgraph_element pge "
20532 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20533 "classid = 'pg_propgraph_element'::regclass AND objid = pge.oid\n");
20534
20535 /* Sort the output for efficiency below */
20536 appendPQExpBufferStr(query, "ORDER BY 1,2");
20537
20538 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20539
20540 ntups = PQntuples(res);
20541
20542 i_classid = PQfnumber(res, "classid");
20543 i_objid = PQfnumber(res, "objid");
20544 i_refclassid = PQfnumber(res, "refclassid");
20545 i_refobjid = PQfnumber(res, "refobjid");
20546 i_deptype = PQfnumber(res, "deptype");
20547
20548 /*
20549 * Since we ordered the SELECT by referencing ID, we can expect that
20550 * multiple entries for the same object will appear together; this saves
20551 * on searches.
20552 */
20553 dobj = NULL;
20554
20555 for (i = 0; i < ntups; i++)
20556 {
20557 CatalogId objId;
20559 char deptype;
20560
20561 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20562 objId.oid = atooid(PQgetvalue(res, i, i_objid));
20563 refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20564 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20565 deptype = *(PQgetvalue(res, i, i_deptype));
20566
20567 if (dobj == NULL ||
20568 dobj->catId.tableoid != objId.tableoid ||
20569 dobj->catId.oid != objId.oid)
20570 dobj = findObjectByCatalogId(objId);
20571
20572 /*
20573 * Failure to find objects mentioned in pg_depend is not unexpected,
20574 * since for example we don't collect info about TOAST tables.
20575 */
20576 if (dobj == NULL)
20577 {
20578#ifdef NOT_USED
20579 pg_log_warning("no referencing object %u %u",
20580 objId.tableoid, objId.oid);
20581#endif
20582 continue;
20583 }
20584
20586
20587 if (refdobj == NULL)
20588 {
20589#ifdef NOT_USED
20590 pg_log_warning("no referenced object %u %u",
20591 refobjId.tableoid, refobjId.oid);
20592#endif
20593 continue;
20594 }
20595
20596 /*
20597 * For 'x' dependencies, mark the object for later; we still add the
20598 * normal dependency, for possible ordering purposes. Currently
20599 * pg_dump_sort.c knows to put extensions ahead of all object types
20600 * that could possibly depend on them, but this is safer.
20601 */
20602 if (deptype == 'x')
20603 dobj->depends_on_ext = true;
20604
20605 /*
20606 * Ordinarily, table rowtypes have implicit dependencies on their
20607 * tables. However, for a composite type the implicit dependency goes
20608 * the other way in pg_depend; which is the right thing for DROP but
20609 * it doesn't produce the dependency ordering we need. So in that one
20610 * case, we reverse the direction of the dependency.
20611 */
20612 if (deptype == 'i' &&
20613 dobj->objType == DO_TABLE &&
20614 refdobj->objType == DO_TYPE)
20616 else
20617 /* normal case */
20618 addObjectDependency(dobj, refdobj->dumpId);
20619 }
20620
20621 PQclear(res);
20622
20623 destroyPQExpBuffer(query);
20624}
20625
20626
20627/*
20628 * createBoundaryObjects - create dummy DumpableObjects to represent
20629 * dump section boundaries.
20630 */
20631static DumpableObject *
20633{
20635
20637
20638 dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20639 dobjs[0].catId = nilCatalogId;
20640 AssignDumpId(dobjs + 0);
20641 dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20642
20643 dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20644 dobjs[1].catId = nilCatalogId;
20645 AssignDumpId(dobjs + 1);
20646 dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20647
20648 return dobjs;
20649}
20650
20651/*
20652 * addBoundaryDependencies - add dependencies as needed to enforce the dump
20653 * section boundaries.
20654 */
20655static void
20658{
20661 int i;
20662
20663 for (i = 0; i < numObjs; i++)
20664 {
20665 DumpableObject *dobj = dobjs[i];
20666
20667 /*
20668 * The classification of object types here must match the SECTION_xxx
20669 * values assigned during subsequent ArchiveEntry calls!
20670 */
20671 switch (dobj->objType)
20672 {
20673 case DO_NAMESPACE:
20674 case DO_EXTENSION:
20675 case DO_TYPE:
20676 case DO_SHELL_TYPE:
20677 case DO_FUNC:
20678 case DO_AGG:
20679 case DO_OPERATOR:
20680 case DO_ACCESS_METHOD:
20681 case DO_OPCLASS:
20682 case DO_OPFAMILY:
20683 case DO_COLLATION:
20684 case DO_CONVERSION:
20685 case DO_TABLE:
20686 case DO_TABLE_ATTACH:
20687 case DO_ATTRDEF:
20688 case DO_PROCLANG:
20689 case DO_CAST:
20690 case DO_DUMMY_TYPE:
20691 case DO_TSPARSER:
20692 case DO_TSDICT:
20693 case DO_TSTEMPLATE:
20694 case DO_TSCONFIG:
20695 case DO_FDW:
20696 case DO_FOREIGN_SERVER:
20697 case DO_TRANSFORM:
20698 /* Pre-data objects: must come before the pre-data boundary */
20700 break;
20701 case DO_TABLE_DATA:
20702 case DO_SEQUENCE_SET:
20703 case DO_LARGE_OBJECT:
20705 /* Data objects: must come between the boundaries */
20706 addObjectDependency(dobj, preDataBound->dumpId);
20708 break;
20709 case DO_INDEX:
20710 case DO_INDEX_ATTACH:
20711 case DO_STATSEXT:
20712 case DO_REFRESH_MATVIEW:
20713 case DO_TRIGGER:
20714 case DO_EVENT_TRIGGER:
20715 case DO_DEFAULT_ACL:
20716 case DO_POLICY:
20717 case DO_PUBLICATION:
20718 case DO_PUBLICATION_REL:
20720 case DO_SUBSCRIPTION:
20722 /* Post-data objects: must come after the post-data boundary */
20723 addObjectDependency(dobj, postDataBound->dumpId);
20724 break;
20725 case DO_RULE:
20726 /* Rules are post-data, but only if dumped separately */
20727 if (((RuleInfo *) dobj)->separate)
20728 addObjectDependency(dobj, postDataBound->dumpId);
20729 break;
20730 case DO_CONSTRAINT:
20731 case DO_FK_CONSTRAINT:
20732 /* Constraints are post-data, but only if dumped separately */
20733 if (((ConstraintInfo *) dobj)->separate)
20734 addObjectDependency(dobj, postDataBound->dumpId);
20735 break;
20737 /* nothing to do */
20738 break;
20740 /* must come after the pre-data boundary */
20741 addObjectDependency(dobj, preDataBound->dumpId);
20742 break;
20743 case DO_REL_STATS:
20744 /* stats section varies by parent object type, DATA or POST */
20745 if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20746 {
20747 addObjectDependency(dobj, preDataBound->dumpId);
20748 addObjectDependency(postDataBound, dobj->dumpId);
20749 }
20750 else
20751 addObjectDependency(dobj, postDataBound->dumpId);
20752 break;
20753 }
20754 }
20755}
20756
20757
20758/*
20759 * BuildArchiveDependencies - create dependency data for archive TOC entries
20760 *
20761 * The raw dependency data obtained by getDependencies() is not terribly
20762 * useful in an archive dump, because in many cases there are dependency
20763 * chains linking through objects that don't appear explicitly in the dump.
20764 * For example, a view will depend on its _RETURN rule while the _RETURN rule
20765 * will depend on other objects --- but the rule will not appear as a separate
20766 * object in the dump. We need to adjust the view's dependencies to include
20767 * whatever the rule depends on that is included in the dump.
20768 *
20769 * Just to make things more complicated, there are also "special" dependencies
20770 * such as the dependency of a TABLE DATA item on its TABLE, which we must
20771 * not rearrange because pg_restore knows that TABLE DATA only depends on
20772 * its table. In these cases we must leave the dependencies strictly as-is
20773 * even if they refer to not-to-be-dumped objects.
20774 *
20775 * To handle this, the convention is that "special" dependencies are created
20776 * during ArchiveEntry calls, and an archive TOC item that has any such
20777 * entries will not be touched here. Otherwise, we recursively search the
20778 * DumpableObject data structures to build the correct dependencies for each
20779 * archive TOC item.
20780 */
20781static void
20783{
20785 TocEntry *te;
20786
20787 /* Scan all TOC entries in the archive */
20788 for (te = AH->toc->next; te != AH->toc; te = te->next)
20789 {
20790 DumpableObject *dobj;
20791 DumpId *dependencies;
20792 int nDeps;
20793 int allocDeps;
20794
20795 /* No need to process entries that will not be dumped */
20796 if (te->reqs == 0)
20797 continue;
20798 /* Ignore entries that already have "special" dependencies */
20799 if (te->nDeps > 0)
20800 continue;
20801 /* Otherwise, look up the item's original DumpableObject, if any */
20802 dobj = findObjectByDumpId(te->dumpId);
20803 if (dobj == NULL)
20804 continue;
20805 /* No work if it has no dependencies */
20806 if (dobj->nDeps <= 0)
20807 continue;
20808 /* Set up work array */
20809 allocDeps = 64;
20810 dependencies = pg_malloc_array(DumpId, allocDeps);
20811 nDeps = 0;
20812 /* Recursively find all dumpable dependencies */
20813 findDumpableDependencies(AH, dobj,
20814 &dependencies, &nDeps, &allocDeps);
20815 /* And save 'em ... */
20816 if (nDeps > 0)
20817 {
20818 dependencies = pg_realloc_array(dependencies, DumpId, nDeps);
20819 te->dependencies = dependencies;
20820 te->nDeps = nDeps;
20821 }
20822 else
20823 free(dependencies);
20824 }
20825}
20826
20827/* Recursive search subroutine for BuildArchiveDependencies */
20828static void
20830 DumpId **dependencies, int *nDeps, int *allocDeps)
20831{
20832 int i;
20833
20834 /*
20835 * Ignore section boundary objects: if we search through them, we'll
20836 * report lots of bogus dependencies.
20837 */
20838 if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20840 return;
20841
20842 for (i = 0; i < dobj->nDeps; i++)
20843 {
20844 DumpId depid = dobj->dependencies[i];
20845
20846 if (TocIDRequired(AH, depid) != 0)
20847 {
20848 /* Object will be dumped, so just reference it as a dependency */
20849 if (*nDeps >= *allocDeps)
20850 {
20851 *allocDeps *= 2;
20852 *dependencies = pg_realloc_array(*dependencies, DumpId, *allocDeps);
20853 }
20854 (*dependencies)[*nDeps] = depid;
20855 (*nDeps)++;
20856 }
20857 else
20858 {
20859 /*
20860 * Object will not be dumped, so recursively consider its deps. We
20861 * rely on the assumption that sortDumpableObjects already broke
20862 * any dependency loops, else we might recurse infinitely.
20863 */
20865
20866 if (otherdobj)
20868 dependencies, nDeps, allocDeps);
20869 }
20870 }
20871}
20872
20873
20874/*
20875 * getFormattedTypeName - retrieve a nicely-formatted type name for the
20876 * given type OID.
20877 *
20878 * This does not guarantee to schema-qualify the output, so it should not
20879 * be used to create the target object name for CREATE or ALTER commands.
20880 *
20881 * Note that the result is cached and must not be freed by the caller.
20882 */
20883static const char *
20885{
20887 char *result;
20888 PQExpBuffer query;
20889 PGresult *res;
20890
20891 if (oid == 0)
20892 {
20893 if ((opts & zeroAsStar) != 0)
20894 return "*";
20895 else if ((opts & zeroAsNone) != 0)
20896 return "NONE";
20897 }
20898
20899 /* see if we have the result cached in the type's TypeInfo record */
20900 typeInfo = findTypeByOid(oid);
20901 if (typeInfo && typeInfo->ftypname)
20902 return typeInfo->ftypname;
20903
20904 query = createPQExpBuffer();
20905 appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20906 oid);
20907
20908 res = ExecuteSqlQueryForSingleRow(fout, query->data);
20909
20910 /* result of format_type is already quoted */
20911 result = pg_strdup(PQgetvalue(res, 0, 0));
20912
20913 PQclear(res);
20914 destroyPQExpBuffer(query);
20915
20916 /*
20917 * Cache the result for re-use in later requests, if possible. If we
20918 * don't have a TypeInfo for the type, the string will be leaked once the
20919 * caller is done with it ... but that case really should not happen, so
20920 * leaking if it does seems acceptable.
20921 */
20922 if (typeInfo)
20923 typeInfo->ftypname = result;
20924
20925 return result;
20926}
20927
20928/*
20929 * Return a column list clause for the given relation.
20930 *
20931 * Special case: if there are no undropped columns in the relation, return
20932 * "", not an invalid "()" column list.
20933 */
20934static const char *
20936{
20937 int numatts = ti->numatts;
20938 char **attnames = ti->attnames;
20939 bool *attisdropped = ti->attisdropped;
20940 char *attgenerated = ti->attgenerated;
20941 bool needComma;
20942 int i;
20943
20944 appendPQExpBufferChar(buffer, '(');
20945 needComma = false;
20946 for (i = 0; i < numatts; i++)
20947 {
20948 if (attisdropped[i])
20949 continue;
20950 if (attgenerated[i])
20951 continue;
20952 if (needComma)
20953 appendPQExpBufferStr(buffer, ", ");
20954 appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20955 needComma = true;
20956 }
20957
20958 if (!needComma)
20959 return ""; /* no undropped columns */
20960
20961 appendPQExpBufferChar(buffer, ')');
20962 return buffer->data;
20963}
20964
20965/*
20966 * Check if a reloptions array is nonempty.
20967 */
20968static bool
20969nonemptyReloptions(const char *reloptions)
20970{
20971 /* Don't want to print it if it's just "{}" */
20972 return (reloptions != NULL && strlen(reloptions) > 2);
20973}
20974
20975/*
20976 * Format a reloptions array and append it to the given buffer.
20977 *
20978 * "prefix" is prepended to the option names; typically it's "" or "toast.".
20979 */
20980static void
20981appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20982 const char *prefix, Archive *fout)
20983{
20984 bool res;
20985
20986 res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20987 fout->std_strings);
20988 if (!res)
20989 pg_log_warning("could not parse %s array", "reloptions");
20990}
20991
20992/*
20993 * read_dump_filters - retrieve object identifier patterns from file
20994 *
20995 * Parse the specified filter file for include and exclude patterns, and add
20996 * them to the relevant lists. If the filename is "-" then filters will be
20997 * read from STDIN rather than a file.
20998 */
20999static void
21000read_dump_filters(const char *filename, DumpOptions *dopt)
21001{
21003 char *objname;
21005 FilterObjectType objtype;
21006
21008
21009 while (filter_read_item(&fstate, &objname, &comtype, &objtype))
21010 {
21012 {
21013 switch (objtype)
21014 {
21016 break;
21023 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
21024 "include",
21025 filter_object_type_name(objtype));
21026 exit_nicely(1);
21027 break; /* unreachable */
21028
21031 break;
21034 break;
21037 dopt->include_everything = false;
21038 break;
21041 dopt->include_everything = false;
21042 break;
21045 objname);
21046 dopt->include_everything = false;
21047 break;
21048 }
21049 }
21051 {
21052 switch (objtype)
21053 {
21055 break;
21061 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
21062 "exclude",
21063 filter_object_type_name(objtype));
21064 exit_nicely(1);
21065 break;
21066
21069 break;
21072 objname);
21073 break;
21076 objname);
21077 break;
21080 break;
21083 break;
21086 objname);
21087 break;
21088 }
21089 }
21090 else
21091 {
21093 Assert(objtype == FILTER_OBJECT_TYPE_NONE);
21094 }
21095
21096 if (objname)
21097 free(objname);
21098 }
21099
21101}
Acl * acldefault(ObjectType objtype, Oid ownerId)
Definition acl.c:827
#define InvalidAttrNumber
Definition attnum.h:23
int lo_read(int fd, char *buf, int len)
Definition be-fsstubs.c:154
static void help(void)
Definition pg_config.c:71
void recordAdditionalCatalogID(CatalogId catId, DumpableObject *dobj)
Definition common.c:722
void recordExtensionMembership(CatalogId catId, ExtensionInfo *ext)
Definition common.c:1066
FuncInfo * findFuncByOid(Oid oid)
Definition common.c:921
TableInfo * findTableByOid(Oid oid)
Definition common.c:866
ExtensionInfo * findExtensionByOid(Oid oid)
Definition common.c:1011
CollInfo * findCollationByOid(Oid oid)
Definition common.c:975
SubscriptionInfo * findSubscriptionByOid(Oid oid)
Definition common.c:1047
OprInfo * findOprByOid(Oid oid)
Definition common.c:939
NamespaceInfo * findNamespaceByOid(Oid oid)
Definition common.c:993
DumpId createDumpId(void)
Definition common.c:748
void addObjectDependency(DumpableObject *dobj, DumpId refId)
Definition common.c:821
DumpableObject * findObjectByDumpId(DumpId dumpId)
Definition common.c:768
void parseOidArray(const char *str, Oid *array, int arraysize)
Definition common.c:1114
ExtensionInfo * findOwningExtension(CatalogId catalogId)
Definition common.c:1090
TableInfo * getSchemaData(Archive *fout, int *numTablesPtr)
Definition common.c:98
TypeInfo * findTypeByOid(Oid oid)
Definition common.c:902
DumpableObject * findObjectByCatalogId(CatalogId catalogId)
Definition common.c:781
void AssignDumpId(DumpableObject *dobj)
Definition common.c:660
void getDumpableObjects(DumpableObject ***objs, int *numObjs)
Definition common.c:800
PublicationInfo * findPublicationByOid(Oid oid)
Definition common.c:1029
void on_exit_close_archive(Archive *AHX)
Definition parallel.c:330
void init_parallel_dump_utils(void)
Definition parallel.c:238
#define PG_MAX_JOBS
Definition parallel.h:48
bool is_superuser(void)
Definition common.c:2482
uint32 BlockNumber
Definition block.h:31
static void cleanup(void)
Definition bootstrap.c:886
static const gbtree_vinfo tinfo
Definition btree_bit.c:109
#define PG_INT32_MAX
Definition c.h:673
#define ngettext(s, p, n)
Definition c.h:1270
#define INT64_FORMAT
Definition c.h:634
#define Assert(condition)
Definition c.h:943
#define PG_TEXTDOMAIN(domain)
Definition c.h:1303
int64_t int64
Definition c.h:621
#define PG_INT16_MIN
Definition c.h:669
#define CppAsString2(x)
Definition c.h:506
int32_t int32
Definition c.h:620
#define PG_INT64_MAX
Definition c.h:676
#define PG_INT64_MIN
Definition c.h:675
uint32_t uint32
Definition c.h:624
#define lengthof(array)
Definition c.h:873
#define PG_INT32_MIN
Definition c.h:672
#define PG_INT16_MAX
Definition c.h:670
#define OidIsValid(objectId)
Definition c.h:858
uint32 result
int nspid
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition exec.c:430
char * supports_compression(const pg_compress_specification compression_spec)
Definition compress_io.c:87
char * validate_compress_specification(pg_compress_specification *spec)
bool parse_compress_algorithm(char *name, pg_compress_algorithm *algorithm)
Definition compression.c:79
void parse_compress_specification(pg_compress_algorithm algorithm, char *specification, pg_compress_specification *result)
#define PG_COMPRESSION_OPTION_WORKERS
Definition compression.h:29
void parse_compress_options(const char *option, char **algorithm, char **detail)
#define ALWAYS_SECURE_SEARCH_PATH_SQL
Definition connect.h:25
char * generate_restrict_key(void)
Definition dumputils.c:976
bool buildACLCommands(const char *name, const char *subname, const char *nspname, const char *type, const char *acls, const char *baseacls, const char *owner, const char *prefix, int remoteVersion, PQExpBuffer sql)
Definition dumputils.c:104
bool valid_restrict_key(const char *restrict_key)
Definition dumputils.c:1000
void buildShSecLabelQuery(const char *catalog_name, Oid objectId, PQExpBuffer sql)
Definition dumputils.c:681
void makeAlterConfigCommand(PGconn *conn, const char *configitem, const char *type, const char *name, const char *type2, const char *name2, PQExpBuffer buf)
Definition dumputils.c:868
bool buildDefaultACLCommands(const char *type, const char *nspname, const char *acls, const char *acldefault, const char *owner, int remoteVersion, PQExpBuffer sql)
Definition dumputils.c:366
char * sanitize_line(const char *str, bool want_hyphen)
Definition dumputils.c:52
bool variable_is_guc_list_quote(const char *name)
Definition dumputils.c:733
void quoteAclUserName(PQExpBuffer output, const char *input)
Definition dumputils.c:588
void emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, const char *objtype, const char *objname)
Definition dumputils.c:699
Datum arg
Definition elog.c:1323
#define _(x)
Definition elog.c:96
char * PQdb(const PGconn *conn)
const char * PQparameterStatus(const PGconn *conn, const char *paramName)
int PQclientEncoding(const PGconn *conn)
char * PQerrorMessage(const PGconn *conn)
int PQsetClientEncoding(PGconn *conn, const char *encoding)
void PQfreemem(void *ptr)
Definition fe-exec.c:4068
Oid PQftype(const PGresult *res, int field_num)
Definition fe-exec.c:3750
int PQfnumber(const PGresult *res, const char *field_name)
Definition fe-exec.c:3620
int PQgetCopyData(PGconn *conn, char **buffer, int async)
Definition fe-exec.c:2833
int lo_close(PGconn *conn, int fd)
Definition fe-lobj.c:96
int lo_open(PGconn *conn, Oid lobjId, int mode)
Definition fe-lobj.c:57
void * pg_malloc(size_t size)
Definition fe_memutils.c:53
char * pg_strdup(const char *in)
Definition fe_memutils.c:91
void pg_free(void *ptr)
#define pg_realloc_array(pointer, type, count)
Definition fe_memutils.h:74
#define pg_malloc_array(type, count)
Definition fe_memutils.h:66
#define pg_malloc0_object(type)
Definition fe_memutils.h:61
#define pg_malloc_object(type)
Definition fe_memutils.h:60
#define pg_malloc0_array(type, count)
Definition fe_memutils.h:67
DataDirSyncMethod
Definition file_utils.h:28
@ DATA_DIR_SYNC_METHOD_FSYNC
Definition file_utils.h:29
void filter_init(FilterStateData *fstate, const char *filename, exit_function f_exit)
Definition filter.c:36
void filter_free(FilterStateData *fstate)
Definition filter.c:60
const char * filter_object_type_name(FilterObjectType fot)
Definition filter.c:82
bool filter_read_item(FilterStateData *fstate, char **objname, FilterCommandType *comtype, FilterObjectType *objtype)
Definition filter.c:392
void pg_log_filter_error(FilterStateData *fstate, const char *fmt,...)
Definition filter.c:154
FilterObjectType
Definition filter.h:48
@ FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN
Definition filter.h:51
@ FILTER_OBJECT_TYPE_SCHEMA
Definition filter.h:57
@ FILTER_OBJECT_TYPE_INDEX
Definition filter.h:56
@ FILTER_OBJECT_TYPE_TRIGGER
Definition filter.h:60
@ FILTER_OBJECT_TYPE_FOREIGN_DATA
Definition filter.h:54
@ FILTER_OBJECT_TYPE_DATABASE
Definition filter.h:52
@ FILTER_OBJECT_TYPE_FUNCTION
Definition filter.h:55
@ FILTER_OBJECT_TYPE_TABLE_DATA
Definition filter.h:50
@ FILTER_OBJECT_TYPE_NONE
Definition filter.h:49
@ FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN
Definition filter.h:59
@ FILTER_OBJECT_TYPE_EXTENSION
Definition filter.h:53
@ FILTER_OBJECT_TYPE_TABLE
Definition filter.h:58
FilterCommandType
Definition filter.h:38
@ FILTER_COMMAND_TYPE_NONE
Definition filter.h:39
@ FILTER_COMMAND_TYPE_EXCLUDE
Definition filter.h:41
@ FILTER_COMMAND_TYPE_INCLUDE
Definition filter.h:40
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition getopt_long.c:60
#define no_argument
Definition getopt_long.h:25
#define required_argument
Definition getopt_long.h:26
#define comment
#define storage
long val
Definition informix.c:689
static struct @177 value
static char * encoding
Definition initdb.c:139
static DataDirSyncMethod sync_method
Definition initdb.c:170
static int pg_cmp_u32(uint32 a, uint32 b)
Definition int.h:719
int j
Definition isn.c:78
int i
Definition isn.c:77
#define PQgetvalue
#define PQgetResult
#define PQgetlength
#define PQclear
#define PQnfields
#define PQresultStatus
#define PQgetisnull
#define PQfname
#define PQntuples
@ PGRES_COMMAND_OK
Definition libpq-fe.h:131
@ PGRES_COPY_OUT
Definition libpq-fe.h:137
@ PGRES_TUPLES_OK
Definition libpq-fe.h:134
#define INV_READ
Definition libpq-fs.h:22
void pg_logging_increase_verbosity(void)
Definition logging.c:187
void pg_logging_init(const char *argv0)
Definition logging.c:85
void pg_logging_set_level(enum pg_log_level new_level)
Definition logging.c:178
#define pg_log_error(...)
Definition logging.h:108
#define pg_log_error_hint(...)
Definition logging.h:114
#define pg_log_info(...)
Definition logging.h:126
@ PG_LOG_WARNING
Definition logging.h:38
#define pg_log_error_detail(...)
Definition logging.h:111
const char * progname
Definition main.c:44
char * pstrdup(const char *in)
Definition mcxt.c:1910
bool option_parse_int(const char *optarg, const char *optname, int min_range, int max_range, int *result)
bool parse_sync_method(const char *optarg, DataDirSyncMethod *sync_method)
#define check_mut_excl_opts(set, opt,...)
Oid oprid(Operator op)
Definition parse_oper.c:241
static AmcheckOptions opts
Definition pg_amcheck.c:112
NameData attname
char attalign
int16 attlen
NameData rolname
Definition pg_authid.h:36
@ SECTION_NONE
Definition pg_backup.h:57
@ SECTION_POST_DATA
Definition pg_backup.h:60
@ SECTION_PRE_DATA
Definition pg_backup.h:58
@ SECTION_DATA
Definition pg_backup.h:59
int DumpId
Definition pg_backup.h:285
int EndLO(Archive *AHX, Oid oid)
void ProcessArchiveRestoreOptions(Archive *AHX)
RestoreOptions * NewRestoreOptions(void)
#define InvalidDumpId
Definition pg_backup.h:287
#define appendStringLiteralAH(buf, str, AH)
Definition pg_backup.h:344
int StartLO(Archive *AHX, Oid oid)
enum _archiveFormat ArchiveFormat
void RestoreArchive(Archive *AHX, bool append_data)
void ConnectDatabaseAhx(Archive *AHX, const ConnParams *cparams, bool isReconnect)
void CloseArchive(Archive *AHX)
Archive * CreateArchive(const char *FileSpec, const ArchiveFormat fmt, const pg_compress_specification compression_spec, bool dosync, ArchiveMode mode, SetupWorkerPtrType setupDumpWorker, DataDirSyncMethod sync_method)
@ archModeWrite
Definition pg_backup.h:51
@ archModeAppend
Definition pg_backup.h:50
@ PREPQUERY_DUMPFUNC
Definition pg_backup.h:72
@ PREPQUERY_DUMPTABLEATTACH
Definition pg_backup.h:75
@ PREPQUERY_DUMPBASETYPE
Definition pg_backup.h:67
@ PREPQUERY_DUMPRANGETYPE
Definition pg_backup.h:74
@ PREPQUERY_DUMPOPR
Definition pg_backup.h:73
@ PREPQUERY_DUMPEXTSTATSOBJSTATS
Definition pg_backup.h:71
@ PREPQUERY_GETATTRIBUTESTATS
Definition pg_backup.h:76
@ PREPQUERY_DUMPDOMAIN
Definition pg_backup.h:69
@ PREPQUERY_DUMPCOMPOSITETYPE
Definition pg_backup.h:68
@ PREPQUERY_DUMPAGG
Definition pg_backup.h:66
@ PREPQUERY_GETCOLUMNACLS
Definition pg_backup.h:77
@ PREPQUERY_GETDOMAINCONSTRAINTS
Definition pg_backup.h:78
@ PREPQUERY_DUMPENUMTYPE
Definition pg_backup.h:70
int archprintf(Archive *AH, const char *fmt,...) pg_attribute_printf(2
void SetArchiveOptions(Archive *AH, DumpOptions *dopt, RestoreOptions *ropt)
#define NUM_PREP_QUERIES
Definition pg_backup.h:81
void archputs(const char *s, Archive *AH)
@ archUnknown
Definition pg_backup.h:41
@ archTar
Definition pg_backup.h:43
@ archCustom
Definition pg_backup.h:42
@ archDirectory
Definition pg_backup.h:45
@ archNull
Definition pg_backup.h:44
void InitDumpOptions(DumpOptions *opts)
void WriteData(Archive *AHX, const void *data, size_t dLen)
int TocIDRequired(ArchiveHandle *AH, DumpId id)
TocEntry * ArchiveEntry(Archive *AHX, CatalogId catalogId, DumpId dumpId, ArchiveOpts *opts)
#define ARCHIVE_OPTS(...)
#define LOBBUFSIZE
#define REQ_STATS
int(* DataDumperPtr)(Archive *AH, const void *userArg)
void ExecuteSqlStatement(Archive *AHX, const char *query)
PGresult * ExecuteSqlQuery(Archive *AHX, const char *query, ExecStatusType status)
PGresult * ExecuteSqlQueryForSingleRow(Archive *fout, const char *query)
void exit_nicely(int code)
void set_dump_section(const char *arg, int *dumpSections)
#define pg_fatal(...)
static char format
static char * label
static PgChecksumMode mode
#define FUNC_MAX_ARGS
const void size_t len
const void * data
char datlocprovider
Definition pg_database.h:46
NameData datname
Definition pg_database.h:37
bool datistemplate
Definition pg_database.h:49
int32 datconnlimit
Definition pg_database.h:61
static void expand_schema_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names)
Definition pg_dump.c:1647
static const CatalogId nilCatalogId
Definition pg_dump.c:191
static void dumpEncoding(Archive *AH)
Definition pg_dump.c:3825
void getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:8388
static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId, const char *type, const char *name, const char *subname, const char *nspname, const char *tag, const char *owner, const DumpableAcl *dacl)
Definition pg_dump.c:16570
static SimpleStringList schema_include_patterns
Definition pg_dump.c:167
static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
Definition pg_dump.c:18391
ExtensionInfo * getExtensions(Archive *fout, int *numExtensions)
Definition pg_dump.c:6215
static void selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
Definition pg_dump.c:2199
static void collectBinaryUpgradeClassOids(Archive *fout)
Definition pg_dump.c:5915
static PQExpBuffer createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:17226
static void dumpUserMappings(Archive *fout, const char *servername, const char *namespace, const char *owner, CatalogId catalogId, DumpId dumpId)
Definition pg_dump.c:16384
static void dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
Definition pg_dump.c:5015
static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs, DumpableObject *boundaryObjs)
Definition pg_dump.c:20657
void getPublicationNamespaces(Archive *fout)
Definition pg_dump.c:4804
static void dumpSearchPath(Archive *AH)
Definition pg_dump.c:3874
static int ncomments
Definition pg_dump.c:203
static void selectDumpableTable(TableInfo *tbinfo, Archive *fout)
Definition pg_dump.c:2068
static DumpableObject * createBoundaryObjects(void)
Definition pg_dump.c:20633
static char * convertTSFunction(Archive *fout, Oid funcOid)
Definition pg_dump.c:14554
static void dumpDatabase(Archive *fout)
Definition pg_dump.c:3272
static SimpleStringList table_include_patterns
Definition pg_dump.c:172
static void append_depends_on_extension(Archive *fout, PQExpBuffer create, const DumpableObject *dobj, const char *catalog, const char *keyword, const char *objname)
Definition pg_dump.c:5728
static Oid get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
Definition pg_dump.c:5773
static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
Definition pg_dump.c:12016
static bool forcePartitionRootLoad(const TableInfo *tbinfo)
Definition pg_dump.c:2829
static void dumpCast(Archive *fout, const CastInfo *cast)
Definition pg_dump.c:14030
static SimpleOidList schema_exclude_oids
Definition pg_dump.c:170
static bool have_extra_float_digits
Definition pg_dump.c:194
static void dumpIndex(Archive *fout, const IndxInfo *indxinfo)
Definition pg_dump.c:18481
void getPartitioningInfo(Archive *fout)
Definition pg_dump.c:7877
static int nbinaryUpgradeClassOids
Definition pg_dump.c:211
static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12609
OidOptions
Definition pg_dump.c:145
@ zeroIsError
Definition pg_dump.c:146
@ zeroAsStar
Definition pg_dump.c:147
@ zeroAsNone
Definition pg_dump.c:148
static char * dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
Definition pg_dump.c:11246
static SimpleOidList extension_include_oids
Definition pg_dump.c:186
static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
Definition pg_dump.c:15953
static void dumpAgg(Archive *fout, const AggInfo *agginfo)
Definition pg_dump.c:15529
static int extra_float_digits
Definition pg_dump.c:195
static int SequenceItemCmp(const void *p1, const void *p2)
Definition pg_dump.c:19392
static void dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
Definition pg_dump.c:11533
static void dumpTableComment(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
Definition pg_dump.c:11559
static SimpleStringList extension_include_patterns
Definition pg_dump.c:185
static void selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
Definition pg_dump.c:1982
InhInfo * getInherits(Archive *fout, int *numInherits)
Definition pg_dump.c:7821
void getForeignDataWrappers(Archive *fout)
Definition pg_dump.c:10519
static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
Definition pg_dump.c:19812
static void binary_upgrade_set_type_oids_by_rel(Archive *fout, PQExpBuffer upgrade_buffer, const TableInfo *tbinfo)
Definition pg_dump.c:5884
static void dumpTable(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:17025
static SimpleOidList extension_exclude_oids
Definition pg_dump.c:189
static SimpleStringList table_exclude_patterns
Definition pg_dump.c:175
static PQExpBuffer createViewAsClause(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:17177
static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
Definition pg_dump.c:18675
void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:4226
static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12387
void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], int numExtensions)
Definition pg_dump.c:20195
static void dumpComment(Archive *fout, const char *type, const char *name, const char *namespace, const char *owner, CatalogId catalogId, int subid, DumpId dumpId)
Definition pg_dump.c:11098
static char * getFormattedOperatorName(const char *oproid)
Definition pg_dump.c:14524
static char * format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
Definition pg_dump.c:13579
static int nseclabels
Definition pg_dump.c:207
static pg_compress_algorithm compression_algorithm
Definition pg_dump.c:159
static void dumpStdStrings(Archive *AH)
Definition pg_dump.c:3850
static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
Definition pg_dump.c:19032
static void dumpType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12216
static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
Definition pg_dump.c:18323
void getTypes(Archive *fout)
Definition pg_dump.c:6290
static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
Definition pg_dump.c:14576
static void dumpOpr(Archive *fout, const OprInfo *oprinfo)
Definition pg_dump.c:14264
static void selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
Definition pg_dump.c:2324
static void selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
Definition pg_dump.c:2306
static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
Definition pg_dump.c:19723
static void dumpFunc(Archive *fout, const FuncInfo *finfo)
Definition pg_dump.c:13608
static void selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
Definition pg_dump.c:2152
static void BuildArchiveDependencies(Archive *fout)
Definition pg_dump.c:20783
static RelStatsInfo * getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages, char *reltuples, int32 relallvisible, int32 relallfrozen, char relkind, char **indAttNames, int nindAttNames)
Definition pg_dump.c:7200
static const char *const SeqTypeNames[]
Definition pg_dump.c:119
void getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:7756
static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
Definition pg_dump.c:3026
static int nsequences
Definition pg_dump.c:215
static const char * getAttrName(int attrnum, const TableInfo *tblInfo)
Definition pg_dump.c:18452
static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
Definition pg_dump.c:16284
static RoleNameItem * rolenames
Definition pg_dump.c:198
static void collectRoleNames(Archive *fout)
Definition pg_dump.c:10834
static PGresult * fetchAttributeStats(Archive *fout)
Definition pg_dump.c:11132
static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions, const char *prefix, Archive *fout)
Definition pg_dump.c:20982
void getOpclasses(Archive *fout)
Definition pg_dump.c:6736
void getForeignServers(Archive *fout)
Definition pg_dump.c:10613
void getFuncs(Archive *fout)
Definition pg_dump.c:7005
static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
Definition pg_dump.c:2857
static void prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
Definition pg_dump.c:1907
static bool dosync
Definition pg_dump.c:152
static int dumpTableData_copy(Archive *fout, const void *dcontext)
Definition pg_dump.c:2364
#define MAX_BLOBS_PER_ARCHIVE_ENTRY
Definition pg_dump.c:231
static const char * getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
Definition pg_dump.c:20885
static void getDependencies(Archive *fout)
Definition pg_dump.c:20469
static void buildMatViewRefreshDependencies(Archive *fout)
Definition pg_dump.c:3116
void getTSDictionaries(Archive *fout)
Definition pg_dump.c:10335
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout, PQExpBuffer upgrade_buffer, Oid pg_type_oid, bool force_array_type, bool include_multirange_type)
Definition pg_dump.c:5804
#define DUMP_DEFAULT_ROWS_PER_INSERT
Definition pg_dump.c:224
void getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:4884
static SeqType parse_sequence_type(const char *name)
Definition pg_dump.c:19376
static const char * getRoleName(const char *roleoid_str)
Definition pg_dump.c:10798
static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
Definition pg_dump.c:13378
static SequenceItem * sequences
Definition pg_dump.c:214
static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
Definition pg_dump.c:2972
static int findComments(Oid classoid, Oid objoid, CommentItem **items)
Definition pg_dump.c:11657
static SimpleStringList foreign_servers_include_patterns
Definition pg_dump.c:182
static void selectDumpableCast(CastInfo *cast, Archive *fout)
Definition pg_dump.c:2174
void getCasts(Archive *fout)
Definition pg_dump.c:9137
static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
Definition pg_dump.c:4682
static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
Definition pg_dump.c:4398
void getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:7937
static void setupDumpWorker(Archive *AH)
Definition pg_dump.c:1580
static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
Definition pg_dump.c:8547
void getTSConfigurations(Archive *fout)
Definition pg_dump.c:10460
static int nrolenames
Definition pg_dump.c:199
static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
Definition pg_dump.c:16860
static SimpleStringList table_include_patterns_and_children
Definition pg_dump.c:173
static char * convertRegProcReference(const char *proc)
Definition pg_dump.c:14483
static void getAdditionalACLs(Archive *fout)
Definition pg_dump.c:10869
static void getTableDataFKConstraints(void)
Definition pg_dump.c:3231
static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
Definition pg_dump.c:3007
static SimpleOidList table_exclude_oids
Definition pg_dump.c:177
SeqType
Definition pg_dump.c:113
@ SEQTYPE_BIGINT
Definition pg_dump.c:116
@ SEQTYPE_INTEGER
Definition pg_dump.c:115
@ SEQTYPE_SMALLINT
Definition pg_dump.c:114
void getAccessMethods(Archive *fout)
Definition pg_dump.c:6662
void getConversions(Archive *fout)
Definition pg_dump.c:6600
void getRules(Archive *fout)
Definition pg_dump.c:8682
static void dumpDomain(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12858
void getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
Definition pg_dump.c:9331
static void collectComments(Archive *fout)
Definition pg_dump.c:11734
static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
Definition pg_dump.c:8570
static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
Definition pg_dump.c:14925
static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
Definition pg_dump.c:16478
static void selectDumpableObject(DumpableObject *dobj, Archive *fout)
Definition pg_dump.c:2342
static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
Definition pg_dump.c:16033
static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
Definition pg_dump.c:18632
static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
Definition pg_dump.c:5494
void getCollations(Archive *fout)
Definition pg_dump.c:6534
static char * format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
Definition pg_dump.c:13556
static int strict_names
Definition pg_dump.c:157
static void dumpTransform(Archive *fout, const TransformInfo *transform)
Definition pg_dump.c:14135
void getAggregates(Archive *fout)
Definition pg_dump.c:6864
static void dumpLO(Archive *fout, const LoInfo *loinfo)
Definition pg_dump.c:4090
void getNamespaces(Archive *fout)
Definition pg_dump.c:6083
static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
Definition pg_dump.c:5058
void getPublications(Archive *fout)
Definition pg_dump.c:4516
static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer, const DumpableObject *dobj, const char *objtype, const char *objname, const char *objnamespace)
Definition pg_dump.c:6039
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj)
Definition pg_dump.c:11819
static void getLOs(Archive *fout)
Definition pg_dump.c:3936
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf, const char *dbname, Oid dboid)
Definition pg_dump.c:3781
void getTSParsers(Archive *fout)
Definition pg_dump.c:10261
static void setup_connection(Archive *AH, const char *dumpencoding, const char *dumpsnapshot, char *use_role)
Definition pg_dump.c:1399
static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
Definition pg_dump.c:19353
static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12545
static void selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
Definition pg_dump.c:2232
static const char * fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
Definition pg_dump.c:20936
static void expand_table_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names, bool with_child_tables)
Definition pg_dump.c:1811
static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj, DumpId **dependencies, int *nDeps, int *allocDeps)
Definition pg_dump.c:20830
static void determineNotNullFlags(Archive *fout, PGresult *res, int r, TableInfo *tbinfo, int j, int i_notnull_name, int i_notnull_comment, int i_notnull_invalidoid, int i_notnull_noinherit, int i_notnull_islocal, PQExpBuffer *invalidnotnulloids)
Definition pg_dump.c:10119
static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:17266
static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
Definition pg_dump.c:15889
static void expand_foreign_server_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids)
Definition pg_dump.c:1759
TableInfo * getTables(Archive *fout, int *numTables)
Definition pg_dump.c:7278
static void dumpRule(Archive *fout, const RuleInfo *rinfo)
Definition pg_dump.c:20028
static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:13083
static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12247
static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
Definition pg_dump.c:12093
#define fmtQualifiedDumpable(obj)
Definition pg_dump.c:236
static bool nonemptyReloptions(const char *reloptions)
Definition pg_dump.c:20970
static SimpleStringList extension_exclude_patterns
Definition pg_dump.c:188
static BinaryUpgradeClassOidItem * binaryUpgradeClassOids
Definition pg_dump.c:210
static SimpleOidList table_include_oids
Definition pg_dump.c:174
static void dumpStatisticsExtStats(Archive *fout, const StatsExtInfo *statsextinfo)
Definition pg_dump.c:18752
void getExtendedStatistics(Archive *fout)
Definition pg_dump.c:8306
static NamespaceInfo * findNamespace(Oid nsoid)
Definition pg_dump.c:6197
static char * get_synchronized_snapshot(Archive *fout)
Definition pg_dump.c:1595
static int dumpLOs(Archive *fout, const void *arg)
Definition pg_dump.c:4180
static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
Definition pg_dump.c:5563
static void appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname, const char *argtype, const char *argval)
Definition pg_dump.c:11114
void processExtensionTables(Archive *fout, ExtensionInfo extinfo[], int numExtensions)
Definition pg_dump.c:20288
static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
Definition pg_dump.c:19938
static int BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
Definition pg_dump.c:5899
static void dumpCommentExtended(Archive *fout, const char *type, const char *name, const char *namespace, const char *owner, CatalogId catalogId, int subid, DumpId dumpId, const char *initdb_comment)
Definition pg_dump.c:10998
void getDefaultACLs(Archive *fout)
Definition pg_dump.c:10701
static SimpleStringList tabledata_exclude_patterns
Definition pg_dump.c:178
static void dumpConversion(Archive *fout, const ConvInfo *convinfo)
Definition pg_dump.c:15401
static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
Definition pg_dump.c:16211
static void dumpProcLang(Archive *fout, const ProcLangInfo *plang)
Definition pg_dump.c:13424
static void dumpSecLabel(Archive *fout, const char *type, const char *name, const char *namespace, const char *owner, CatalogId catalogId, int subid, DumpId dumpId)
Definition pg_dump.c:16698
void getSubscriptions(Archive *fout)
Definition pg_dump.c:5159
static void collectSecLabels(Archive *fout)
Definition pg_dump.c:16939
static void selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
Definition pg_dump.c:2267
static void collectSequences(Archive *fout)
Definition pg_dump.c:19407
static Oid g_last_builtin_oid
Definition pg_dump.c:154
#define MAX_ATTR_STATS_RELS
Definition pg_dump.c:218
void getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:8779
void getTransforms(Archive *fout)
Definition pg_dump.c:9247
void getEventTriggers(Archive *fout)
Definition pg_dump.c:8975
static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode)
Definition pg_dump.c:1609
static void read_dump_filters(const char *filename, DumpOptions *dopt)
Definition pg_dump.c:21001
static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
Definition pg_dump.c:16091
static SecLabelItem * seclabels
Definition pg_dump.c:206
static SimpleStringList tabledata_exclude_patterns_and_children
Definition pg_dump.c:179
static char * get_language_name(Archive *fout, Oid langid)
Definition pg_dump.c:9226
static bool checkExtensionMembership(DumpableObject *dobj, Archive *fout)
Definition pg_dump.c:1932
static CommentItem * comments
Definition pg_dump.c:202
static int dumpTableData_insert(Archive *fout, const void *dcontext)
Definition pg_dump.c:2535
static SimpleOidList tabledata_exclude_oids
Definition pg_dump.c:180
static SimpleStringList table_exclude_patterns_and_children
Definition pg_dump.c:176
static void binary_upgrade_set_pg_class_oids(Archive *fout, PQExpBuffer upgrade_buffer, Oid pg_class_oid)
Definition pg_dump.c:5949
void getTSTemplates(Archive *fout)
Definition pg_dump.c:10401
static void set_restrict_relation_kind(Archive *AH, const char *value)
Definition pg_dump.c:5138
static char * format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
Definition pg_dump.c:15497
void getProcLangs(Archive *fout)
Definition pg_dump.c:9053
static void dumpSequence(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:19469
bool shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
Definition pg_dump.c:10246
static TableInfo * getRootTableInfo(const TableInfo *tbinfo)
Definition pg_dump.c:2804
void getSubscriptionRelations(Archive *fout)
Definition pg_dump.c:5408
void getOperators(Archive *fout)
Definition pg_dump.c:6458
static SimpleOidList foreign_servers_include_oids
Definition pg_dump.c:183
static void dumpCollation(Archive *fout, const CollInfo *collinfo)
Definition pg_dump.c:15144
static void dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
Definition pg_dump.c:16778
static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo, PGresult *res)
Definition pg_dump.c:13289
static void expand_extension_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names)
Definition pg_dump.c:1706
void getOpfamilies(Archive *fout)
Definition pg_dump.c:6799
static void selectDumpableType(TypeInfo *tyinfo, Archive *fout)
Definition pg_dump.c:2107
static SimpleOidList schema_include_oids
Definition pg_dump.c:168
static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
Definition pg_dump.c:14644
static SimpleStringList schema_exclude_patterns
Definition pg_dump.c:169
#define DUMP_COMPONENT_COMMENT
Definition pg_dump.h:111
#define DUMP_COMPONENT_DATA
Definition pg_dump.h:110
#define DUMP_COMPONENT_USERMAP
Definition pg_dump.h:115
#define DUMP_COMPONENT_POLICY
Definition pg_dump.h:114
#define DUMP_COMPONENT_SECLABEL
Definition pg_dump.h:112
#define DUMP_COMPONENT_ALL
Definition pg_dump.h:117
#define DUMP_COMPONENT_ACL
Definition pg_dump.h:113
#define DUMP_COMPONENT_NONE
Definition pg_dump.h:108
#define DUMP_COMPONENTS_REQUIRING_LOCK
Definition pg_dump.h:141
void sortDumpableObjects(DumpableObject **objs, int numObjs, DumpId preBoundaryId, DumpId postBoundaryId)
#define DUMP_COMPONENT_DEFINITION
Definition pg_dump.h:109
@ DO_EVENT_TRIGGER
Definition pg_dump.h:80
@ DO_REFRESH_MATVIEW
Definition pg_dump.h:81
@ DO_POLICY
Definition pg_dump.h:82
@ DO_CAST
Definition pg_dump.h:64
@ DO_FOREIGN_SERVER
Definition pg_dump.h:73
@ DO_PRE_DATA_BOUNDARY
Definition pg_dump.h:78
@ DO_PROCLANG
Definition pg_dump.h:63
@ DO_TYPE
Definition pg_dump.h:43
@ DO_INDEX
Definition pg_dump.h:56
@ DO_COLLATION
Definition pg_dump.h:51
@ DO_LARGE_OBJECT
Definition pg_dump.h:76
@ DO_TSCONFIG
Definition pg_dump.h:71
@ DO_OPERATOR
Definition pg_dump.h:47
@ DO_FK_CONSTRAINT
Definition pg_dump.h:62
@ DO_CONSTRAINT
Definition pg_dump.h:61
@ DO_SUBSCRIPTION
Definition pg_dump.h:87
@ DO_DEFAULT_ACL
Definition pg_dump.h:74
@ DO_FDW
Definition pg_dump.h:72
@ DO_SUBSCRIPTION_REL
Definition pg_dump.h:88
@ DO_REL_STATS
Definition pg_dump.h:86
@ DO_SEQUENCE_SET
Definition pg_dump.h:66
@ DO_ATTRDEF
Definition pg_dump.h:55
@ DO_PUBLICATION_REL
Definition pg_dump.h:84
@ DO_TABLE_ATTACH
Definition pg_dump.h:54
@ DO_OPCLASS
Definition pg_dump.h:49
@ DO_INDEX_ATTACH
Definition pg_dump.h:57
@ DO_TSTEMPLATE
Definition pg_dump.h:70
@ DO_STATSEXT
Definition pg_dump.h:58
@ DO_FUNC
Definition pg_dump.h:45
@ DO_POST_DATA_BOUNDARY
Definition pg_dump.h:79
@ DO_LARGE_OBJECT_DATA
Definition pg_dump.h:77
@ DO_OPFAMILY
Definition pg_dump.h:50
@ DO_TRANSFORM
Definition pg_dump.h:75
@ DO_ACCESS_METHOD
Definition pg_dump.h:48
@ DO_PUBLICATION_TABLE_IN_SCHEMA
Definition pg_dump.h:85
@ DO_CONVERSION
Definition pg_dump.h:52
@ DO_TRIGGER
Definition pg_dump.h:60
@ DO_RULE
Definition pg_dump.h:59
@ DO_DUMMY_TYPE
Definition pg_dump.h:67
@ DO_TSDICT
Definition pg_dump.h:69
@ DO_TSPARSER
Definition pg_dump.h:68
@ DO_EXTENSION
Definition pg_dump.h:42
@ DO_TABLE_DATA
Definition pg_dump.h:65
@ DO_PUBLICATION
Definition pg_dump.h:83
@ DO_TABLE
Definition pg_dump.h:53
@ DO_NAMESPACE
Definition pg_dump.h:41
@ DO_AGG
Definition pg_dump.h:46
@ DO_SHELL_TYPE
Definition pg_dump.h:44
void sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
#define DUMP_COMPONENT_STATISTICS
Definition pg_dump.h:116
static int statistics_only
Definition pg_dumpall.c:125
static Archive * fout
Definition pg_dumpall.c:139
static int no_statistics
Definition pg_dumpall.c:116
static int no_data
Definition pg_dumpall.c:114
static int no_schema
Definition pg_dumpall.c:115
static char * filename
Definition pg_dumpall.c:133
static int with_statistics
Definition pg_dumpall.c:121
PGDLLIMPORT int optind
Definition getopt.c:47
PGDLLIMPORT char * optarg
Definition getopt.c:49
NameData subname
static char buf[DEFAULT_XLOG_SEG_SIZE]
char typalign
Definition pg_type.h:178
#define pg_encoding_to_char
Definition pg_wchar.h:483
static char * tablespace
Definition pgbench.c:217
#define pg_log_warning(...)
Definition pgfnames.c:24
int pg_strcasecmp(const char *s1, const char *s2)
#define sprintf
Definition port.h:263
#define snprintf
Definition port.h:261
const char * get_progname(const char *argv0)
Definition path.c:669
#define printf(...)
Definition port.h:267
off_t pgoff_t
Definition port.h:422
#define InvalidOid
unsigned int Oid
#define atooid(x)
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
PQExpBuffer createPQExpBuffer(void)
Definition pqexpbuffer.c:72
void initPQExpBuffer(PQExpBuffer str)
Definition pqexpbuffer.c:90
void resetPQExpBuffer(PQExpBuffer str)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
void destroyPQExpBuffer(PQExpBuffer str)
void appendPQExpBufferChar(PQExpBuffer str, char ch)
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
void termPQExpBuffer(PQExpBuffer str)
char * c
static int fb(int x)
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
Oid RelFileNumber
Definition relpath.h:25
#define RelFileNumberIsValid(relnumber)
Definition relpath.h:27
bool quote_all_identifiers
Definition ruleutils.c:344
void simple_string_list_append(SimpleStringList *list, const char *val)
Definition simple_list.c:63
void simple_ptr_list_append(SimplePtrList *list, void *ptr)
bool simple_oid_list_member(SimpleOidList *list, Oid val)
Definition simple_list.c:45
void simple_oid_list_append(SimpleOidList *list, Oid val)
Definition simple_list.c:26
#define free(a)
#define PG_DEPENDENCIES_KEY_ATTRIBUTES
#define PG_DEPENDENCIES_KEY_DEGREE
#define PG_DEPENDENCIES_KEY_DEPENDENCY
#define PG_NDISTINCT_KEY_ATTRIBUTES
#define PG_NDISTINCT_KEY_NDISTINCT
PGconn * GetConnection(void)
Definition streamutil.c:60
char * dbname
Definition streamutil.c:49
PGconn * conn
Definition streamutil.c:52
const char * fmtId(const char *rawid)
void setFmtEncoding(int encoding)
void appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
void appendPGArray(PQExpBuffer buffer, const char *value)
bool processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern, bool have_where, bool force_escape, const char *schemavar, const char *namevar, const char *altnamevar, const char *visibilityrule, PQExpBuffer dbnamebuf, int *dotcnt)
bool parsePGArray(const char *atext, char ***itemarray, int *nitems)
bool appendReloptionsArray(PQExpBuffer buffer, const char *reloptions, const char *prefix, int encoding, bool std_strings)
void appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
int minRemoteVersion
Definition pg_backup.h:237
int remoteVersion
Definition pg_backup.h:234
DumpOptions * dopt
Definition pg_backup.h:229
bool * is_prepared
Definition pg_backup.h:256
char * searchpath
Definition pg_backup.h:248
bool isStandby
Definition pg_backup.h:235
int maxRemoteVersion
Definition pg_backup.h:238
bool std_strings
Definition pg_backup.h:245
int numWorkers
Definition pg_backup.h:240
int encoding
Definition pg_backup.h:244
char * use_role
Definition pg_backup.h:249
char * sync_snapshot_id
Definition pg_backup.h:241
int verbose
Definition pg_backup.h:232
RelFileNumber toast_index_relfilenumber
Definition pg_dump.c:108
RelFileNumber toast_relfilenumber
Definition pg_dump.c:106
RelFileNumber relfilenumber
Definition pg_dump.c:104
Oid tableoid
Definition pg_backup.h:281
Oid classoid
Definition pg_dump.c:86
Oid objoid
Definition pg_dump.c:87
int objsubid
Definition pg_dump.c:88
const char * descr
Definition pg_dump.c:85
const char * rolename
Definition pg_dump.c:80
Oid roleoid
Definition pg_dump.c:79
const char * provider
Definition pg_dump.c:93
Oid classoid
Definition pg_dump.c:95
int objsubid
Definition pg_dump.c:97
const char * label
Definition pg_dump.c:94
int64 minv
Definition pg_dump.c:134
int64 cache
Definition pg_dump.c:138
int64 startv
Definition pg_dump.c:136
int64 maxv
Definition pg_dump.c:135
bool is_called
Definition pg_dump.c:140
int64 incby
Definition pg_dump.c:137
int64 last_value
Definition pg_dump.c:139
SeqType seqtype
Definition pg_dump.c:132
bool cycled
Definition pg_dump.c:133
bool null_seqtuple
Definition pg_dump.c:141
SimpleOidListCell * head
Definition simple_list.h:28
struct SimplePtrListCell * next
Definition simple_list.h:48
char val[FLEXIBLE_ARRAY_MEMBER]
Definition simple_list.h:37
struct SimpleStringListCell * next
Definition simple_list.h:34
SimpleStringListCell * head
Definition simple_list.h:42
char * suboriginremotelsn
Definition pg_dump.h:732
bool subpasswordrequired
Definition pg_dump.h:720
const char * rolname
Definition pg_dump.h:714
char * subservername
Definition pg_dump.h:725
char * subsynccommit
Definition pg_dump.h:728
char * subpublications
Definition pg_dump.h:730
char * subwalrcvtimeout
Definition pg_dump.h:729
char * subslotname
Definition pg_dump.h:727
char subtwophasestate
Definition pg_dump.h:718
bool subretaindeadtuples
Definition pg_dump.h:723
char * subconninfo
Definition pg_dump.h:726
DumpableObject dobj
Definition pg_dump.h:713
DumpableObject dobj
Definition pg_dump.h:270
ArchiveFormat format
struct _tocEntry * toc
DumpableObject dobj
Definition pg_dump.h:404
char * adef_expr
Definition pg_dump.h:407
TableInfo * adtable
Definition pg_dump.h:405
bool separate
Definition pg_dump.h:408
char * pgport
Definition pg_backup.h:88
char * pghost
Definition pg_backup.h:89
trivalue promptPassword
Definition pg_backup.h:91
char * username
Definition pg_backup.h:90
char * dbname
Definition pg_backup.h:87
TypeInfo * condomain
Definition pg_dump.h:520
TableInfo * contable
Definition pg_dump.h:519
DumpableObject dobj
Definition pg_dump.h:518
DumpId conindex
Definition pg_dump.h:524
bool condeferrable
Definition pg_dump.h:525
char * condef
Definition pg_dump.h:522
int no_toast_compression
Definition pg_backup.h:192
char * restrict_key
Definition pg_backup.h:220
int column_inserts
Definition pg_backup.h:185
bool dontOutputLOs
Definition pg_backup.h:208
int use_setsessauth
Definition pg_backup.h:198
int outputCreateDB
Definition pg_backup.h:206
bool include_everything
Definition pg_backup.h:203
int sequence_data
Definition pg_backup.h:212
int disable_dollar_quoting
Definition pg_backup.h:184
bool dumpSchema
Definition pg_backup.h:216
int serializable_deferrable
Definition pg_backup.h:194
int outputNoTableAm
Definition pg_backup.h:196
int enable_row_security
Definition pg_backup.h:199
char * outputSuperuser
Definition pg_backup.h:210
int no_security_labels
Definition pg_backup.h:190
int no_unlogged_table_data
Definition pg_backup.h:193
bool dumpStatistics
Definition pg_backup.h:218
int no_publications
Definition pg_backup.h:189
ConnParams cparams
Definition pg_backup.h:173
const char * lockWaitTimeout
Definition pg_backup.h:180
int no_subscriptions
Definition pg_backup.h:191
int load_via_partition_root
Definition pg_backup.h:200
int outputNoTablespaces
Definition pg_backup.h:197
int disable_triggers
Definition pg_backup.h:195
int outputNoOwner
Definition pg_backup.h:209
int binary_upgrade
Definition pg_backup.h:175
char privtype
Definition pg_dump.h:173
char * acldefault
Definition pg_dump.h:171
char * acl
Definition pg_dump.h:170
char * initprivs
Definition pg_dump.h:174
DumpComponents dump
Definition pg_dump.h:153
DumpId * dependencies
Definition pg_dump.h:159
DumpId dumpId
Definition pg_dump.h:151
DumpComponents components
Definition pg_dump.h:156
DumpableObjectType objType
Definition pg_dump.h:149
CatalogId catId
Definition pg_dump.h:150
DumpComponents dump_contains
Definition pg_dump.h:155
bool depends_on_ext
Definition pg_dump.h:158
DumpableObject dobj
Definition pg_dump.h:195
char * extconfig
Definition pg_dump.h:199
bool postponed_def
Definition pg_dump.h:248
Oid lang
Definition pg_dump.h:244
const char * rolname
Definition pg_dump.h:243
Oid * argtypes
Definition pg_dump.h:246
Oid prorettype
Definition pg_dump.h:247
DumpableObject dobj
Definition pg_dump.h:241
int nargs
Definition pg_dump.h:245
DumpableAcl dacl
Definition pg_dump.h:242
int32 nindAttNames
Definition pg_dump.h:463
char ** indAttNames
Definition pg_dump.h:462
int32 relpages
Definition pg_dump.h:452
int32 relallfrozen
Definition pg_dump.h:455
char * reltuples
Definition pg_dump.h:453
teSection section
Definition pg_dump.h:464
int32 relallvisible
Definition pg_dump.h:454
DumpableObject dobj
Definition pg_dump.h:450
int suppressDumpWarnings
Definition pg_backup.h:152
ConnParams cparams
Definition pg_backup.h:146
pg_compress_specification compression_spec
Definition pg_backup.h:150
int disable_dollar_quoting
Definition pg_backup.h:110
char * restrict_key
Definition pg_backup.h:168
const char * filename
Definition pg_backup.h:121
const char * lockWaitTimeout
Definition pg_backup.h:125
int enable_row_security
Definition pg_backup.h:159
DumpableObject dobj
Definition pg_dump.h:477
bool separate
Definition pg_dump.h:482
char ev_enabled
Definition pg_dump.h:481
bool is_instead
Definition pg_dump.h:480
TableInfo * ruletable
Definition pg_dump.h:478
char ev_type
Definition pg_dump.h:479
DumpableObject dobj
Definition pg_dump.h:413
char * attidentity
Definition pg_dump.h:361
char * reltablespace
Definition pg_dump.h:314
struct _relStatsInfo * stats
Definition pg_dump.h:381
int ncheck
Definition pg_dump.h:330
bool ispartition
Definition pg_dump.h:344
DumpableObject dobj
Definition pg_dump.h:307
bool is_identity_sequence
Definition pg_dump.h:337
Oid reloftype
Definition pg_dump.h:332
bool interesting
Definition pg_dump.h:341
char * toast_reloptions
Definition pg_dump.h:317
struct _tableInfo ** parents
Definition pg_dump.h:348
DumpableAcl dacl
Definition pg_dump.h:308
bool relispopulated
Definition pg_dump.h:312
Oid reltype
Definition pg_dump.h:331
bool hasoids
Definition pg_dump.h:324
Oid toast_oid
Definition pg_dump.h:327
Oid foreign_server
Definition pg_dump.h:333
bool hasrules
Definition pg_dump.h:319
uint32 frozenxid
Definition pg_dump.h:325
int owning_col
Definition pg_dump.h:336
char * checkoption
Definition pg_dump.h:316
bool hastriggers
Definition pg_dump.h:320
const char * rolname
Definition pg_dump.h:309
char relreplident
Definition pg_dump.h:313
uint32 minmxid
Definition pg_dump.h:326
int toastpages
Definition pg_dump.h:339
Oid owning_tab
Definition pg_dump.h:335
struct _tableDataInfo * dataObj
Definition pg_dump.h:390
char * amname
Definition pg_dump.h:383
bool dummy_view
Definition pg_dump.h:342
int32 relpages
Definition pg_dump.h:338
bool forcerowsec
Definition pg_dump.h:323
bool hascolumnACLs
Definition pg_dump.h:321
char relpersistence
Definition pg_dump.h:311
char ** attnames
Definition pg_dump.h:355
char relkind
Definition pg_dump.h:310
bool hasindex
Definition pg_dump.h:318
char * reloptions
Definition pg_dump.h:315
uint32 toast_frozenxid
Definition pg_dump.h:328
uint32 toast_minmxid
Definition pg_dump.h:329
bool postponed_def
Definition pg_dump.h:343
bool rowsec
Definition pg_dump.h:322
struct _tocEntry * next
const void * defnDumperArg
DumpId * dependencies
DumpableObject dobj
Definition pg_dump.h:555
DumpableObject dobj
Definition pg_dump.h:205
char data[NAMEDATALEN]
Definition c.h:831
#define MinTransactionIdAttributeNumber
Definition sysattr.h:22
#define MaxCommandIdAttributeNumber
Definition sysattr.h:25
#define MaxTransactionIdAttributeNumber
Definition sysattr.h:24
#define TableOidAttributeNumber
Definition sysattr.h:26
#define SelfItemPointerAttributeNumber
Definition sysattr.h:21
#define MinCommandIdAttributeNumber
Definition sysattr.h:23
static StringInfo copybuf
Definition tablesync.c:129
static ItemArray items
static void * fn(void *arg)
#define FirstNormalObjectId
Definition transam.h:197
@ TRI_YES
Definition vacuumlo.c:38
@ TRI_NO
Definition vacuumlo.c:37
static char * error_detail
Definition validator.c:43
bool SplitGUCList(char *rawstring, char separator, List **namelist)
Definition varlena.c:3063
const char * description
const char * type
const char * name
ArchiveMode
Definition xlog.h:66

◆ MAX_ATTR_STATS_RELS

#define MAX_ATTR_STATS_RELS   64

Definition at line 218 of file pg_dump.c.

◆ MAX_BLOBS_PER_ARCHIVE_ENTRY

#define MAX_BLOBS_PER_ARCHIVE_ENTRY   1000

Definition at line 231 of file pg_dump.c.

Typedef Documentation

◆ OidOptions

◆ SeqType

Enumeration Type Documentation

◆ OidOptions

Enumerator
zeroIsError 
zeroAsStar 
zeroAsNone 

Definition at line 144 of file pg_dump.c.

145{
146 zeroIsError = 1,
147 zeroAsStar = 2,
148 zeroAsNone = 4,
149} OidOptions;

◆ SeqType

Enumerator
SEQTYPE_SMALLINT 
SEQTYPE_INTEGER 
SEQTYPE_BIGINT 

Definition at line 112 of file pg_dump.c.

Function Documentation

◆ addBoundaryDependencies()

static void addBoundaryDependencies ( DumpableObject **  dobjs,
int  numObjs,
DumpableObject boundaryObjs 
)
static

Definition at line 20657 of file pg_dump.c.

20659{
20662 int i;
20663
20664 for (i = 0; i < numObjs; i++)
20665 {
20666 DumpableObject *dobj = dobjs[i];
20667
20668 /*
20669 * The classification of object types here must match the SECTION_xxx
20670 * values assigned during subsequent ArchiveEntry calls!
20671 */
20672 switch (dobj->objType)
20673 {
20674 case DO_NAMESPACE:
20675 case DO_EXTENSION:
20676 case DO_TYPE:
20677 case DO_SHELL_TYPE:
20678 case DO_FUNC:
20679 case DO_AGG:
20680 case DO_OPERATOR:
20681 case DO_ACCESS_METHOD:
20682 case DO_OPCLASS:
20683 case DO_OPFAMILY:
20684 case DO_COLLATION:
20685 case DO_CONVERSION:
20686 case DO_TABLE:
20687 case DO_TABLE_ATTACH:
20688 case DO_ATTRDEF:
20689 case DO_PROCLANG:
20690 case DO_CAST:
20691 case DO_DUMMY_TYPE:
20692 case DO_TSPARSER:
20693 case DO_TSDICT:
20694 case DO_TSTEMPLATE:
20695 case DO_TSCONFIG:
20696 case DO_FDW:
20697 case DO_FOREIGN_SERVER:
20698 case DO_TRANSFORM:
20699 /* Pre-data objects: must come before the pre-data boundary */
20701 break;
20702 case DO_TABLE_DATA:
20703 case DO_SEQUENCE_SET:
20704 case DO_LARGE_OBJECT:
20706 /* Data objects: must come between the boundaries */
20707 addObjectDependency(dobj, preDataBound->dumpId);
20709 break;
20710 case DO_INDEX:
20711 case DO_INDEX_ATTACH:
20712 case DO_STATSEXT:
20713 case DO_REFRESH_MATVIEW:
20714 case DO_TRIGGER:
20715 case DO_EVENT_TRIGGER:
20716 case DO_DEFAULT_ACL:
20717 case DO_POLICY:
20718 case DO_PUBLICATION:
20719 case DO_PUBLICATION_REL:
20721 case DO_SUBSCRIPTION:
20723 /* Post-data objects: must come after the post-data boundary */
20724 addObjectDependency(dobj, postDataBound->dumpId);
20725 break;
20726 case DO_RULE:
20727 /* Rules are post-data, but only if dumped separately */
20728 if (((RuleInfo *) dobj)->separate)
20729 addObjectDependency(dobj, postDataBound->dumpId);
20730 break;
20731 case DO_CONSTRAINT:
20732 case DO_FK_CONSTRAINT:
20733 /* Constraints are post-data, but only if dumped separately */
20734 if (((ConstraintInfo *) dobj)->separate)
20735 addObjectDependency(dobj, postDataBound->dumpId);
20736 break;
20738 /* nothing to do */
20739 break;
20741 /* must come after the pre-data boundary */
20742 addObjectDependency(dobj, preDataBound->dumpId);
20743 break;
20744 case DO_REL_STATS:
20745 /* stats section varies by parent object type, DATA or POST */
20746 if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20747 {
20748 addObjectDependency(dobj, preDataBound->dumpId);
20749 addObjectDependency(postDataBound, dobj->dumpId);
20750 }
20751 else
20752 addObjectDependency(dobj, postDataBound->dumpId);
20753 break;
20754 }
20755 }
20756}

References addObjectDependency(), DO_ACCESS_METHOD, DO_AGG, DO_ATTRDEF, DO_CAST, DO_COLLATION, DO_CONSTRAINT, DO_CONVERSION, DO_DEFAULT_ACL, DO_DUMMY_TYPE, DO_EVENT_TRIGGER, DO_EXTENSION, DO_FDW, DO_FK_CONSTRAINT, DO_FOREIGN_SERVER, DO_FUNC, DO_INDEX, DO_INDEX_ATTACH, DO_LARGE_OBJECT, DO_LARGE_OBJECT_DATA, DO_NAMESPACE, DO_OPCLASS, DO_OPERATOR, DO_OPFAMILY, DO_POLICY, DO_POST_DATA_BOUNDARY, DO_PRE_DATA_BOUNDARY, DO_PROCLANG, DO_PUBLICATION, DO_PUBLICATION_REL, DO_PUBLICATION_TABLE_IN_SCHEMA, DO_REFRESH_MATVIEW, DO_REL_STATS, DO_RULE, DO_SEQUENCE_SET, DO_SHELL_TYPE, DO_STATSEXT, DO_SUBSCRIPTION, DO_SUBSCRIPTION_REL, DO_TABLE, DO_TABLE_ATTACH, DO_TABLE_DATA, DO_TRANSFORM, DO_TRIGGER, DO_TSCONFIG, DO_TSDICT, DO_TSPARSER, DO_TSTEMPLATE, DO_TYPE, _dumpableObject::dumpId, fb(), i, _dumpableObject::objType, and SECTION_DATA.

Referenced by main().

◆ addConstrChildIdxDeps()

static void addConstrChildIdxDeps ( DumpableObject dobj,
const IndxInfo refidx 
)
static

Definition at line 8547 of file pg_dump.c.

8548{
8549 SimplePtrListCell *cell;
8550
8552
8553 for (cell = refidx->partattaches.head; cell; cell = cell->next)
8554 {
8556
8557 addObjectDependency(dobj, attach->dobj.dumpId);
8558
8559 if (attach->partitionIdx->partattaches.head != NULL)
8560 addConstrChildIdxDeps(dobj, attach->partitionIdx);
8561 }
8562}

References addConstrChildIdxDeps(), addObjectDependency(), Assert, DO_FK_CONSTRAINT, _dumpableObject::dumpId, fb(), SimplePtrListCell::next, _dumpableObject::objType, and SimplePtrListCell::ptr.

Referenced by addConstrChildIdxDeps(), and getConstraints().

◆ append_depends_on_extension()

static void append_depends_on_extension ( Archive fout,
PQExpBuffer  create,
const DumpableObject dobj,
const char catalog,
const char keyword,
const char objname 
)
static

Definition at line 5728 of file pg_dump.c.

5734{
5735 if (dobj->depends_on_ext)
5736 {
5737 char *nm;
5738 PGresult *res;
5739 PQExpBuffer query;
5740 int ntups;
5741 int i_extname;
5742 int i;
5743
5744 /* dodge fmtId() non-reentrancy */
5745 nm = pg_strdup(objname);
5746
5747 query = createPQExpBuffer();
5748 appendPQExpBuffer(query,
5749 "SELECT e.extname "
5750 "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5751 "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5752 "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5753 "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5754 catalog,
5755 dobj->catId.oid);
5756 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5757 ntups = PQntuples(res);
5758 i_extname = PQfnumber(res, "extname");
5759 for (i = 0; i < ntups; i++)
5760 {
5761 appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5762 keyword, nm,
5763 fmtId(PQgetvalue(res, i, i_extname)));
5764 }
5765
5766 PQclear(res);
5767 destroyPQExpBuffer(query);
5768 pg_free(nm);
5769 }
5770}

References appendPQExpBuffer(), _dumpableObject::catId, createPQExpBuffer(), PQExpBufferData::data, _dumpableObject::depends_on_ext, destroyPQExpBuffer(), ExecuteSqlQuery(), fb(), fmtId(), fout, i, CatalogId::oid, pg_free(), pg_strdup(), PGRES_TUPLES_OK, PQclear, PQfnumber(), PQgetvalue, and PQntuples.

Referenced by dumpConstraint(), dumpFunc(), dumpIndex(), dumpTableSchema(), and dumpTrigger().

◆ appendNamedArgument()

static void appendNamedArgument ( PQExpBuffer  out,
Archive fout,
const char argname,
const char argtype,
const char argval 
)
static

Definition at line 11114 of file pg_dump.c.

11116{
11117 appendPQExpBufferStr(out, ",\n\t");
11118
11119 appendStringLiteralAH(out, argname, fout);
11120 appendPQExpBufferStr(out, ", ");
11121
11123 appendPQExpBuffer(out, "::%s", argtype);
11124}

References appendPQExpBuffer(), appendPQExpBufferStr(), appendStringLiteralAH, fb(), and fout.

Referenced by dumpRelationStats_dumper(), and dumpStatisticsExtStats().

◆ appendReloptionsArrayAH()

static void appendReloptionsArrayAH ( PQExpBuffer  buffer,
const char reloptions,
const char prefix,
Archive fout 
)
static

Definition at line 20982 of file pg_dump.c.

20984{
20985 bool res;
20986
20987 res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20988 fout->std_strings);
20989 if (!res)
20990 pg_log_warning("could not parse %s array", "reloptions");
20991}

References appendReloptionsArray(), Archive::encoding, fout, pg_log_warning, and Archive::std_strings.

Referenced by dumpConstraint(), dumpRule(), and dumpTableSchema().

◆ binary_upgrade_extension_member()

static void binary_upgrade_extension_member ( PQExpBuffer  upgrade_buffer,
const DumpableObject dobj,
const char objtype,
const char objname,
const char objnamespace 
)
static

Definition at line 6039 of file pg_dump.c.

6044{
6046 int i;
6047
6048 if (!dobj->ext_member)
6049 return;
6050
6051 /*
6052 * Find the parent extension. We could avoid this search if we wanted to
6053 * add a link field to DumpableObject, but the space costs of that would
6054 * be considerable. We assume that member objects could only have a
6055 * direct dependency on their own extension, not any others.
6056 */
6057 for (i = 0; i < dobj->nDeps; i++)
6058 {
6060 if (extobj && extobj->objType == DO_EXTENSION)
6061 break;
6062 extobj = NULL;
6063 }
6064 if (extobj == NULL)
6065 pg_fatal("could not find parent extension for %s %s",
6066 objtype, objname);
6067
6069 "\n-- For binary upgrade, handle extension membership the hard way\n");
6070 appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
6071 fmtId(extobj->name),
6072 objtype);
6073 if (objnamespace && *objnamespace)
6075 appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
6076}

References appendPQExpBuffer(), appendPQExpBufferStr(), _dumpableObject::dependencies, DO_EXTENSION, _dumpableObject::ext_member, fb(), findObjectByDumpId(), fmtId(), i, _dumpableObject::nDeps, and pg_fatal.

Referenced by dumpAccessMethod(), dumpAgg(), dumpBaseType(), dumpCast(), dumpCollation(), dumpCompositeType(), dumpConversion(), dumpDomain(), dumpEnumType(), dumpEventTrigger(), dumpForeignDataWrapper(), dumpForeignServer(), dumpFunc(), dumpNamespace(), dumpOpclass(), dumpOpfamily(), dumpOpr(), dumpProcLang(), dumpRangeType(), dumpSequence(), dumpTableSchema(), dumpTransform(), dumpTSConfig(), dumpTSDictionary(), dumpTSParser(), dumpTSTemplate(), and dumpUndefinedType().

◆ binary_upgrade_set_pg_class_oids()

static void binary_upgrade_set_pg_class_oids ( Archive fout,
PQExpBuffer  upgrade_buffer,
Oid  pg_class_oid 
)
static

Definition at line 5949 of file pg_dump.c.

5951{
5954
5956
5957 /*
5958 * Preserve the OID and relfilenumber of the table, table's index, table's
5959 * toast table and toast table's index if any.
5960 *
5961 * One complexity is that the current table definition might not require
5962 * the creation of a TOAST table, but the old database might have a TOAST
5963 * table that was created earlier, before some wide columns were dropped.
5964 * By setting the TOAST oid we force creation of the TOAST heap and index
5965 * by the new backend, so we can copy the files during binary upgrade
5966 * without worrying about this case.
5967 */
5968 key.oid = pg_class_oid;
5972
5974 "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5975
5976 if (entry->relkind != RELKIND_INDEX &&
5978 {
5980 "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5981 pg_class_oid);
5982
5983 /*
5984 * Not every relation has storage. Also, in a pre-v12 database,
5985 * partitioned tables have a relfilenumber, which should not be
5986 * preserved when upgrading.
5987 */
5988 if (RelFileNumberIsValid(entry->relfilenumber) &&
5991 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5992 entry->relfilenumber);
5993
5994 /*
5995 * In a pre-v12 database, partitioned tables might be marked as having
5996 * toast tables, but we should ignore them if so.
5997 */
5998 if (OidIsValid(entry->toast_oid) &&
6000 {
6002 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
6003 entry->toast_oid);
6005 "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
6006 entry->toast_relfilenumber);
6007
6008 /* every toast table has an index */
6010 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
6011 entry->toast_index_oid);
6013 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
6015 }
6016 }
6017 else
6018 {
6019 /* Preserve the OID and relfilenumber of the index */
6021 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
6022 pg_class_oid);
6024 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
6025 entry->relfilenumber);
6026 }
6027
6029}

References appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), Assert, BinaryUpgradeClassOidItemCmp(), binaryUpgradeClassOids, fb(), nbinaryUpgradeClassOids, OidIsValid, BinaryUpgradeClassOidItem::relfilenumber, RelFileNumberIsValid, BinaryUpgradeClassOidItem::relkind, BinaryUpgradeClassOidItem::toast_index_oid, BinaryUpgradeClassOidItem::toast_index_relfilenumber, BinaryUpgradeClassOidItem::toast_oid, and BinaryUpgradeClassOidItem::toast_relfilenumber.

Referenced by dumpCompositeType(), dumpConstraint(), dumpIndex(), dumpSequence(), and dumpTableSchema().

◆ binary_upgrade_set_type_oids_by_rel()

static void binary_upgrade_set_type_oids_by_rel ( Archive fout,
PQExpBuffer  upgrade_buffer,
const TableInfo tbinfo 
)
static

Definition at line 5884 of file pg_dump.c.

5887{
5888 Oid pg_type_oid = tbinfo->reltype;
5889
5892 pg_type_oid, false, false);
5893}

References binary_upgrade_set_type_oids_by_type_oid(), fb(), fout, and OidIsValid.

Referenced by dumpTableSchema().

◆ binary_upgrade_set_type_oids_by_type_oid()

static void binary_upgrade_set_type_oids_by_type_oid ( Archive fout,
PQExpBuffer  upgrade_buffer,
Oid  pg_type_oid,
bool  force_array_type,
bool  include_multirange_type 
)
static

Definition at line 5804 of file pg_dump.c.

5809{
5811 PGresult *res;
5815 TypeInfo *tinfo;
5816
5817 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5819 "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5820 pg_type_oid);
5821
5823 if (tinfo)
5824 pg_type_array_oid = tinfo->typarray;
5825 else
5827
5830
5832 {
5834 "\n-- For binary upgrade, must preserve pg_type array oid\n");
5836 "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5838 }
5839
5840 /*
5841 * Pre-set the multirange type oid and its own array type oid.
5842 */
5844 {
5845 if (fout->remoteVersion >= 140000)
5846 {
5848 "SELECT t.oid, t.typarray "
5849 "FROM pg_catalog.pg_type t "
5850 "JOIN pg_catalog.pg_range r "
5851 "ON t.oid = r.rngmultitypid "
5852 "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5853 pg_type_oid);
5854
5856
5857 pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5858 pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5859
5860 PQclear(res);
5861 }
5862 else
5863 {
5866 }
5867
5869 "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5871 "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5874 "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5876 "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5878 }
5879
5881}

References appendPQExpBuffer(), appendPQExpBufferStr(), atooid, createPQExpBuffer(), destroyPQExpBuffer(), ExecuteSqlQueryForSingleRow(), fb(), findTypeByOid(), fout, get_next_possible_free_pg_type_oid(), InvalidOid, OidIsValid, PQclear, PQfnumber(), PQgetvalue, printfPQExpBuffer(), Archive::remoteVersion, and tinfo.

Referenced by binary_upgrade_set_type_oids_by_rel(), dumpBaseType(), dumpCompositeType(), dumpDomain(), dumpEnumType(), dumpRangeType(), dumpShellType(), and dumpUndefinedType().

◆ BinaryUpgradeClassOidItemCmp()

static int BinaryUpgradeClassOidItemCmp ( const void p1,
const void p2 
)
static

Definition at line 5899 of file pg_dump.c.

5900{
5903
5904 return pg_cmp_u32(v1.oid, v2.oid);
5905}

References fb(), BinaryUpgradeClassOidItem::oid, and pg_cmp_u32().

Referenced by binary_upgrade_set_pg_class_oids().

◆ BuildArchiveDependencies()

static void BuildArchiveDependencies ( Archive fout)
static

Definition at line 20783 of file pg_dump.c.

20784{
20786 TocEntry *te;
20787
20788 /* Scan all TOC entries in the archive */
20789 for (te = AH->toc->next; te != AH->toc; te = te->next)
20790 {
20791 DumpableObject *dobj;
20792 DumpId *dependencies;
20793 int nDeps;
20794 int allocDeps;
20795
20796 /* No need to process entries that will not be dumped */
20797 if (te->reqs == 0)
20798 continue;
20799 /* Ignore entries that already have "special" dependencies */
20800 if (te->nDeps > 0)
20801 continue;
20802 /* Otherwise, look up the item's original DumpableObject, if any */
20803 dobj = findObjectByDumpId(te->dumpId);
20804 if (dobj == NULL)
20805 continue;
20806 /* No work if it has no dependencies */
20807 if (dobj->nDeps <= 0)
20808 continue;
20809 /* Set up work array */
20810 allocDeps = 64;
20811 dependencies = pg_malloc_array(DumpId, allocDeps);
20812 nDeps = 0;
20813 /* Recursively find all dumpable dependencies */
20814 findDumpableDependencies(AH, dobj,
20815 &dependencies, &nDeps, &allocDeps);
20816 /* And save 'em ... */
20817 if (nDeps > 0)
20818 {
20819 dependencies = pg_realloc_array(dependencies, DumpId, nDeps);
20820 te->dependencies = dependencies;
20821 te->nDeps = nDeps;
20822 }
20823 else
20824 free(dependencies);
20825 }
20826}

References _tocEntry::dependencies, _tocEntry::dumpId, fb(), findDumpableDependencies(), findObjectByDumpId(), fout, free, _tocEntry::nDeps, _dumpableObject::nDeps, _tocEntry::next, pg_malloc_array, pg_realloc_array, _tocEntry::reqs, and _archiveHandle::toc.

Referenced by main().

◆ buildMatViewRefreshDependencies()

static void buildMatViewRefreshDependencies ( Archive fout)
static

Definition at line 3116 of file pg_dump.c.

3117{
3118 PQExpBuffer query;
3119 PGresult *res;
3120 int ntups,
3121 i;
3122 int i_classid,
3123 i_objid,
3124 i_refobjid;
3125
3126 /* No Mat Views before 9.3. */
3127 if (fout->remoteVersion < 90300)
3128 return;
3129
3130 query = createPQExpBuffer();
3131
3132 appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3133 "( "
3134 "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3135 "FROM pg_depend d1 "
3136 "JOIN pg_class c1 ON c1.oid = d1.objid "
3137 "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3138 " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3139 "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3140 "AND d2.objid = r1.oid "
3141 "AND d2.refobjid <> d1.objid "
3142 "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3143 "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3145 "WHERE d1.classid = 'pg_class'::regclass "
3146 "UNION "
3147 "SELECT w.objid, d3.refobjid, c3.relkind "
3148 "FROM w "
3149 "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3150 "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3151 "AND d3.objid = r3.oid "
3152 "AND d3.refobjid <> w.refobjid "
3153 "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3154 "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3156 ") "
3157 "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3158 "FROM w "
3159 "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3160
3161 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3162
3163 ntups = PQntuples(res);
3164
3165 i_classid = PQfnumber(res, "classid");
3166 i_objid = PQfnumber(res, "objid");
3167 i_refobjid = PQfnumber(res, "refobjid");
3168
3169 for (i = 0; i < ntups; i++)
3170 {
3171 CatalogId objId;
3173 DumpableObject *dobj;
3177
3178 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3179 objId.oid = atooid(PQgetvalue(res, i, i_objid));
3180 refobjId.tableoid = objId.tableoid;
3181 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3182
3183 dobj = findObjectByCatalogId(objId);
3184 if (dobj == NULL)
3185 continue;
3186
3187 Assert(dobj->objType == DO_TABLE);
3188 tbinfo = (TableInfo *) dobj;
3189 Assert(tbinfo->relkind == RELKIND_MATVIEW);
3190 dobj = (DumpableObject *) tbinfo->dataObj;
3191 if (dobj == NULL)
3192 continue;
3194
3196 if (refdobj == NULL)
3197 continue;
3198
3199 Assert(refdobj->objType == DO_TABLE);
3201 Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3202 refdobj = (DumpableObject *) reftbinfo->dataObj;
3203 if (refdobj == NULL)
3204 continue;
3205 Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3206
3207 addObjectDependency(dobj, refdobj->dumpId);
3208
3209 if (!reftbinfo->relispopulated)
3210 tbinfo->relispopulated = false;
3211 }
3212
3213 PQclear(res);
3214
3215 destroyPQExpBuffer(query);
3216}

References addObjectDependency(), appendPQExpBufferStr(), Assert, atooid, CppAsString2, createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), DO_REFRESH_MATVIEW, DO_TABLE, _dumpableObject::dumpId, ExecuteSqlQuery(), fb(), findObjectByCatalogId(), fout, i, _dumpableObject::objType, CatalogId::oid, PGRES_TUPLES_OK, PQclear, PQfnumber(), PQgetvalue, PQntuples, Archive::remoteVersion, and CatalogId::tableoid.

Referenced by main().

◆ checkExtensionMembership()

static bool checkExtensionMembership ( DumpableObject dobj,
Archive fout 
)
static

Definition at line 1932 of file pg_dump.c.

1933{
1935
1936 if (ext == NULL)
1937 return false;
1938
1939 dobj->ext_member = true;
1940
1941 /* Record dependency so that getDependencies needn't deal with that */
1942 addObjectDependency(dobj, ext->dobj.dumpId);
1943
1944 /*
1945 * In 9.6 and above, mark the member object to have any non-initial ACLs
1946 * dumped. (Any initial ACLs will be removed later, using data from
1947 * pg_init_privs, so that we'll dump only the delta from the extension's
1948 * initial setup.)
1949 *
1950 * Prior to 9.6, we do not include any extension member components.
1951 *
1952 * In binary upgrades, we still dump all components of the members
1953 * individually, since the idea is to exactly reproduce the database
1954 * contents rather than replace the extension contents with something
1955 * different.
1956 *
1957 * Note: it might be interesting someday to implement storage and delta
1958 * dumping of extension members' RLS policies and/or security labels.
1959 * However there is a pitfall for RLS policies: trying to dump them
1960 * requires getting a lock on their tables, and the calling user might not
1961 * have privileges for that. We need no lock to examine a table's ACLs,
1962 * so the current feature doesn't have a problem of that sort.
1963 */
1964 if (fout->dopt->binary_upgrade)
1965 dobj->dump = ext->dobj.dump;
1966 else
1967 {
1968 if (fout->remoteVersion < 90600)
1969 dobj->dump = DUMP_COMPONENT_NONE;
1970 else
1971 dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1972 }
1973
1974 return true;
1975}

References addObjectDependency(), _dumpOptions::binary_upgrade, _dumpableObject::catId, _extensionInfo::dobj, Archive::dopt, _dumpableObject::dump, DUMP_COMPONENT_ACL, DUMP_COMPONENT_NONE, _dumpableObject::dump_contains, _dumpableObject::dumpId, _dumpableObject::ext_member, fb(), findOwningExtension(), fout, and Archive::remoteVersion.

Referenced by selectDumpableAccessMethod(), selectDumpableCast(), selectDumpableNamespace(), selectDumpableObject(), selectDumpableProcLang(), selectDumpablePublicationObject(), selectDumpableStatisticsObject(), selectDumpableTable(), and selectDumpableType().

◆ collectBinaryUpgradeClassOids()

static void collectBinaryUpgradeClassOids ( Archive fout)
static

Definition at line 5915 of file pg_dump.c.

5916{
5917 PGresult *res;
5918 const char *query;
5919
5920 query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5921 "ct.relfilenode, i.indexrelid, cti.relfilenode "
5922 "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5923 "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5924 "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5925 "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5926 "ORDER BY c.oid;";
5927
5928 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5929
5933
5934 for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5935 {
5943 }
5944
5945 PQclear(res);
5946}

References atooid, binaryUpgradeClassOids, ExecuteSqlQuery(), fout, i, nbinaryUpgradeClassOids, BinaryUpgradeClassOidItem::oid, pg_malloc_array, PGRES_TUPLES_OK, PQclear, PQgetvalue, PQntuples, BinaryUpgradeClassOidItem::relfilenumber, BinaryUpgradeClassOidItem::relkind, BinaryUpgradeClassOidItem::toast_index_oid, BinaryUpgradeClassOidItem::toast_index_relfilenumber, BinaryUpgradeClassOidItem::toast_oid, and BinaryUpgradeClassOidItem::toast_relfilenumber.

Referenced by main().

◆ collectComments()

static void collectComments ( Archive fout)
static

Definition at line 11734 of file pg_dump.c.

11735{
11736 PGresult *res;
11737 PQExpBuffer query;
11738 int i_description;
11739 int i_classoid;
11740 int i_objoid;
11741 int i_objsubid;
11742 int ntups;
11743 int i;
11744 DumpableObject *dobj;
11745
11746 query = createPQExpBuffer();
11747
11748 appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11749 "FROM pg_catalog.pg_description "
11750 "ORDER BY classoid, objoid, objsubid");
11751
11752 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11753
11754 /* Construct lookup table containing OIDs in numeric form */
11755
11756 i_description = PQfnumber(res, "description");
11757 i_classoid = PQfnumber(res, "classoid");
11758 i_objoid = PQfnumber(res, "objoid");
11759 i_objsubid = PQfnumber(res, "objsubid");
11760
11761 ntups = PQntuples(res);
11762
11764 ncomments = 0;
11765 dobj = NULL;
11766
11767 for (i = 0; i < ntups; i++)
11768 {
11769 CatalogId objId;
11770 int subid;
11771
11772 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11773 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11774 subid = atoi(PQgetvalue(res, i, i_objsubid));
11775
11776 /* We needn't remember comments that don't match any dumpable object */
11777 if (dobj == NULL ||
11778 dobj->catId.tableoid != objId.tableoid ||
11779 dobj->catId.oid != objId.oid)
11780 dobj = findObjectByCatalogId(objId);
11781 if (dobj == NULL)
11782 continue;
11783
11784 /*
11785 * Comments on columns of composite types are linked to the type's
11786 * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11787 * in the type's own DumpableObject.
11788 */
11789 if (subid != 0 && dobj->objType == DO_TABLE &&
11790 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11791 {
11793
11794 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11795 if (cTypeInfo)
11796 cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11797 }
11798 else
11799 dobj->components |= DUMP_COMPONENT_COMMENT;
11800
11803 comments[ncomments].objoid = objId.oid;
11804 comments[ncomments].objsubid = subid;
11805 ncomments++;
11806 }
11807
11808 PQclear(res);
11809 destroyPQExpBuffer(query);
11810}

References appendPQExpBufferStr(), atooid, _dumpableObject::catId, CommentItem::classoid, comments, createPQExpBuffer(), PQExpBufferData::data, CommentItem::descr, destroyPQExpBuffer(), DO_TABLE, DUMP_COMPONENT_COMMENT, ExecuteSqlQuery(), fb(), findObjectByCatalogId(), findTypeByOid(), fout, i, ncomments, CommentItem::objoid, CommentItem::objsubid, _dumpableObject::objType, CatalogId::oid, pg_malloc_array, pg_strdup(), PGRES_TUPLES_OK, PQclear, PQfnumber(), PQgetvalue, PQntuples, and CatalogId::tableoid.

Referenced by main().

◆ collectRoleNames()

static void collectRoleNames ( Archive fout)
static

Definition at line 10834 of file pg_dump.c.

10835{
10836 PGresult *res;
10837 const char *query;
10838 int i;
10839
10840 query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10841
10842 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10843
10844 nrolenames = PQntuples(res);
10845
10847
10848 for (i = 0; i < nrolenames; i++)
10849 {
10850 rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10852 }
10853
10854 PQclear(res);
10855}

References atooid, ExecuteSqlQuery(), fout, i, nrolenames, pg_malloc_array, pg_strdup(), PGRES_TUPLES_OK, PQclear, PQgetvalue, PQntuples, RoleNameItem::rolename, rolenames, and RoleNameItem::roleoid.

Referenced by main().

◆ collectSecLabels()

static void collectSecLabels ( Archive fout)
static

Definition at line 16939 of file pg_dump.c.

16940{
16941 PGresult *res;
16942 PQExpBuffer query;
16943 int i_label;
16944 int i_provider;
16945 int i_classoid;
16946 int i_objoid;
16947 int i_objsubid;
16948 int ntups;
16949 int i;
16950 DumpableObject *dobj;
16951
16952 query = createPQExpBuffer();
16953
16955 "SELECT label, provider, classoid, objoid, objsubid "
16956 "FROM pg_catalog.pg_seclabels "
16957 "ORDER BY classoid, objoid, objsubid");
16958
16959 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16960
16961 /* Construct lookup table containing OIDs in numeric form */
16962 i_label = PQfnumber(res, "label");
16963 i_provider = PQfnumber(res, "provider");
16964 i_classoid = PQfnumber(res, "classoid");
16965 i_objoid = PQfnumber(res, "objoid");
16966 i_objsubid = PQfnumber(res, "objsubid");
16967
16968 ntups = PQntuples(res);
16969
16971 nseclabels = 0;
16972 dobj = NULL;
16973
16974 for (i = 0; i < ntups; i++)
16975 {
16976 CatalogId objId;
16977 int subid;
16978
16979 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16980 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16981 subid = atoi(PQgetvalue(res, i, i_objsubid));
16982
16983 /* We needn't remember labels that don't match any dumpable object */
16984 if (dobj == NULL ||
16985 dobj->catId.tableoid != objId.tableoid ||
16986 dobj->catId.oid != objId.oid)
16987 dobj = findObjectByCatalogId(objId);
16988 if (dobj == NULL)
16989 continue;
16990
16991 /*
16992 * Labels on columns of composite types are linked to the type's
16993 * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16994 * in the type's own DumpableObject.
16995 */
16996 if (subid != 0 && dobj->objType == DO_TABLE &&
16997 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16998 {
17000
17001 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
17002 if (cTypeInfo)
17003 cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
17004 }
17005 else
17006 dobj->components |= DUMP_COMPONENT_SECLABEL;
17007
17011 seclabels[nseclabels].objoid = objId.oid;
17012 seclabels[nseclabels].objsubid = subid;
17013 nseclabels++;
17014 }
17015
17016 PQclear(res);
17017 destroyPQExpBuffer(query);
17018}

References appendPQExpBufferStr(), atooid, _dumpableObject::catId, SecLabelItem::classoid, createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), DO_TABLE, DUMP_COMPONENT_SECLABEL, ExecuteSqlQuery(), fb(), findObjectByCatalogId(), findTypeByOid(), fout, i, SecLabelItem::label, nseclabels, SecLabelItem::objoid, SecLabelItem::objsubid, _dumpableObject::objType, CatalogId::oid, pg_malloc_array, pg_strdup(), PGRES_TUPLES_OK, PQclear, PQfnumber(), PQgetvalue, PQntuples, SecLabelItem::provider, seclabels, and CatalogId::tableoid.

Referenced by main().

◆ collectSequences()

static void collectSequences ( Archive fout)
static

Definition at line 19407 of file pg_dump.c.

19408{
19409 PGresult *res;
19410 const char *query;
19411
19412 /*
19413 * Before Postgres 10, sequence metadata is in the sequence itself. With
19414 * some extra effort, we might be able to use the sorted table for those
19415 * versions, but for now it seems unlikely to be worth it.
19416 *
19417 * Since version 18, we can gather the sequence data in this query with
19418 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
19419 */
19420 if (fout->remoteVersion < 100000)
19421 return;
19422 else if (fout->remoteVersion < 180000 ||
19424 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19425 "seqstart, seqincrement, "
19426 "seqmax, seqmin, "
19427 "seqcache, seqcycle, "
19428 "NULL, 'f' "
19429 "FROM pg_catalog.pg_sequence "
19430 "ORDER BY seqrelid";
19431 else
19432 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19433 "seqstart, seqincrement, "
19434 "seqmax, seqmin, "
19435 "seqcache, seqcycle, "
19436 "last_value, is_called "
19437 "FROM pg_catalog.pg_sequence, "
19438 "pg_get_sequence_data(seqrelid) "
19439 "ORDER BY seqrelid;";
19440
19441 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
19442
19443 nsequences = PQntuples(res);
19445
19446 for (int i = 0; i < nsequences; i++)
19447 {
19448 sequences[i].oid = atooid(PQgetvalue(res, i, 0));
19450 sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
19451 sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
19452 sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
19453 sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
19454 sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
19455 sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
19456 sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
19457 sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
19458 sequences[i].null_seqtuple = (PQgetisnull(res, i, 8) || PQgetisnull(res, i, 9));
19459 }
19460
19461 PQclear(res);
19462}

References atooid, SequenceItem::cache, SequenceItem::cycled, Archive::dopt, _dumpOptions::dumpData, ExecuteSqlQuery(), fb(), fout, i, SequenceItem::incby, SequenceItem::is_called, SequenceItem::last_value, SequenceItem::maxv, SequenceItem::minv, nsequences, SequenceItem::null_seqtuple, SequenceItem::oid, parse_sequence_type(), pg_malloc_array, PGRES_TUPLES_OK, PQclear, PQgetisnull, PQgetvalue, PQntuples, Archive::remoteVersion, SequenceItem::seqtype, _dumpOptions::sequence_data, sequences, and SequenceItem::startv.

Referenced by main().

◆ convertRegProcReference()

static char * convertRegProcReference ( const char proc)
static

Definition at line 14483 of file pg_dump.c.

14484{
14485 char *name;
14486 char *paren;
14487 bool inquote;
14488
14489 /* In all cases "-" means a null reference */
14490 if (strcmp(proc, "-") == 0)
14491 return NULL;
14492
14493 name = pg_strdup(proc);
14494 /* find non-double-quoted left paren */
14495 inquote = false;
14496 for (paren = name; *paren; paren++)
14497 {
14498 if (*paren == '(' && !inquote)
14499 {
14500 *paren = '\0';
14501 break;
14502 }
14503 if (*paren == '"')
14504 inquote = !inquote;
14505 }
14506 return name;
14507}

References fb(), name, and pg_strdup().

Referenced by dumpOpr().

◆ convertTSFunction()

static char * convertTSFunction ( Archive fout,
Oid  funcOid 
)
static

Definition at line 14554 of file pg_dump.c.

14555{
14556 char *result;
14557 char query[128];
14558 PGresult *res;
14559
14560 snprintf(query, sizeof(query),
14561 "SELECT '%u'::pg_catalog.regproc", funcOid);
14562 res = ExecuteSqlQueryForSingleRow(fout, query);
14563
14564 result = pg_strdup(PQgetvalue(res, 0, 0));
14565
14566 PQclear(res);
14567
14568 return result;
14569}

References ExecuteSqlQueryForSingleRow(), fout, pg_strdup(), PQclear, PQgetvalue, result, and snprintf.

Referenced by dumpTSParser(), and dumpTSTemplate().

◆ createBoundaryObjects()

static DumpableObject * createBoundaryObjects ( void  )
static

Definition at line 20633 of file pg_dump.c.

20634{
20636
20638
20639 dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20640 dobjs[0].catId = nilCatalogId;
20641 AssignDumpId(dobjs + 0);
20642 dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20643
20644 dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20645 dobjs[1].catId = nilCatalogId;
20646 AssignDumpId(dobjs + 1);
20647 dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20648
20649 return dobjs;
20650}

References AssignDumpId(), DO_POST_DATA_BOUNDARY, DO_PRE_DATA_BOUNDARY, fb(), nilCatalogId, pg_malloc_array, and pg_strdup().

Referenced by main().

◆ createDummyViewAsClause()

static PQExpBuffer createDummyViewAsClause ( Archive fout,
const TableInfo tbinfo 
)
static

Definition at line 17226 of file pg_dump.c.

17227{
17229 int j;
17230
17231 appendPQExpBufferStr(result, "SELECT");
17232
17233 for (j = 0; j < tbinfo->numatts; j++)
17234 {
17235 if (j > 0)
17238
17239 appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17240
17241 /*
17242 * Must add collation if not default for the type, because CREATE OR
17243 * REPLACE VIEW won't change it
17244 */
17245 if (OidIsValid(tbinfo->attcollation[j]))
17246 {
17247 CollInfo *coll;
17248
17249 coll = findCollationByOid(tbinfo->attcollation[j]);
17250 if (coll)
17251 appendPQExpBuffer(result, " COLLATE %s",
17252 fmtQualifiedDumpable(coll));
17253 }
17254
17255 appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17256 }
17257
17258 return result;
17259}

References appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), createPQExpBuffer(), fb(), findCollationByOid(), fmtId(), fmtQualifiedDumpable, j, OidIsValid, and result.

Referenced by dumpRule(), and dumpTableSchema().

◆ createViewAsClause()

static PQExpBuffer createViewAsClause ( Archive fout,
const TableInfo tbinfo 
)
static

Definition at line 17177 of file pg_dump.c.

17178{
17181 PGresult *res;
17182 int len;
17183
17184 /* Fetch the view definition */
17185 appendPQExpBuffer(query,
17186 "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
17187 tbinfo->dobj.catId.oid);
17188
17189 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17190
17191 if (PQntuples(res) != 1)
17192 {
17193 if (PQntuples(res) < 1)
17194 pg_fatal("query to obtain definition of view \"%s\" returned no data",
17195 tbinfo->dobj.name);
17196 else
17197 pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
17198 tbinfo->dobj.name);
17199 }
17200
17201 len = PQgetlength(res, 0, 0);
17202
17203 if (len == 0)
17204 pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17205 tbinfo->dobj.name);
17206
17207 /* Strip off the trailing semicolon so that other things may follow. */
17208 Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17209 appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17210
17211 PQclear(res);
17212 destroyPQExpBuffer(query);
17213
17214 return result;
17215}

References appendBinaryPQExpBuffer(), appendPQExpBuffer(), Assert, createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), ExecuteSqlQuery(), fb(), fout, len, pg_fatal, PGRES_TUPLES_OK, PQclear, PQgetlength, PQgetvalue, PQntuples, and result.

Referenced by dumpRule(), and dumpTableSchema().

◆ determineNotNullFlags()

static void determineNotNullFlags ( Archive fout,
PGresult res,
int  r,
TableInfo tbinfo,
int  j,
int  i_notnull_name,
int  i_notnull_comment,
int  i_notnull_invalidoid,
int  i_notnull_noinherit,
int  i_notnull_islocal,
PQExpBuffer invalidnotnulloids 
)
static

Definition at line 10119 of file pg_dump.c.

10127{
10128 DumpOptions *dopt = fout->dopt;
10129
10130 /*
10131 * If this not-null constraint is not valid, list its OID in
10132 * invalidnotnulloids and do nothing further. It'll be processed
10133 * elsewhere later.
10134 *
10135 * Because invalid not-null constraints are rare, we don't want to malloc
10136 * invalidnotnulloids until we're sure we're going it need it, which
10137 * happens here.
10138 */
10139 if (!PQgetisnull(res, r, i_notnull_invalidoid))
10140 {
10141 char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10142
10143 if (*invalidnotnulloids == NULL)
10144 {
10148 }
10149 else
10151
10152 /*
10153 * Track when a parent constraint is invalid for the cases where a
10154 * child constraint has been validated independenly.
10155 */
10156 tbinfo->notnull_invalid[j] = true;
10157
10158 /* nothing else to do */
10159 tbinfo->notnull_constrs[j] = NULL;
10160 return;
10161 }
10162
10163 /*
10164 * notnull_noinh is straight from the query result. notnull_islocal also,
10165 * though flagInhAttrs may change that one later.
10166 */
10167 tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10168 tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10169 tbinfo->notnull_invalid[j] = false;
10170
10171 /*
10172 * Determine a constraint name to use. If the column is not marked not-
10173 * null, we set NULL which cues ... to do nothing. An empty string says
10174 * to print an unnamed NOT NULL, and anything else is a constraint name to
10175 * use.
10176 */
10177 if (fout->remoteVersion < 180000)
10178 {
10179 /*
10180 * < 18 doesn't have not-null names, so an unnamed constraint is
10181 * sufficient.
10182 */
10183 if (PQgetisnull(res, r, i_notnull_name))
10184 tbinfo->notnull_constrs[j] = NULL;
10185 else
10186 tbinfo->notnull_constrs[j] = "";
10187 }
10188 else
10189 {
10190 if (PQgetisnull(res, r, i_notnull_name))
10191 tbinfo->notnull_constrs[j] = NULL;
10192 else
10193 {
10194 /*
10195 * In binary upgrade of inheritance child tables, must have a
10196 * constraint name that we can UPDATE later; same if there's a
10197 * comment on the constraint.
10198 */
10199 if ((dopt->binary_upgrade &&
10200 !tbinfo->ispartition &&
10201 !tbinfo->notnull_islocal[j]) ||
10203 {
10204 tbinfo->notnull_constrs[j] =
10206 }
10207 else
10208 {
10209 char *default_name;
10210
10211 /* XXX should match ChooseConstraintName better */
10212 default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10213 tbinfo->attnames[j]);
10214 if (strcmp(default_name,
10215 PQgetvalue(res, r, i_notnull_name)) == 0)
10216 tbinfo->notnull_constrs[j] = "";
10217 else
10218 {
10219 tbinfo->notnull_constrs[j] =
10221 }
10223 }
10224 }
10225 }
10226}

References appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), _dumpOptions::binary_upgrade, createPQExpBuffer(), Archive::dopt, fb(), fout, free, j, PQgetisnull, PQgetvalue, psprintf(), pstrdup(), and Archive::remoteVersion.

Referenced by getTableAttrs().

◆ dumpAccessMethod()

static void dumpAccessMethod ( Archive fout,
const AccessMethodInfo aminfo 
)
static

Definition at line 14576 of file pg_dump.c.

14577{
14578 DumpOptions *dopt = fout->dopt;
14579 PQExpBuffer q;
14581 char *qamname;
14582
14583 /* Do nothing if not dumping schema */
14584 if (!dopt->dumpSchema)
14585 return;
14586
14587 q = createPQExpBuffer();
14589
14590 qamname = pg_strdup(fmtId(aminfo->dobj.name));
14591
14592 appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14593
14594 switch (aminfo->amtype)
14595 {
14596 case AMTYPE_INDEX:
14597 appendPQExpBufferStr(q, "TYPE INDEX ");
14598 break;
14599 case AMTYPE_TABLE:
14600 appendPQExpBufferStr(q, "TYPE TABLE ");
14601 break;
14602 default:
14603 pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14604 aminfo->amtype, qamname);
14607 free(qamname);
14608 return;
14609 }
14610
14611 appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14612
14613 appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14614 qamname);
14615
14616 if (dopt->binary_upgrade)
14618 "ACCESS METHOD", qamname, NULL);
14619
14620 if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14621 ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14622 ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14623 .description = "ACCESS METHOD",
14624 .section = SECTION_PRE_DATA,
14625 .createStmt = q->data,
14626 .dropStmt = delq->data));
14627
14628 /* Dump Access Method Comments */
14629 if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14630 dumpComment(fout, "ACCESS METHOD", qamname,
14631 NULL, "",
14632 aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14633
14636 free(qamname);
14637}

References appendPQExpBuffer(), appendPQExpBufferStr(), ARCHIVE_OPTS, ArchiveEntry(), _dumpOptions::binary_upgrade, binary_upgrade_extension_member(), createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), Archive::dopt, DUMP_COMPONENT_COMMENT, DUMP_COMPONENT_DEFINITION, dumpComment(), _dumpOptions::dumpSchema, fb(), fmtId(), fout, free, pg_log_warning, pg_strdup(), and SECTION_PRE_DATA.

Referenced by dumpDumpableObject().

◆ dumpACL()

static DumpId dumpACL ( Archive fout,
DumpId  objDumpId,
DumpId  altDumpId,
const char type,
const char name,
const char subname,
const char nspname,
const char tag,
const char owner,
const DumpableAcl dacl 
)
static

Definition at line 16570 of file pg_dump.c.

16574{
16576 DumpOptions *dopt = fout->dopt;
16577 const char *acls = dacl->acl;
16578 const char *acldefault = dacl->acldefault;
16579 char privtype = dacl->privtype;
16580 const char *initprivs = dacl->initprivs;
16581 const char *baseacls;
16582 PQExpBuffer sql;
16583
16584 /* Do nothing if ACL dump is not enabled */
16585 if (dopt->aclsSkip)
16586 return InvalidDumpId;
16587
16588 /* --data-only skips ACLs *except* large object ACLs */
16589 if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16590 return InvalidDumpId;
16591
16592 sql = createPQExpBuffer();
16593
16594 /*
16595 * In binary upgrade mode, we don't run an extension's script but instead
16596 * dump out the objects independently and then recreate them. To preserve
16597 * any initial privileges which were set on extension objects, we need to
16598 * compute the set of GRANT and REVOKE commands necessary to get from the
16599 * default privileges of an object to its initial privileges as recorded
16600 * in pg_init_privs.
16601 *
16602 * At restore time, we apply these commands after having called
16603 * binary_upgrade_set_record_init_privs(true). That tells the backend to
16604 * copy the results into pg_init_privs. This is how we preserve the
16605 * contents of that catalog across binary upgrades.
16606 */
16607 if (dopt->binary_upgrade && privtype == 'e' &&
16608 initprivs && *initprivs != '\0')
16609 {
16610 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16611 if (!buildACLCommands(name, subname, nspname, type,
16612 initprivs, acldefault, owner,
16613 "", fout->remoteVersion, sql))
16614 pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16615 initprivs, acldefault, name, type);
16616 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16617 }
16618
16619 /*
16620 * Now figure the GRANT and REVOKE commands needed to get to the object's
16621 * actual current ACL, starting from the initprivs if given, else from the
16622 * object-type-specific default. Also, while buildACLCommands will assume
16623 * that a NULL/empty acls string means it needn't do anything, what that
16624 * actually represents is the object-type-specific default; so we need to
16625 * substitute the acldefault string to get the right results in that case.
16626 */
16627 if (initprivs && *initprivs != '\0')
16628 {
16629 baseacls = initprivs;
16630 if (acls == NULL || *acls == '\0')
16631 acls = acldefault;
16632 }
16633 else
16635
16636 if (!buildACLCommands(name, subname, nspname, type,
16637 acls, baseacls, owner,
16638 "", fout->remoteVersion, sql))
16639 pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16640 acls, baseacls, name, type);
16641
16642 if (sql->len > 0)
16643 {
16645 DumpId aclDeps[2];
16646 int nDeps = 0;
16647
16648 if (tag)
16650 else if (subname)
16651 appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16652 else
16653 appendPQExpBuffer(tagbuf, "%s %s", type, name);
16654
16655 aclDeps[nDeps++] = objDumpId;
16656 if (altDumpId != InvalidDumpId)
16657 aclDeps[nDeps++] = altDumpId;
16658
16660
16662 ARCHIVE_OPTS(.tag = tagbuf->data,
16663 .namespace = nspname,
16664 .owner = owner,
16665 .description = "ACL",
16666 .section = SECTION_NONE,
16667 .createStmt = sql->data,
16668 .deps = aclDeps,
16669 .nDeps = nDeps));
16670
16672 }
16673
16674 destroyPQExpBuffer(sql);
16675
16676 return aclDumpId;
16677}

References _dumpableAcl::acl, acldefault(), _dumpableAcl::acldefault, _dumpOptions::aclsSkip, appendPQExpBuffer(), appendPQExpBufferStr(), ARCHIVE_OPTS, ArchiveEntry(), _dumpOptions::binary_upgrade, buildACLCommands(), createDumpId(), createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), Archive::dopt, _dumpOptions::dumpSchema, fb(), fout, _dumpableAcl::initprivs, InvalidDumpId, PQExpBufferData::len, name, nilCatalogId, pg_fatal, _dumpableAcl::privtype, Archive::remoteVersion, SECTION_NONE, subname, and type.

Referenced by dumpAgg(), dumpBaseType(), dumpCompositeType(), dumpDatabase(), dumpDomain(), dumpEnumType(), dumpForeignDataWrapper(), dumpForeignServer(), dumpFunc(), dumpLO(), dumpNamespace(), dumpProcLang(), dumpRangeType(), dumpTable(), and dumpUndefinedType().

◆ dumpAgg()

static void dumpAgg ( Archive fout,
const AggInfo agginfo 
)
static

Definition at line 15529 of file pg_dump.c.

15530{
15531 DumpOptions *dopt = fout->dopt;
15532 PQExpBuffer query;
15533 PQExpBuffer q;
15535 PQExpBuffer details;
15536 char *aggsig; /* identity signature */
15537 char *aggfullsig = NULL; /* full signature */
15538 char *aggsig_tag;
15539 PGresult *res;
15540 int i_agginitval;
15541 int i_aggminitval;
15542 const char *aggtransfn;
15543 const char *aggfinalfn;
15544 const char *aggcombinefn;
15545 const char *aggserialfn;
15546 const char *aggdeserialfn;
15547 const char *aggmtransfn;
15548 const char *aggminvtransfn;
15549 const char *aggmfinalfn;
15550 bool aggfinalextra;
15551 bool aggmfinalextra;
15552 char aggfinalmodify;
15553 char aggmfinalmodify;
15554 const char *aggsortop;
15555 char *aggsortconvop;
15556 char aggkind;
15557 const char *aggtranstype;
15558 const char *aggtransspace;
15559 const char *aggmtranstype;
15560 const char *aggmtransspace;
15561 const char *agginitval;
15562 const char *aggminitval;
15563 const char *proparallel;
15564 char defaultfinalmodify;
15565
15566 /* Do nothing if not dumping schema */
15567 if (!dopt->dumpSchema)
15568 return;
15569
15570 query = createPQExpBuffer();
15571 q = createPQExpBuffer();
15573 details = createPQExpBuffer();
15574
15576 {
15577 /* Set up query for aggregate-specific details */
15579 "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15580
15582 "SELECT "
15583 "aggtransfn,\n"
15584 "aggfinalfn,\n"
15585 "aggtranstype::pg_catalog.regtype,\n"
15586 "agginitval,\n"
15587 "aggsortop,\n"
15588 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15589 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15590
15591 if (fout->remoteVersion >= 90400)
15593 "aggkind,\n"
15594 "aggmtransfn,\n"
15595 "aggminvtransfn,\n"
15596 "aggmfinalfn,\n"
15597 "aggmtranstype::pg_catalog.regtype,\n"
15598 "aggfinalextra,\n"
15599 "aggmfinalextra,\n"
15600 "aggtransspace,\n"
15601 "aggmtransspace,\n"
15602 "aggminitval,\n");
15603 else
15605 "'n' AS aggkind,\n"
15606 "'-' AS aggmtransfn,\n"
15607 "'-' AS aggminvtransfn,\n"
15608 "'-' AS aggmfinalfn,\n"
15609 "0 AS aggmtranstype,\n"
15610 "false AS aggfinalextra,\n"
15611 "false AS aggmfinalextra,\n"
15612 "0 AS aggtransspace,\n"
15613 "0 AS aggmtransspace,\n"
15614 "NULL AS aggminitval,\n");
15615
15616 if (fout->remoteVersion >= 90600)
15618 "aggcombinefn,\n"
15619 "aggserialfn,\n"
15620 "aggdeserialfn,\n"
15621 "proparallel,\n");
15622 else
15624 "'-' AS aggcombinefn,\n"
15625 "'-' AS aggserialfn,\n"
15626 "'-' AS aggdeserialfn,\n"
15627 "'u' AS proparallel,\n");
15628
15629 if (fout->remoteVersion >= 110000)
15631 "aggfinalmodify,\n"
15632 "aggmfinalmodify\n");
15633 else
15635 "'0' AS aggfinalmodify,\n"
15636 "'0' AS aggmfinalmodify\n");
15637
15639 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15640 "WHERE a.aggfnoid = p.oid "
15641 "AND p.oid = $1");
15642
15643 ExecuteSqlStatement(fout, query->data);
15644
15646 }
15647
15648 printfPQExpBuffer(query,
15649 "EXECUTE dumpAgg('%u')",
15650 agginfo->aggfn.dobj.catId.oid);
15651
15652 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15653
15654 i_agginitval = PQfnumber(res, "agginitval");
15655 i_aggminitval = PQfnumber(res, "aggminitval");
15656
15657 aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15658 aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15659 aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15660 aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15661 aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15662 aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15663 aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15664 aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15665 aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15666 aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15667 aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15668 aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15669 aggsortop =