* pg_dump is a utility for dumping out a postgres database
* into a script file.
*
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* pg_dump will read the system catalogs in a database and dump out a
* by PostgreSQL
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.349 2003/09/23 22:48:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.447 2006/08/21 00:57:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <unistd.h>
+
#include <ctype.h>
#ifdef ENABLE_NLS
#include <locale.h>
#include "getopt_long.h"
-#ifndef HAVE_OPTRESET
+#ifndef HAVE_INT_OPTRESET
int optreset;
#endif
-#include "access/attnum.h"
+
+
#include "access/htup.h"
#include "catalog/pg_class.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
-
#include "commands/sequence.h"
-
-#include "libpq-fe.h"
#include "libpq/libpq-fs.h"
+#include "mb/pg_wchar.h"
-#include "pg_dump.h"
-#include "pg_backup.h"
#include "pg_backup_archiver.h"
#include "dumputils.h"
-#define _(x) gettext((x))
-
-typedef struct _dumpContext
-{
- TableInfo *tblinfo;
- int tblidx;
- bool oids;
-} DumpContext;
-
-static void help(const char *progname);
-static NamespaceInfo *findNamespace(const char *nsoid, const char *objoid);
-static void dumpClasses(const TableInfo *tblinfo, const int numTables,
- Archive *fout, const bool oids);
-static void dumpComment(Archive *fout, const char *target,
- const char *namespace, const char *owner,
- const char *oid, const char *classname, int subid,
- const char *((*deps)[]));
-static void dumpOneBaseType(Archive *fout, TypeInfo *tinfo,
- FuncInfo *g_finfo, int numFuncs,
- TypeInfo *g_tinfo, int numTypes);
-static void dumpOneDomain(Archive *fout, TypeInfo *tinfo);
-static void dumpOneCompositeType(Archive *fout, TypeInfo *tinfo);
-static void dumpOneTable(Archive *fout, TableInfo *tbinfo,
- TableInfo *g_tblinfo);
-static void dumpOneSequence(Archive *fout, TableInfo *tbinfo,
- const bool schemaOnly, const bool dataOnly);
-
-static void dumpTableACL(Archive *fout, TableInfo *tbinfo);
-static void dumpFuncACL(Archive *fout, FuncInfo *finfo);
-static void dumpAggACL(Archive *fout, AggInfo *finfo);
-static void dumpACL(Archive *fout, const char *type, const char *name,
- const char *tag, const char *nspname,
- const char *owner, const char *acl, const char *objoid);
-
-static void dumpConstraints(Archive *fout, TableInfo *tblinfo, int numTables);
-static void dumpTriggers(Archive *fout, TableInfo *tblinfo, int numTables);
-static void dumpRules(Archive *fout, TableInfo *tblinfo, int numTables);
-static char *format_function_signature(FuncInfo *finfo, bool honor_quotes);
-static void dumpOneFunc(Archive *fout, FuncInfo *finfo);
-static void dumpOneOpr(Archive *fout, OprInfo *oprinfo,
- OprInfo *g_oprinfo, int numOperators);
-static const char *convertRegProcReference(const char *proc);
-static const char *convertOperatorReference(const char *opr,
- OprInfo *g_oprinfo, int numOperators);
-static void dumpOneOpclass(Archive *fout, OpclassInfo *opcinfo);
-static void dumpOneAgg(Archive *fout, AggInfo *agginfo);
-static Oid findLastBuiltinOid_V71(const char *);
-static Oid findLastBuiltinOid_V70(void);
-static void setMaxOid(Archive *fout);
-static void selectSourceSchema(const char *schemaName);
-static char *getFormattedTypeName(const char *oid, OidOptions opts);
-static char *myFormatType(const char *typname, int32 typmod);
-static const char *fmtQualifiedId(const char *schema, const char *id);
-static int dumpBlobs(Archive *AH, char *, void *);
-static int dumpDatabase(Archive *AH);
-static const char *getAttrName(int attrnum, TableInfo *tblInfo);
-static const char *fmtCopyColumnList(const TableInfo *ti);
-
extern char *optarg;
extern int optind,
opterr;
+
+typedef struct
+{
+ const char *descr; /* comment for an object */
+ Oid classoid; /* object class (catalog OID) */
+ Oid objoid; /* object OID */
+ int objsubid; /* subobject (table column #) */
+} CommentItem;
+
+
/* global decls */
bool g_verbose; /* User wants verbose narration of our
* activities. */
PGconn *g_conn; /* the database connection */
/* various user-settable parameters */
-bool dumpData; /* dump data using proper insert strings */
+bool dumpInserts; /* dump data using proper insert strings */
bool attrNames; /* put attr names into insert strings */
bool schemaOnly;
bool dataOnly;
bool aclsSkip;
+/* subquery used to convert user ID (eg, datdba) to user name */
+static const char *username_subquery;
+
/* obsolete as of 7.3: */
static Oid g_last_builtin_oid; /* value of the last builtin oid */
-static char *selectTableName = NULL; /* name of a single table to dump */
-static char *selectSchemaName = NULL; /* name of a single schema to dump */
+/* select and exclude tables and schemas */
+typedef struct objnameArg
+{
+ struct objnameArg *next;
+ char *name; /* name of the relation */
+ bool is_include; /* include/exclude? */
+} objnameArg;
+
+objnameArg *schemaList = NULL; /* List of schemas to include/exclude */
+objnameArg *tableList = NULL; /* List of tables to include/exclude */
+
+char *matchingSchemas = NULL; /* Final list of schemas to dump by oid */
+char *matchingTables = NULL; /* Final list of tables to dump by oid */
char g_opaque_type[10]; /* name for the opaque type */
char g_comment_start[10];
char g_comment_end[10];
+static const CatalogId nilCatalogId = {0, 0};
+
/* these are to avoid passing around info for findNamespace() */
static NamespaceInfo *g_namespaces;
static int g_numNamespaces;
+/* flag to turn on/off dollar quoting */
+static int disable_dollar_quoting = 0;
+
+
+static void help(const char *progname);
+static NamespaceInfo *findNamespace(Oid nsoid, Oid objoid);
+static void dumpTableData(Archive *fout, TableDataInfo *tdinfo);
+static void dumpComment(Archive *fout, const char *target,
+ const char *namespace, const char *owner,
+ CatalogId catalogId, int subid, DumpId dumpId);
+static int findComments(Archive *fout, Oid classoid, Oid objoid,
+ CommentItem **items);
+static int collectComments(Archive *fout, CommentItem **items);
+static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
+static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
+static void dumpType(Archive *fout, TypeInfo *tinfo);
+static void dumpBaseType(Archive *fout, TypeInfo *tinfo);
+static void dumpDomain(Archive *fout, TypeInfo *tinfo);
+static void dumpCompositeType(Archive *fout, TypeInfo *tinfo);
+static void dumpShellType(Archive *fout, ShellTypeInfo *stinfo);
+static void dumpProcLang(Archive *fout, ProcLangInfo *plang);
+static void dumpFunc(Archive *fout, FuncInfo *finfo);
+static void dumpCast(Archive *fout, CastInfo *cast);
+static void dumpOpr(Archive *fout, OprInfo *oprinfo);
+static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
+static void dumpConversion(Archive *fout, ConvInfo *convinfo);
+static void dumpRule(Archive *fout, RuleInfo *rinfo);
+static void dumpAgg(Archive *fout, AggInfo *agginfo);
+static void dumpTrigger(Archive *fout, TriggerInfo *tginfo);
+static void dumpTable(Archive *fout, TableInfo *tbinfo);
+static void dumpTableSchema(Archive *fout, TableInfo *tbinfo);
+static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo);
+static void dumpSequence(Archive *fout, TableInfo *tbinfo);
+static void dumpIndex(Archive *fout, IndxInfo *indxinfo);
+static void dumpConstraint(Archive *fout, ConstraintInfo *coninfo);
+static void dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo);
+
+static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
+ const char *type, const char *name,
+ const char *tag, const char *nspname, const char *owner,
+ const char *acls);
+
+static void getDependencies(void);
+static void getDomainConstraints(TypeInfo *tinfo);
+static void getTableData(TableInfo *tblinfo, int numTables, bool oids);
+static char *format_function_arguments(FuncInfo *finfo, int nallargs,
+ char **allargtypes,
+ char **argmodes,
+ char **argnames);
+static char *format_function_signature(FuncInfo *finfo, bool honor_quotes);
+static const char *convertRegProcReference(const char *proc);
+static const char *convertOperatorReference(const char *opr);
+static Oid findLastBuiltinOid_V71(const char *);
+static Oid findLastBuiltinOid_V70(void);
+static void selectSourceSchema(const char *schemaName);
+static char *getFormattedTypeName(Oid oid, OidOptions opts);
+static char *myFormatType(const char *typname, int32 typmod);
+static const char *fmtQualifiedId(const char *schema, const char *id);
+static bool hasBlobs(Archive *AH);
+static int dumpBlobs(Archive *AH, void *arg);
+static int dumpBlobComments(Archive *AH, void *arg);
+static void dumpDatabase(Archive *AH);
+static void dumpEncoding(Archive *AH);
+static void dumpStdStrings(Archive *AH);
+static const char *getAttrName(int attrnum, TableInfo *tblInfo);
+static const char *fmtCopyColumnList(const TableInfo *ti);
+static void do_sql_command(PGconn *conn, const char *query);
+static void check_sql_result(PGresult *res, PGconn *conn, const char *query,
+ ExecStatusType expected);
+
int
main(int argc, char **argv)
{
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+ objnameArg *this_obj_name, *schemaList_tail = NULL, *tableList_tail = NULL;
int c;
const char *filename = NULL;
const char *format = "p";
const char *pghost = NULL;
const char *pgport = NULL;
const char *username = NULL;
+ const char *dumpencoding = NULL;
+ const char *std_strings;
bool oids = false;
- PGresult *res;
TableInfo *tblinfo;
int numTables;
+ DumpableObject **dobjs;
+ int numObjs;
+ int i;
+ bool switch_include_exclude;
bool force_password = false;
int compressLevel = -1;
bool ignore_version = false;
int plainText = 0;
int outputClean = 0;
int outputCreate = 0;
- int outputBlobs = 0;
+ bool outputBlobs = true;
int outputNoOwner = 0;
static int use_setsessauth = 0;
static int disable_triggers = 0;
{"no-owner", no_argument, NULL, 'O'},
{"port", required_argument, NULL, 'p'},
{"schema", required_argument, NULL, 'n'},
+ {"exclude-schema", required_argument, NULL, 'N'},
{"schema-only", no_argument, NULL, 's'},
{"superuser", required_argument, NULL, 'S'},
{"table", required_argument, NULL, 't'},
+ {"exclude-table", required_argument, NULL, 'T'},
{"password", no_argument, NULL, 'W'},
{"username", required_argument, NULL, 'U'},
{"verbose", no_argument, NULL, 'v'},
{"no-privileges", no_argument, NULL, 'x'},
{"no-acl", no_argument, NULL, 'x'},
{"compress", required_argument, NULL, 'Z'},
+ {"encoding", required_argument, NULL, 'E'},
{"help", no_argument, NULL, '?'},
{"version", no_argument, NULL, 'V'},
/*
- * the following options don't have an equivalent short option
- * letter, but are available as '-X long-name'
+ * the following options don't have an equivalent short option letter,
+ * but are available as '-X long-name'
*/
- {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
+ {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1},
{"disable-triggers", no_argument, &disable_triggers, 1},
+ {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
{NULL, 0, NULL, 0}
};
int optindex;
-#ifdef ENABLE_NLS
- setlocale(LC_ALL, "");
- bindtextdomain("pg_dump", LOCALEDIR);
- textdomain("pg_dump");
-#endif
+ set_pglocale_pgservice(argv[0], "pg_dump");
g_verbose = false;
g_comment_end[0] = '\0';
strcpy(g_opaque_type, "opaque");
- dataOnly = schemaOnly = dumpData = attrNames = false;
+ dataOnly = schemaOnly = dumpInserts = attrNames = false;
progname = get_progname(argv[0]);
/* Set default options based on progname */
if (strcmp(progname, "pg_backup") == 0)
- {
format = "c";
- outputBlobs = true;
- }
if (argc > 1)
{
}
}
- while ((c = getopt_long(argc, argv, "abcCdDf:F:h:in:oOp:RsS:t:uU:vWxX:Z:",
+ while ((c = getopt_long(argc, argv, "abcCdDE:f:F:h:in:N:oOp:RsS:t:T:uU:vWxX:Z:",
long_options, &optindex)) != -1)
{
switch (c)
break;
case 'b': /* Dump blobs */
- outputBlobs = true;
+ /* this is now default, so just ignore the switch */
break;
- case 'c': /* clean (i.e., drop) schema prior to
- * create */
+ case 'c': /* clean (i.e., drop) schema prior to create */
outputClean = 1;
break;
break;
case 'd': /* dump data as proper insert strings */
- dumpData = true;
+ dumpInserts = true;
break;
case 'D': /* dump data as proper insert strings with
* attr names */
- dumpData = true;
+ dumpInserts = true;
attrNames = true;
break;
+ case 'E': /* Dump encoding */
+ dumpencoding = optarg;
+ break;
+
case 'f':
filename = optarg;
break;
ignore_version = true;
break;
- case 'n': /* Dump data for this schema only */
- selectSchemaName = strdup(optarg);
+ case 'n': /* Include schemas */
+ case 'N': /* Exclude schemas */
+ case 't': /* Include tables */
+ case 'T': /* Exclude tables */
+
+ if (strlen(optarg) < 1)
+ {
+ fprintf(stderr, _("%s: invalid -%c option\n"), progname, c);
+ exit(1);
+ }
+
+ {
+ /* Create a struct for this name */
+ objnameArg *new_obj_name = (objnameArg *)
+ malloc(sizeof(objnameArg));
+
+ new_obj_name->next = NULL;
+ new_obj_name->name = strdup(optarg);
+ new_obj_name->is_include = islower(c) ? true : false;
+
+ /* add new entry to the proper list */
+ if (tolower(c) == 'n')
+ {
+ if (!schemaList_tail)
+ schemaList_tail = schemaList = new_obj_name;
+ else
+ schemaList_tail = schemaList_tail->next = new_obj_name;
+ }
+ else
+ {
+ if (!tableList_tail)
+ tableList_tail = tableList = new_obj_name;
+ else
+ tableList_tail = tableList_tail->next = new_obj_name;
+ }
+ }
break;
case 'o': /* Dump oids */
case 's': /* dump schema only */
schemaOnly = true;
+ outputBlobs = false;
break;
- case 'S': /* Username for superuser in plain text
- * output */
+ case 'S': /* Username for superuser in plain text output */
outputSuperuser = strdup(optarg);
break;
- case 't': /* Dump data for this table only */
- selectTableName = strdup(optarg);
- break;
-
case 'u':
force_password = true;
username = simple_prompt("User name: ", 100, true);
break;
/*
- * Option letters were getting scarce, so I invented this
- * new scheme: '-X feature' turns on some feature. Compare
- * to the -f option in GCC. You should also add an
- * equivalent GNU-style option --feature. Features that
- * require arguments should use '-X feature=foo'.
+ * Option letters were getting scarce, so I invented this new
+ * scheme: '-X feature' turns on some feature. Compare to the
+ * -f option in GCC. You should also add an equivalent
+ * GNU-style option --feature. Features that require
+ * arguments should use '-X feature=foo'.
*/
case 'X':
- if (strcmp(optarg, "use-set-session-authorization") == 0)
- /* no-op, still allowed for compatibility */ ;
+ if (strcmp(optarg, "disable-dollar-quoting") == 0)
+ disable_dollar_quoting = 1;
else if (strcmp(optarg, "disable-triggers") == 0)
disable_triggers = 1;
+ else if (strcmp(optarg, "use-set-session-authorization") == 0)
+ use_setsessauth = 1;
else
{
fprintf(stderr,
exit(1);
}
- if (outputBlobs && selectTableName != NULL)
- {
- write_msg(NULL, "large-object output not supported for a single table\n");
- write_msg(NULL, "use a full dump instead\n");
- exit(1);
- }
-
- if (outputBlobs && selectSchemaName != NULL)
- {
- write_msg(NULL, "large-object output not supported for a single schema\n");
- write_msg(NULL, "use a full dump instead\n");
- exit(1);
- }
+ if (matchingTables != NULL || matchingSchemas != NULL)
+ outputBlobs = false;
- if (dumpData == true && oids == true)
+ if (dumpInserts == true && oids == true)
{
write_msg(NULL, "INSERT (-d, -D) and OID (-o) options cannot be used together\n");
write_msg(NULL, "(The INSERT command cannot set OIDs.)\n");
exit(1);
}
- if (outputBlobs == true && (format[0] == 'p' || format[0] == 'P'))
- {
- write_msg(NULL, "large-object output is not supported for plain-text dump files\n");
- write_msg(NULL, "(Use a different output format.)\n");
- exit(1);
- }
-
/* open the output file */
switch (format[0])
{
}
/*
- * Open the database using the Archiver, so it knows about it. Errors
- * mean death.
+ * Open the database using the Archiver, so it knows about it. Errors mean
+ * death.
*/
g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport,
username, force_password, ignore_version);
+ /* Set the client encoding if requested */
+ if (dumpencoding)
+ {
+ if (PQsetClientEncoding(g_conn, dumpencoding) < 0)
+ {
+ write_msg(NULL, "invalid client encoding \"%s\" specified\n",
+ dumpencoding);
+ exit(1);
+ }
+ }
+
/*
- * Start serializable transaction to dump consistent data.
+ * Get the active encoding and the standard_conforming_strings setting,
+ * so we know how to escape strings.
*/
- res = PQexec(g_conn, "BEGIN");
- if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
- exit_horribly(g_fout, NULL, "BEGIN command failed: %s",
- PQerrorMessage(g_conn));
- PQclear(res);
+ g_fout->encoding = PQclientEncoding(g_conn);
- res = PQexec(g_conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
- if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
- exit_horribly(g_fout, NULL, "could not set transaction isolation level to serializable: %s",
- PQerrorMessage(g_conn));
- PQclear(res);
+ std_strings = PQparameterStatus(g_conn, "standard_conforming_strings");
+ g_fout->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
/* Set the datestyle to ISO to ensure the dump's portability */
- res = PQexec(g_conn, "SET DATESTYLE = ISO");
- if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
- exit_horribly(g_fout, NULL, "could not set datestyle to ISO: %s",
- PQerrorMessage(g_conn));
- PQclear(res);
+ do_sql_command(g_conn, "SET DATESTYLE = ISO");
+
+ /*
+ * Start serializable transaction to dump consistent data.
+ */
+ do_sql_command(g_conn, "BEGIN");
+
+ do_sql_command(g_conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
+
+ /* Select the appropriate subquery to convert user IDs to names */
+ if (g_fout->remoteVersion >= 80100)
+ username_subquery = "SELECT rolname FROM pg_catalog.pg_roles WHERE oid =";
+ else if (g_fout->remoteVersion >= 70300)
+ username_subquery = "SELECT usename FROM pg_catalog.pg_user WHERE usesysid =";
+ else
+ username_subquery = "SELECT usename FROM pg_user WHERE usesysid =";
/*
* If supported, set extra_float_digits so that we can dump float data
* exactly (given correctly implemented float I/O code, anyway)
*/
if (g_fout->remoteVersion >= 70400)
- {
- res = PQexec(g_conn, "SET extra_float_digits TO 2");
- if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
- exit_horribly(g_fout, NULL, "could not set extra_float_digits: %s",
- PQerrorMessage(g_conn));
- PQclear(res);
- }
+ do_sql_command(g_conn, "SET extra_float_digits TO 2");
/* Find the last built-in OID, if needed */
if (g_fout->remoteVersion < 70300)
write_msg(NULL, "last built-in OID is %u\n", g_last_builtin_oid);
}
- /* Dump the database definition */
- if (!dataOnly)
- dumpDatabase(g_fout);
- if (oids == true)
- setMaxOid(g_fout);
+ if (schemaList != NULL && g_fout->remoteVersion < 70300)
+ {
+ write_msg(NULL, "Postgres must be at least version 7.3 to use schema switches\n");
+ exit_nicely();
+ }
+
+ /* Check schema selection flags */
+ resetPQExpBuffer(query);
+ switch_include_exclude = true;
- tblinfo = dumpSchema(g_fout, &numTables, aclsSkip, schemaOnly, dataOnly);
+ for (this_obj_name = schemaList; this_obj_name; this_obj_name = this_obj_name->next)
+ {
+ if (switch_include_exclude)
+ {
+ /* Special case for when -N is the first argument */
+ if (this_obj_name == schemaList && !this_obj_name->is_include)
+ appendPQExpBuffer(query,
+ "SELECT oid FROM pg_catalog.pg_namespace "
+ "WHERE nspname NOT LIKE 'pg_%%' AND "
+ " nspname != 'information_schema' EXCEPT\n");
+
+ appendPQExpBuffer(query, "SELECT oid FROM pg_catalog.pg_namespace WHERE");
+ }
+
+ appendPQExpBuffer(query, "%s nspname %c ", switch_include_exclude ? "" : " OR",
+ /* any meta-characters? */
+ strpbrk(this_obj_name->name,"([{\\.?+") == NULL ? '=' : '~');
+ appendStringLiteralAH(query, this_obj_name->name, g_fout);
+
+ if (this_obj_name->next && this_obj_name->next->is_include == this_obj_name->is_include)
+ switch_include_exclude = false;
+ else
+ {
+ switch_include_exclude = true;
+
+ /* Add the joiner if needed */
+ if (this_obj_name->next)
+ appendPQExpBuffer(query, "\n%s\n",
+ this_obj_name->next->is_include ? "UNION" : "EXCEPT");
+ }
+ }
- if (!schemaOnly)
- dumpClasses(tblinfo, numTables, g_fout, oids);
+ /* Construct OID list of matching schemas */
+ if (schemaList)
+ {
+ int len;
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+ if (PQntuples(res) == 0)
+ {
+ write_msg(NULL, "No matching schemas were found\n");
+ exit_nicely();
+ }
+
+ for (i = 0, len = strlen(" "); i < PQntuples(res); i++)
+ len += strlen(PQgetvalue(res, i, 0)) + 1;
+
+ /*
+ * Need to use comma separators so it can be used by IN. zero
+ * is a dummy placeholder. Format is " oid oid oid ".
+ */
+ matchingSchemas = malloc(len + 1);
+ strcpy(matchingSchemas, " ");
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ strcat(matchingSchemas, PQgetvalue(res, i, 0));
+ strcat(matchingSchemas, " ");
+ }
+ }
+
+ /* Check table selection flags */
+ resetPQExpBuffer(query);
+ switch_include_exclude = true;
+
+ for (this_obj_name = tableList; this_obj_name; this_obj_name = this_obj_name->next)
+ {
+ if (switch_include_exclude)
+ {
+ /* Special case for when -T is the first argument */
+ if (this_obj_name == tableList && !this_obj_name->is_include && !strlen(query->data))
+ appendPQExpBuffer(query,
+ "SELECT pg_class.oid FROM pg_catalog.pg_class, pg_catalog.pg_namespace "
+ "WHERE relkind='r' AND "
+ " relnamespace = pg_namespace.oid AND "
+ " nspname NOT LIKE 'pg_%%' AND "
+ " nspname != 'information_schema' EXCEPT\n");
+
+ appendPQExpBuffer(query, "SELECT oid FROM pg_catalog.pg_class WHERE relkind='r' AND (");
+ }
+
+ appendPQExpBuffer(query, "%srelname %c ", switch_include_exclude ? "" : " OR ",
+ /* any meta-characters? */
+ strpbrk(this_obj_name->name,"([{\\.?+") == NULL ? '=' : '~');
+ appendStringLiteralAH(query, this_obj_name->name, g_fout);
+
+ if (this_obj_name->next && this_obj_name->next->is_include == this_obj_name->is_include)
+ switch_include_exclude = false;
+ else
+ {
+ switch_include_exclude = true;
+ appendPQExpBuffer(query, ")");
+
+ /* Add the joiner if needed */
+ if (this_obj_name->next)
+ appendPQExpBuffer(query, "\n%s\n", this_obj_name->next->is_include ?
+ "UNION" : "EXCEPT");
+ }
+ }
+
+ /* Construct OID list of matching tables */
+ if (tableList)
+ {
+ int len;
+
+ /* Restrict by schema? */
+ if (matchingSchemas != NULL)
+ {
+ char *matchingSchemas_commas = strdup(matchingSchemas), *p;
+
+ /* Construct "IN" SQL string by adding commas, " oid, oid, oid " */
+ for (p = matchingSchemas_commas; *p; p++)
+ {
+ /* No commas for first/last characters */
+ if (*p == ' ' && p != matchingSchemas_commas && *(p+1))
+ *p = ',';
+ }
+
+ appendPQExpBuffer(query,
+ "\nINTERSECT\nSELECT oid FROM pg_catalog.pg_class WHERE relkind='r' AND relnamespace IN (%s)\n",
+ matchingSchemas_commas);
+ }
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+ if (PQntuples(res) == 0)
+ {
+ write_msg(NULL, "No matching tables were found\n");
+ exit_nicely();
+ }
+
+ for (i = 0, len = strlen(" "); i < PQntuples(res); i++)
+ len += strlen(PQgetvalue(res, i, 0)) + 1;
+
+ matchingTables = malloc(len + 1);
+ strcpy(matchingTables, " ");
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ strcat(matchingTables, PQgetvalue(res, i, 0));
+ strcat(matchingTables, " ");
+ }
+ }
+
+ destroyPQExpBuffer(query);
+
+ /*
+ * Now scan the database and create DumpableObject structs for all the
+ * objects we intend to dump.
+ */
+ tblinfo = getSchemaData(&numTables);
- if (outputBlobs)
- ArchiveEntry(g_fout, "0", "BLOBS", NULL, "",
- "BLOBS", NULL, "", "", NULL, dumpBlobs, NULL);
+ if (!schemaOnly)
+ getTableData(tblinfo, numTables, oids);
- if (!dataOnly) /* dump indexes and triggers at the end
- * for performance */
+ if (outputBlobs && hasBlobs(g_fout))
{
- dumpConstraints(g_fout, tblinfo, numTables);
- dumpTriggers(g_fout, tblinfo, numTables);
- dumpRules(g_fout, tblinfo, numTables);
+ /* Add placeholders to allow correct sorting of blobs */
+ DumpableObject *blobobj;
+
+ blobobj = (DumpableObject *) malloc(sizeof(DumpableObject));
+ blobobj->objType = DO_BLOBS;
+ blobobj->catId = nilCatalogId;
+ AssignDumpId(blobobj);
+ blobobj->name = strdup("BLOBS");
+
+ blobobj = (DumpableObject *) malloc(sizeof(DumpableObject));
+ blobobj->objType = DO_BLOB_COMMENTS;
+ blobobj->catId = nilCatalogId;
+ AssignDumpId(blobobj);
+ blobobj->name = strdup("BLOB COMMENTS");
}
- /* Now sort the output nicely: by OID within object types */
- SortTocByOID(g_fout);
- SortTocByObjectType(g_fout);
+ /*
+ * Collect dependency data to assist in ordering the objects.
+ */
+ getDependencies();
+
+ /*
+ * Sort the objects into a safe dump order (no forward references).
+ *
+ * In 7.3 or later, we can rely on dependency information to help us
+ * determine a safe order, so the initial sort is mostly for cosmetic
+ * purposes: we sort by name to ensure that logically identical schemas
+ * will dump identically. Before 7.3 we don't have dependencies and we
+ * use OID ordering as an (unreliable) guide to creation order.
+ */
+ getDumpableObjects(&dobjs, &numObjs);
+
+ if (g_fout->remoteVersion >= 70300)
+ sortDumpableObjectsByTypeName(dobjs, numObjs);
+ else
+ sortDumpableObjectsByTypeOid(dobjs, numObjs);
+
+ sortDumpableObjects(dobjs, numObjs);
+
+ /*
+ * Create archive TOC entries for all the objects to be dumped, in a safe
+ * order.
+ */
+
+ /* First the special ENCODING and STDSTRINGS entries. */
+ dumpEncoding(g_fout);
+ dumpStdStrings(g_fout);
+
+ /* The database item is always next, unless we don't want it at all */
+ if (!dataOnly && matchingTables == NULL && matchingSchemas == NULL)
+ dumpDatabase(g_fout);
+
+ /* Now the rearrangeable objects. */
+ for (i = 0; i < numObjs; i++)
+ dumpDumpableObject(g_fout, dobjs[i]);
+ /*
+ * And finally we can do the actual output.
+ */
if (plainText)
{
ropt = NewRestoreOptions();
ropt->create = outputCreate;
ropt->noOwner = outputNoOwner;
ropt->disable_triggers = disable_triggers;
+ ropt->use_setsessauth = use_setsessauth;
+ ropt->dataOnly = dataOnly;
if (compressLevel == -1)
ropt->compression = 0;
else
ropt->compression = compressLevel;
- ropt->suppressDumpWarnings = true; /* We've already shown
- * them */
+ ropt->suppressDumpWarnings = true; /* We've already shown them */
RestoreArchive(g_fout, ropt);
}
CloseArchive(g_fout);
PQfinish(g_conn);
+
exit(0);
}
printf(_(" --version output version information, then exit\n"));
printf(_("\nOptions controlling the output content:\n"));
- printf(_(" -a, --data-only dump only the data, not the schema\n"));
- printf(_(" -b, --blobs include large objects in dump\n"));
- printf(_(" -c, --clean clean (drop) schema prior to create\n"));
- printf(_(" -C, --create include commands to create database in dump\n"));
- printf(_(" -d, --inserts dump data as INSERT, rather than COPY, commands\n"));
- printf(_(" -D, --column-inserts dump data as INSERT commands with column names\n"));
- printf(_(" -n, --schema=SCHEMA dump the named schema only\n"));
- printf(_(" -o, --oids include OIDs in dump\n"));
- printf(_(" -O, --no-owner do not output commands to set object ownership\n"
- " in plain text format\n"));
- printf(_(" -s, --schema-only dump only the schema, no data\n"));
- printf(_(" -S, --superuser=NAME specify the superuser user name to use in\n"
- " plain text format\n"));
- printf(_(" -t, --table=TABLE dump the named table only\n"));
- printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
+ printf(_(" -a, --data-only dump only the data, not the schema\n"));
+ printf(_(" -c, --clean clean (drop) schema prior to create\n"));
+ printf(_(" -C, --create include commands to create database in dump\n"));
+ printf(_(" -d, --inserts dump data as INSERT, rather than COPY, commands\n"));
+ printf(_(" -D, --column-inserts dump data as INSERT commands with column names\n"));
+ printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
+ printf(_(" -n, --schema=SCHEMA dump the named schema only\n"));
+ printf(_(" -N, --exclude-schema=SCHEMA do NOT dump the named schema\n"));
+ printf(_(" -o, --oids include OIDs in dump\n"));
+ printf(_(" -O, --no-owner skip restoration of object ownership\n"
+ " in plain text format\n"));
+ printf(_(" -s, --schema-only dump only the schema, no data\n"));
+ printf(_(" -S, --superuser=NAME specify the superuser user name to use in\n"
+ " plain text format\n"));
+ printf(_(" -t, --table=TABLE dump the named table only\n"));
+ printf(_(" -T, --exclude-table=TABLE do NOT dump the named table\n"));
+ printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
+ printf(_(" -X disable-dollar-quoting, --disable-dollar-quoting\n"
+ " disable dollar quoting, use SQL standard quoting\n"));
printf(_(" -X disable-triggers, --disable-triggers\n"
- " disable triggers during data-only restore\n"));
+ " disable triggers during data-only restore\n"));
+ printf(_(" -X use-set-session-authorization, --use-set-session-authorization\n"
+ " use SESSION AUTHORIZATION commands instead of\n"
+ " OWNER TO commands\n"));
printf(_("\nConnection options:\n"));
printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
selectDumpableNamespace(NamespaceInfo *nsinfo)
{
/*
- * If a specific table is being dumped, do not dump any complete
- * namespaces. If a specific namespace is being dumped, dump just
- * that namespace. Otherwise, dump all non-system namespaces.
+ * If specific tables are being dumped, do not dump any complete
+ * namespaces. If specific namespaces are being dumped, dump just
+ * those namespaces. Otherwise, dump all non-system namespaces.
*/
- if (selectTableName != NULL)
- nsinfo->dump = false;
- else if (selectSchemaName != NULL)
+ nsinfo->dobj.dump = false;
+
+ if (matchingTables != NULL)
+ /* false */;
+ else if (matchingSchemas != NULL)
{
- if (strcmp(nsinfo->nspname, selectSchemaName) == 0)
- nsinfo->dump = true;
- else
- nsinfo->dump = false;
+ char *search_oid = malloc(20);
+
+ sprintf(search_oid, " %d ", nsinfo->dobj.catId.oid);
+ if (strstr(matchingSchemas, search_oid) != NULL)
+ nsinfo->dobj.dump = true;
+
+ free(search_oid);
}
- else if (strncmp(nsinfo->nspname, "pg_", 3) == 0 ||
- strcmp(nsinfo->nspname, "information_schema") == 0)
- nsinfo->dump = false;
- else
- nsinfo->dump = true;
+ /* The server prevents users from creating pg_ schemas */
+ else if (strncmp(nsinfo->dobj.name, "pg_", 3) != 0 &&
+ strcmp(nsinfo->dobj.name, "information_schema") != 0)
+ nsinfo->dobj.dump = true;
}
/*
{
/*
* Always dump if dumping parent namespace; else, if a particular
- * tablename has been specified, dump matching table name; else, do
- * not dump.
+ * tablename has been specified, dump matching table name; else, do not
+ * dump.
*/
- tbinfo->dump = false;
- if (tbinfo->relnamespace->dump)
- tbinfo->dump = true;
- else if (selectTableName != NULL &&
- strcmp(tbinfo->relname, selectTableName) == 0)
- {
- /* If both -s and -t specified, must match both to dump */
- if (selectSchemaName == NULL)
- tbinfo->dump = true;
- else if (strcmp(tbinfo->relnamespace->nspname, selectSchemaName) == 0)
- tbinfo->dump = true;
+ tbinfo->dobj.dump = false;
+
+ if (matchingTables == NULL)
+ {
+ if (tbinfo->dobj.namespace->dobj.dump)
+ tbinfo->dobj.dump = true;
+ }
+ else
+ {
+ char *search_oid = malloc(20);
+
+ sprintf(search_oid, " %d ", tbinfo->dobj.catId.oid);
+ if (strstr(matchingTables, search_oid) != NULL)
+ tbinfo->dobj.dump = true;
+
+ free(search_oid);
}
}
+/*
+ * selectDumpableType: policy-setting subroutine
+ * Mark a type as to be dumped or not
+ */
+static void
+selectDumpableType(TypeInfo *tinfo)
+{
+ /* Dump only types in dumpable namespaces */
+ if (!tinfo->dobj.namespace->dobj.dump)
+ tinfo->dobj.dump = false;
+
+ /* skip complex types, except for standalone composite types */
+ /* (note: this test should now be unnecessary) */
+ else if (OidIsValid(tinfo->typrelid) &&
+ tinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
+ tinfo->dobj.dump = false;
+
+ /* skip undefined placeholder types */
+ else if (!tinfo->isDefined)
+ tinfo->dobj.dump = false;
+
+ /* skip all array types that start w/ underscore */
+ else if ((tinfo->dobj.name[0] == '_') &&
+ OidIsValid(tinfo->typelem))
+ tinfo->dobj.dump = false;
+
+ else
+ tinfo->dobj.dump = true;
+}
+
+/*
+ * selectDumpableObject: policy-setting subroutine
+ * Mark a generic dumpable object as to be dumped or not
+ *
+ * Use this only for object types without a special-case routine above.
+ */
+static void
+selectDumpableObject(DumpableObject *dobj)
+{
+ /*
+ * Default policy is to dump if parent namespace is dumpable,
+ * or always for non-namespace-associated items.
+ */
+ if (dobj->namespace)
+ dobj->dump = dobj->namespace->dobj.dump;
+ else
+ dobj->dump = true;
+}
+
/*
* Dump a table's contents for loading using the COPY command
* - this routine is called by the Archiver when it wants the table
* to be dumped.
*/
-#define COPYBUFSIZ 8192
-
static int
-dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv)
+dumpTableData_copy(Archive *fout, void *dcontext)
{
- const DumpContext *dctx = (DumpContext *) dctxv;
- TableInfo *tbinfo = &dctx->tblinfo[dctx->tblidx];
- const char *classname = tbinfo->relname;
+ TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
+ TableInfo *tbinfo = tdinfo->tdtable;
+ const char *classname = tbinfo->dobj.name;
const bool hasoids = tbinfo->hasoids;
- const bool oids = dctx->oids;
+ const bool oids = tdinfo->oids;
PQExpBuffer q = createPQExpBuffer();
PGresult *res;
int ret;
- bool copydone;
- char copybuf[COPYBUFSIZ];
+ char *copybuf;
const char *column_list;
if (g_verbose)
/*
* Make sure we are in proper schema. We will qualify the table name
- * below anyway (in case its name conflicts with a pg_catalog table);
- * but this ensures reproducible results in case the table contains
- * regproc, regclass, etc columns.
+ * below anyway (in case its name conflicts with a pg_catalog table); but
+ * this ensures reproducible results in case the table contains regproc,
+ * regclass, etc columns.
*/
- selectSourceSchema(tbinfo->relnamespace->nspname);
+ selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
/*
* If possible, specify the column list explicitly so that we have no
- * possibility of retrieving data in the wrong column order. (The
- * default column ordering of COPY will not be what we want in certain
- * corner cases involving ADD COLUMN and inheritance.)
+ * possibility of retrieving data in the wrong column order. (The default
+ * column ordering of COPY will not be what we want in certain corner
+ * cases involving ADD COLUMN and inheritance.)
*/
if (g_fout->remoteVersion >= 70300)
column_list = fmtCopyColumnList(tbinfo);
if (oids && hasoids)
{
appendPQExpBuffer(q, "COPY %s %s WITH OIDS TO stdout;",
- fmtQualifiedId(tbinfo->relnamespace->nspname,
+ fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
classname),
column_list);
}
else
{
appendPQExpBuffer(q, "COPY %s %s TO stdout;",
- fmtQualifiedId(tbinfo->relnamespace->nspname,
+ fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
classname),
column_list);
}
res = PQexec(g_conn, q->data);
- if (!res ||
- PQresultStatus(res) == PGRES_FATAL_ERROR)
- {
- write_msg(NULL, "SQL command to dump the contents of table \"%s\" failed\n",
- classname);
- write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
- write_msg(NULL, "The command was: %s\n", q->data);
- exit_nicely();
- }
- if (PQresultStatus(res) != PGRES_COPY_OUT)
- {
- write_msg(NULL, "SQL command to dump the contents of table \"%s\" executed abnormally.\n",
- classname);
- write_msg(NULL, "The server returned status %d when %d was expected.\n",
- PQresultStatus(res), PGRES_COPY_OUT);
- write_msg(NULL, "The command was: %s\n", q->data);
- exit_nicely();
- }
-
- copydone = false;
+ check_sql_result(res, g_conn, q->data, PGRES_COPY_OUT);
+ PQclear(res);
- while (!copydone)
+ for (;;)
{
- ret = PQgetline(g_conn, copybuf, COPYBUFSIZ);
+ ret = PQgetCopyData(g_conn, ©buf, 0);
- if (copybuf[0] == '\\' &&
- copybuf[1] == '.' &&
- copybuf[2] == '\0')
- {
- copydone = true; /* don't print this... */
- }
- else
+ if (ret < 0)
+ break; /* done or error */
+
+ if (copybuf)
{
- archputs(copybuf, fout);
- switch (ret)
- {
- case EOF:
- copydone = true;
- /* FALLTHROUGH */
- case 0:
- archputc('\n', fout);
- break;
- case 1:
- break;
- }
+ WriteData(fout, copybuf, ret);
+ PQfreemem(copybuf);
}
/*
* THROTTLE:
*
* There was considerable discussion in late July, 2000 regarding
- * slowing down pg_dump when backing up large tables. Users with
- * both slow & fast (muti-processor) machines experienced
- * performance degradation when doing a backup.
+ * slowing down pg_dump when backing up large tables. Users with both
+ * slow & fast (multi-processor) machines experienced performance
+ * degradation when doing a backup.
*
* Initial attempts based on sleeping for a number of ms for each ms
- * of work were deemed too complex, then a simple 'sleep in each
- * loop' implementation was suggested. The latter failed because
- * the loop was too tight. Finally, the following was implemented:
+ * of work were deemed too complex, then a simple 'sleep in each loop'
+ * implementation was suggested. The latter failed because the loop
+ * was too tight. Finally, the following was implemented:
*
* If throttle is non-zero, then See how long since the last sleep.
- * Work out how long to sleep (based on ratio). If sleep is more
- * than 100ms, then sleep reset timer EndIf EndIf
+ * Work out how long to sleep (based on ratio). If sleep is more than
+ * 100ms, then sleep reset timer EndIf EndIf
*
* where the throttle value was the number of ms to sleep per ms of
* work. The calculation was done in each loop.
*
* Most of the hard work is done in the backend, and this solution
- * still did not work particularly well: on slow machines, the
- * ratio was 50:1, and on medium paced machines, 1:1, and on fast
- * multi-processor machines, it had little or no effect, for
- * reasons that were unclear.
+ * still did not work particularly well: on slow machines, the ratio
+ * was 50:1, and on medium paced machines, 1:1, and on fast
+ * multi-processor machines, it had little or no effect, for reasons
+ * that were unclear.
*
* Further discussion ensued, and the proposal was dropped.
*
- * For those people who want this feature, it can be implemented
- * using gettimeofday in each loop, calculating the time since
- * last sleep, multiplying that by the sleep ratio, then if the
- * result is more than a preset 'minimum sleep time' (say 100ms),
- * call the 'select' function to sleep for a subsecond period ie.
+ * For those people who want this feature, it can be implemented using
+ * gettimeofday in each loop, calculating the time since last sleep,
+ * multiplying that by the sleep ratio, then if the result is more
+ * than a preset 'minimum sleep time' (say 100ms), call the 'select'
+ * function to sleep for a subsecond period ie.
*
* select(0, NULL, NULL, NULL, &tvi);
*
- * This will return after the interval specified in the structure
- * tvi. Finally, call gettimeofday again to save the 'last sleep
- * time'.
+ * This will return after the interval specified in the structure tvi.
+ * Finally, call gettimeofday again to save the 'last sleep time'.
*/
}
archprintf(fout, "\\.\n\n\n");
- ret = PQendcopy(g_conn);
- if (ret != 0)
+ if (ret == -2)
{
- write_msg(NULL, "SQL command to dump the contents of table \"%s\" failed: PQendcopy() failed.\n", classname);
+ /* copy data transfer failed */
+ write_msg(NULL, "Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.\n", classname);
write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
write_msg(NULL, "The command was: %s\n", q->data);
exit_nicely();
}
+ /* Check command status and return to normal libpq state */
+ res = PQgetResult(g_conn);
+ check_sql_result(res, g_conn, q->data, PGRES_COMMAND_OK);
PQclear(res);
+
destroyPQExpBuffer(q);
return 1;
}
static int
-dumpClasses_dumpData(Archive *fout, char *oid, void *dctxv)
+dumpTableData_insert(Archive *fout, void *dcontext)
{
- const DumpContext *dctx = (DumpContext *) dctxv;
- TableInfo *tbinfo = &dctx->tblinfo[dctx->tblidx];
- const char *classname = tbinfo->relname;
+ TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
+ TableInfo *tbinfo = tdinfo->tdtable;
+ const char *classname = tbinfo->dobj.name;
PQExpBuffer q = createPQExpBuffer();
PGresult *res;
int tuple;
/*
* Make sure we are in proper schema. We will qualify the table name
- * below anyway (in case its name conflicts with a pg_catalog table);
- * but this ensures reproducible results in case the table contains
- * regproc, regclass, etc columns.
+ * below anyway (in case its name conflicts with a pg_catalog table); but
+ * this ensures reproducible results in case the table contains regproc,
+ * regclass, etc columns.
*/
- selectSourceSchema(tbinfo->relnamespace->nspname);
+ selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
if (fout->remoteVersion >= 70100)
{
appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
"SELECT * FROM ONLY %s",
- fmtQualifiedId(tbinfo->relnamespace->nspname,
+ fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
classname));
}
else
{
appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
"SELECT * FROM %s",
- fmtQualifiedId(tbinfo->relnamespace->nspname,
+ fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
classname));
}
res = PQexec(g_conn, q->data);
- if (!res ||
- PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- write_msg(NULL, "dumpClasses(): SQL command failed\n");
- write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
- write_msg(NULL, "The command was: %s\n", q->data);
- exit_nicely();
- }
+ check_sql_result(res, g_conn, q->data, PGRES_COMMAND_OK);
do
{
PQclear(res);
res = PQexec(g_conn, "FETCH 100 FROM _pg_dump_cursor");
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "dumpClasses(): SQL command failed\n");
- write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
- write_msg(NULL, "The command was: FETCH 100 FROM _pg_dump_cursor\n");
- exit_nicely();
- }
-
- nfields = PQnfields(res);
- for (tuple = 0; tuple < PQntuples(res); tuple++)
+ check_sql_result(res, g_conn, "FETCH 100 FROM _pg_dump_cursor",
+ PGRES_TUPLES_OK);
+ nfields = PQnfields(res);
+ for (tuple = 0; tuple < PQntuples(res); tuple++)
{
archprintf(fout, "INSERT INTO %s ", fmtId(classname));
if (nfields == 0)
{
if (field > 0)
appendPQExpBuffer(q, ", ");
- appendPQExpBuffer(q, fmtId(PQfname(res, field)));
+ appendPQExpBufferStr(q, fmtId(PQfname(res, field)));
}
appendPQExpBuffer(q, ") ");
- archprintf(fout, "%s", q->data);
+ archputs(q->data, fout);
}
archprintf(fout, "VALUES (");
for (field = 0; field < nfields; field++)
case NUMERICOID:
{
/*
- * These types are printed without quotes
- * unless they contain values that aren't
- * accepted by the scanner unquoted (e.g.,
- * 'NaN'). Note that strtod() and friends
- * might accept NaN, so we can't use that to
- * test.
+ * These types are printed without quotes unless
+ * they contain values that aren't accepted by the
+ * scanner unquoted (e.g., 'NaN'). Note that
+ * strtod() and friends might accept NaN, so we
+ * can't use that to test.
*
* In reality we only need to defend against
- * infinity and NaN, so we need not get too
- * crazy about pattern matching here.
+ * infinity and NaN, so we need not get too crazy
+ * about pattern matching here.
*/
const char *s = PQgetvalue(res, tuple, field);
default:
/* All other types are printed as string literals. */
resetPQExpBuffer(q);
- appendStringLiteral(q, PQgetvalue(res, tuple, field), false);
- archprintf(fout, "%s", q->data);
+ appendStringLiteralAH(q,
+ PQgetvalue(res, tuple, field),
+ fout);
+ archputs(q->data, fout);
break;
}
}
}
} while (PQntuples(res) > 0);
- archprintf(fout, "\n\n");
PQclear(res);
- res = PQexec(g_conn, "CLOSE _pg_dump_cursor");
- if (!res ||
- PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- write_msg(NULL, "dumpClasses(): SQL command failed\n");
- write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
- write_msg(NULL, "The command was: CLOSE _pg_dump_cursor\n");
- exit_nicely();
- }
- PQclear(res);
+ archprintf(fout, "\n\n");
+
+ do_sql_command(g_conn, "CLOSE _pg_dump_cursor");
destroyPQExpBuffer(q);
return 1;
/*
- * DumpClasses -
- * dump the contents of all the classes.
+ * dumpTableData -
+ * dump the contents of a single table
+ *
+ * Actually, this just makes an ArchiveEntry for the table contents.
*/
static void
-dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
- const bool oids)
+dumpTableData(Archive *fout, TableDataInfo *tdinfo)
{
+ TableInfo *tbinfo = tdinfo->tdtable;
PQExpBuffer copyBuf = createPQExpBuffer();
DataDumperPtr dumpFn;
- DumpContext *dumpCtx;
char *copyStmt;
+
+ if (!dumpInserts)
+ {
+ /* Dump/restore using COPY */
+ dumpFn = dumpTableData_copy;
+ /* must use 2 steps here 'cause fmtId is nonreentrant */
+ appendPQExpBuffer(copyBuf, "COPY %s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n",
+ fmtCopyColumnList(tbinfo),
+ (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : "");
+ copyStmt = copyBuf->data;
+ }
+ else
+ {
+ /* Restore using INSERT */
+ dumpFn = dumpTableData_insert;
+ copyStmt = NULL;
+ }
+
+ ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
+ tbinfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname, false,
+ "TABLE DATA", "", "", copyStmt,
+ tdinfo->dobj.dependencies, tdinfo->dobj.nDeps,
+ dumpFn, tdinfo);
+
+ destroyPQExpBuffer(copyBuf);
+}
+
+/*
+ * getTableData -
+ * set up dumpable objects representing the contents of tables
+ */
+static void
+getTableData(TableInfo *tblinfo, int numTables, bool oids)
+{
int i;
for (i = 0; i < numTables; i++)
{
- const char *classname = tblinfo[i].relname;
-
- /* Skip VIEW relations */
+ /* Skip VIEWs (no data to dump) */
if (tblinfo[i].relkind == RELKIND_VIEW)
continue;
-
- if (tblinfo[i].relkind == RELKIND_SEQUENCE) /* already dumped */
+ /* Skip SEQUENCEs (handled elsewhere) */
+ if (tblinfo[i].relkind == RELKIND_SEQUENCE)
continue;
- if (tblinfo[i].dump)
+ if (tblinfo[i].dobj.dump)
{
- if (g_verbose)
- write_msg(NULL, "preparing to dump the contents of table %s\n",
- classname);
+ TableDataInfo *tdinfo;
- dumpCtx = (DumpContext *) malloc(sizeof(DumpContext));
- dumpCtx->tblinfo = (TableInfo *) tblinfo;
- dumpCtx->tblidx = i;
- dumpCtx->oids = oids;
+ tdinfo = (TableDataInfo *) malloc(sizeof(TableDataInfo));
- if (!dumpData)
- {
- /* Dump/restore using COPY */
- dumpFn = dumpClasses_nodumpData;
- resetPQExpBuffer(copyBuf);
- /* must use 2 steps here 'cause fmtId is nonreentrant */
- appendPQExpBuffer(copyBuf, "COPY %s ",
- fmtId(tblinfo[i].relname));
- appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n",
- fmtCopyColumnList(&(tblinfo[i])),
- (oids && tblinfo[i].hasoids) ? "WITH OIDS " : "");
- copyStmt = copyBuf->data;
- }
- else
- {
- /* Restore using INSERT */
- dumpFn = dumpClasses_dumpData;
- copyStmt = NULL;
- }
+ tdinfo->dobj.objType = DO_TABLE_DATA;
- ArchiveEntry(fout, tblinfo[i].oid, tblinfo[i].relname,
- tblinfo[i].relnamespace->nspname,
- tblinfo[i].usename,
- "TABLE DATA", NULL, "", "", copyStmt,
- dumpFn, dumpCtx);
+ /*
+ * Note: use tableoid 0 so that this object won't be mistaken for
+ * something that pg_depend entries apply to.
+ */
+ tdinfo->dobj.catId.tableoid = 0;
+ tdinfo->dobj.catId.oid = tblinfo[i].dobj.catId.oid;
+ AssignDumpId(&tdinfo->dobj);
+ tdinfo->dobj.name = tblinfo[i].dobj.name;
+ tdinfo->dobj.namespace = tblinfo[i].dobj.namespace;
+ tdinfo->tdtable = &(tblinfo[i]);
+ tdinfo->oids = oids;
+ addObjectDependency(&tdinfo->dobj, tblinfo[i].dobj.dumpId);
}
}
-
- destroyPQExpBuffer(copyBuf);
}
* dumpDatabase:
* dump the database definition
*/
-static int
+static void
dumpDatabase(Archive *AH)
{
PQExpBuffer dbQry = createPQExpBuffer();
PQExpBuffer creaQry = createPQExpBuffer();
PGresult *res;
int ntups;
- int i_dba,
+ int i_tableoid,
+ i_oid,
+ i_dba,
i_encoding,
- i_datpath;
+ i_tablespace;
+ CatalogId dbCatId;
+ DumpId dbDumpId;
const char *datname,
*dba,
*encoding,
- *datpath;
+ *tablespace;
datname = PQdb(g_conn);
selectSourceSchema("pg_catalog");
/* Get the database owner and parameters from pg_database */
- appendPQExpBuffer(dbQry, "select (select usename from pg_user where usesysid = datdba) as dba,"
- " pg_encoding_to_char(encoding) as encoding,"
- " datpath from pg_database"
- " where datname = ");
- appendStringLiteral(dbQry, datname, true);
-
- res = PQexec(g_conn, dbQry->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
+ if (g_fout->remoteVersion >= 80200)
+ {
+ appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
+ "(%s datdba) as dba, "
+ "pg_encoding_to_char(encoding) as encoding, "
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) as tablespace, "
+ "shobj_description(oid, 'pg_database') as description "
+
+ "FROM pg_database "
+ "WHERE datname = ",
+ username_subquery);
+ appendStringLiteralAH(dbQry, datname, AH);
+ }
+ else if (g_fout->remoteVersion >= 80000)
+ {
+ appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
+ "(%s datdba) as dba, "
+ "pg_encoding_to_char(encoding) as encoding, "
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) as tablespace "
+ "FROM pg_database "
+ "WHERE datname = ",
+ username_subquery);
+ appendStringLiteralAH(dbQry, datname, AH);
+ }
+ else if (g_fout->remoteVersion >= 70100)
{
- write_msg(NULL, "SQL command failed\n");
- write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn));
- write_msg(NULL, "The command was: %s\n", dbQry->data);
- exit_nicely();
+ appendPQExpBuffer(dbQry, "SELECT tableoid, oid, "
+ "(%s datdba) as dba, "
+ "pg_encoding_to_char(encoding) as encoding, "
+ "NULL as tablespace "
+ "FROM pg_database "
+ "WHERE datname = ",
+ username_subquery);
+ appendStringLiteralAH(dbQry, datname, AH);
+ }
+ else
+ {
+ appendPQExpBuffer(dbQry, "SELECT "
+ "(SELECT oid FROM pg_class WHERE relname = 'pg_database') AS tableoid, "
+ "oid, "
+ "(%s datdba) as dba, "
+ "pg_encoding_to_char(encoding) as encoding, "
+ "NULL as tablespace "
+ "FROM pg_database "
+ "WHERE datname = ",
+ username_subquery);
+ appendStringLiteralAH(dbQry, datname, AH);
}
+ res = PQexec(g_conn, dbQry->data);
+ check_sql_result(res, g_conn, dbQry->data, PGRES_TUPLES_OK);
+
ntups = PQntuples(res);
if (ntups <= 0)
exit_nicely();
}
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
i_dba = PQfnumber(res, "dba");
i_encoding = PQfnumber(res, "encoding");
- i_datpath = PQfnumber(res, "datpath");
+ i_tablespace = PQfnumber(res, "tablespace");
+
+ dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
+ dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
dba = PQgetvalue(res, 0, i_dba);
encoding = PQgetvalue(res, 0, i_encoding);
- datpath = PQgetvalue(res, 0, i_datpath);
+ tablespace = PQgetvalue(res, 0, i_tablespace);
appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
fmtId(datname));
- if (strlen(datpath) > 0)
- {
- appendPQExpBuffer(creaQry, " LOCATION = ");
- appendStringLiteral(creaQry, datpath, true);
- }
if (strlen(encoding) > 0)
{
appendPQExpBuffer(creaQry, " ENCODING = ");
- appendStringLiteral(creaQry, encoding, true);
+ appendStringLiteralAH(creaQry, encoding, AH);
}
+ if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0)
+ appendPQExpBuffer(creaQry, " TABLESPACE = %s",
+ fmtId(tablespace));
appendPQExpBuffer(creaQry, ";\n");
appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
fmtId(datname));
- ArchiveEntry(AH, "0", /* OID */
+ dbDumpId = createDumpId();
+
+ ArchiveEntry(AH,
+ dbCatId, /* catalog ID */
+ dbDumpId, /* dump ID */
datname, /* Name */
NULL, /* Namespace */
+ NULL, /* Tablespace */
dba, /* Owner */
+ false, /* with oids */
"DATABASE", /* Desc */
- NULL, /* Deps */
creaQry->data, /* Create */
delQry->data, /* Del */
NULL, /* Copy */
+ NULL, /* Deps */
+ 0, /* # Deps */
NULL, /* Dumper */
NULL); /* Dumper Arg */
+ /* Dump DB comment if any */
+ if (g_fout->remoteVersion >= 80200)
+ {
+ /* 8.2 keeps comments on shared objects in a shared table, so
+ * we cannot use the dumpComment used for other database objects.
+ */
+ char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
+ if (comment && strlen(comment)) {
+ resetPQExpBuffer(dbQry);
+ appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", fmtId(datname));
+ appendStringLiteralAH(dbQry, comment, AH);
+ appendPQExpBuffer(dbQry, ";\n");
+
+ ArchiveEntry(AH, dbCatId, createDumpId(), datname, NULL, NULL,
+ dba, false, "COMMENT", dbQry->data, "", NULL,
+ &dbDumpId, 1, NULL, NULL);
+ }
+ } else {
+ resetPQExpBuffer(dbQry);
+ appendPQExpBuffer(dbQry, "DATABASE %s", fmtId(datname));
+ dumpComment(AH, dbQry->data, NULL, "",
+ dbCatId, 0, dbDumpId);
+ }
+
PQclear(res);
destroyPQExpBuffer(dbQry);
destroyPQExpBuffer(delQry);
destroyPQExpBuffer(creaQry);
+}
- return 1;
+
+/*
+ * dumpEncoding: put the correct encoding into the archive
+ */
+static void
+dumpEncoding(Archive *AH)
+{
+ const char *encname = pg_encoding_to_char(AH->encoding);
+ PQExpBuffer qry = createPQExpBuffer();
+
+ if (g_verbose)
+ write_msg(NULL, "saving encoding = %s\n", encname);
+
+ appendPQExpBuffer(qry, "SET client_encoding = ");
+ appendStringLiteralAH(qry, encname, AH);
+ appendPQExpBuffer(qry, ";\n");
+
+ ArchiveEntry(AH, nilCatalogId, createDumpId(),
+ "ENCODING", NULL, NULL, "",
+ false, "ENCODING", qry->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(qry);
}
/*
- * dumpBlobs:
- * dump all blobs
- *
+ * dumpStdStrings: put the correct escape string behavior into the archive
+ */
+static void
+dumpStdStrings(Archive *AH)
+{
+ const char *stdstrings = AH->std_strings ? "on" : "off";
+ PQExpBuffer qry = createPQExpBuffer();
+
+ if (g_verbose)
+ write_msg(NULL, "saving standard_conforming_strings = %s\n",
+ stdstrings);
+
+ appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
+ stdstrings);
+
+ ArchiveEntry(AH, nilCatalogId, createDumpId(),
+ "STDSTRINGS", NULL, NULL, "",
+ false, "STDSTRINGS", qry->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(qry);
+}
+
+
+/*
+ * hasBlobs:
+ * Test whether database contains any large objects
*/
+static bool
+hasBlobs(Archive *AH)
+{
+ bool result;
+ const char *blobQry;
+ PGresult *res;
-#define loBufSize 16384
-#define loFetchSize 1000
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ /* Check for BLOB OIDs */
+ if (AH->remoteVersion >= 70100)
+ blobQry = "SELECT loid FROM pg_largeobject LIMIT 1";
+ else
+ blobQry = "SELECT oid FROM pg_class WHERE relkind = 'l' LIMIT 1";
+
+ res = PQexec(g_conn, blobQry);
+ check_sql_result(res, g_conn, blobQry, PGRES_TUPLES_OK);
+
+ result = PQntuples(res) > 0;
+
+ PQclear(res);
+
+ return result;
+}
+/*
+ * dumpBlobs:
+ * dump all blobs
+ */
static int
-dumpBlobs(Archive *AH, char *junkOid, void *junkVal)
+dumpBlobs(Archive *AH, void *arg)
{
- PQExpBuffer oidQry = createPQExpBuffer();
- PQExpBuffer oidFetchQry = createPQExpBuffer();
+ const char *blobQry;
+ const char *blobFetchQry;
PGresult *res;
+ char buf[LOBBUFSIZE];
int i;
- int loFd;
- char buf[loBufSize];
int cnt;
- Oid blobOid;
if (g_verbose)
write_msg(NULL, "saving large objects\n");
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
- /* Cursor to get all BLOB tables */
+ /* Cursor to get all BLOB OIDs */
if (AH->remoteVersion >= 70100)
- appendPQExpBuffer(oidQry, "Declare blobOid Cursor for SELECT DISTINCT loid FROM pg_largeobject");
+ blobQry = "DECLARE bloboid CURSOR FOR SELECT DISTINCT loid FROM pg_largeobject";
else
- appendPQExpBuffer(oidQry, "Declare blobOid Cursor for SELECT oid from pg_class where relkind = 'l'");
+ blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_class WHERE relkind = 'l'";
- res = PQexec(g_conn, oidQry->data);
- if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- write_msg(NULL, "dumpBlobs(): cursor declaration failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ res = PQexec(g_conn, blobQry);
+ check_sql_result(res, g_conn, blobQry, PGRES_COMMAND_OK);
- /* Fetch for cursor */
- appendPQExpBuffer(oidFetchQry, "Fetch %d in blobOid", loFetchSize);
+ /* Command to fetch from cursor */
+ blobFetchQry = "FETCH 1000 IN bloboid";
do
{
- /* Do a fetch */
PQclear(res);
- res = PQexec(g_conn, oidFetchQry->data);
- if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "dumpBlobs(): fetch from cursor failed: %s",
- PQerrorMessage(g_conn));
- exit_nicely();
- }
+ /* Do a fetch */
+ res = PQexec(g_conn, blobFetchQry);
+ check_sql_result(res, g_conn, blobFetchQry, PGRES_TUPLES_OK);
/* Process the tuples, if any */
for (i = 0; i < PQntuples(res); i++)
{
+ Oid blobOid;
+ int loFd;
+
blobOid = atooid(PQgetvalue(res, i, 0));
/* Open the BLOB */
loFd = lo_open(g_conn, blobOid, INV_READ);
/* Now read it in chunks, sending data to archive */
do
{
- cnt = lo_read(g_conn, loFd, buf, loBufSize);
+ cnt = lo_read(g_conn, loFd, buf, LOBBUFSIZE);
if (cnt < 0)
{
write_msg(NULL, "dumpBlobs(): error reading large object: %s",
}
WriteData(AH, buf, cnt);
-
} while (cnt > 0);
lo_close(g_conn, loFd);
EndBlob(AH, blobOid);
+ }
+ } while (PQntuples(res) > 0);
+
+ PQclear(res);
+
+ return 1;
+}
+
+/*
+ * dumpBlobComments
+ * dump all blob comments
+ *
+ * Since we don't provide any way to be selective about dumping blobs,
+ * there's no need to be selective about their comments either. We put
+ * all the comments into one big TOC entry.
+ */
+static int
+dumpBlobComments(Archive *AH, void *arg)
+{
+ const char *blobQry;
+ const char *blobFetchQry;
+ PQExpBuffer commentcmd = createPQExpBuffer();
+ PGresult *res;
+ int i;
+
+ if (g_verbose)
+ write_msg(NULL, "saving large object comments\n");
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ /* Cursor to get all BLOB comments */
+ if (AH->remoteVersion >= 70200)
+ blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, obj_description(loid, 'pg_largeobject') FROM (SELECT DISTINCT loid FROM pg_largeobject) ss";
+ else if (AH->remoteVersion >= 70100)
+ blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, obj_description(loid) FROM (SELECT DISTINCT loid FROM pg_largeobject) ss";
+ else
+ blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, (SELECT description FROM pg_description pd WHERE pd.objoid=pc.oid) FROM pg_class pc WHERE relkind = 'l'";
+
+ res = PQexec(g_conn, blobQry);
+ check_sql_result(res, g_conn, blobQry, PGRES_COMMAND_OK);
+
+ /* Command to fetch from cursor */
+ blobFetchQry = "FETCH 100 IN blobcmt";
+
+ do
+ {
+ PQclear(res);
+
+ /* Do a fetch */
+ res = PQexec(g_conn, blobFetchQry);
+ check_sql_result(res, g_conn, blobFetchQry, PGRES_TUPLES_OK);
+
+ /* Process the tuples, if any */
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ Oid blobOid;
+ char *comment;
+
+ /* ignore blobs without comments */
+ if (PQgetisnull(res, i, 1))
+ continue;
+
+ blobOid = atooid(PQgetvalue(res, i, 0));
+ comment = PQgetvalue(res, i, 1);
+ printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ",
+ blobOid);
+ appendStringLiteralAH(commentcmd, comment, AH);
+ appendPQExpBuffer(commentcmd, ";\n");
+
+ archputs(commentcmd->data, AH);
}
} while (PQntuples(res) > 0);
- destroyPQExpBuffer(oidQry);
- destroyPQExpBuffer(oidFetchQry);
+ PQclear(res);
+
+ archputs("\n", AH);
+
+ destroyPQExpBuffer(commentcmd);
return 1;
}
int i;
PQExpBuffer query;
NamespaceInfo *nsinfo;
+ int i_tableoid;
int i_oid;
int i_nspname;
- int i_usename;
+ int i_rolname;
int i_nspacl;
/*
- * Before 7.3, there are no real namespaces; create two dummy entries,
- * one for user stuff and one for system stuff.
+ * Before 7.3, there are no real namespaces; create two dummy entries, one
+ * for user stuff and one for system stuff.
*/
if (g_fout->remoteVersion < 70300)
{
nsinfo = (NamespaceInfo *) malloc(2 * sizeof(NamespaceInfo));
- nsinfo[0].oid = strdup("0");
- nsinfo[0].nspname = strdup("");
- nsinfo[0].usename = strdup("");
+ nsinfo[0].dobj.objType = DO_NAMESPACE;
+ nsinfo[0].dobj.catId.tableoid = 0;
+ nsinfo[0].dobj.catId.oid = 0;
+ AssignDumpId(&nsinfo[0].dobj);
+ nsinfo[0].dobj.name = strdup("public");
+ nsinfo[0].rolname = strdup("");
nsinfo[0].nspacl = strdup("");
selectDumpableNamespace(&nsinfo[0]);
- nsinfo[1].oid = strdup("1");
- nsinfo[1].nspname = strdup("pg_catalog");
- nsinfo[1].usename = strdup("");
+ nsinfo[1].dobj.objType = DO_NAMESPACE;
+ nsinfo[1].dobj.catId.tableoid = 0;
+ nsinfo[1].dobj.catId.oid = 1;
+ AssignDumpId(&nsinfo[1].dobj);
+ nsinfo[1].dobj.name = strdup("pg_catalog");
+ nsinfo[1].rolname = strdup("");
nsinfo[1].nspacl = strdup("");
selectDumpableNamespace(&nsinfo[1]);
selectSourceSchema("pg_catalog");
/*
- * we fetch all namespaces including system ones, so that every object
- * we read in can be linked to a containing namespace.
+ * we fetch all namespaces including system ones, so that every object we
+ * read in can be linked to a containing namespace.
*/
- appendPQExpBuffer(query, "SELECT oid, nspname, "
- "(select usename from pg_user where nspowner = usesysid) as usename, "
- "nspacl "
- "FROM pg_namespace");
+ appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
+ "(%s nspowner) as rolname, "
+ "nspacl FROM pg_namespace",
+ username_subquery);
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain list of schemas failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
nsinfo = (NamespaceInfo *) malloc(ntups * sizeof(NamespaceInfo));
+ i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_nspname = PQfnumber(res, "nspname");
- i_usename = PQfnumber(res, "usename");
+ i_rolname = PQfnumber(res, "rolname");
i_nspacl = PQfnumber(res, "nspacl");
for (i = 0; i < ntups; i++)
{
- nsinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
- nsinfo[i].nspname = strdup(PQgetvalue(res, i, i_nspname));
- nsinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
+ nsinfo[i].dobj.objType = DO_NAMESPACE;
+ nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&nsinfo[i].dobj);
+ nsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_nspname));
+ nsinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl));
/* Decide whether to dump this namespace */
selectDumpableNamespace(&nsinfo[i]);
- if (strlen(nsinfo[i].usename) == 0)
+ if (strlen(nsinfo[i].rolname) == 0)
write_msg(NULL, "WARNING: owner of schema \"%s\" appears to be invalid\n",
- nsinfo[i].nspname);
- }
-
- /*
- * If the user attempted to dump a specific namespace, check to ensure
- * that the specified namespace actually exists.
- */
- if (selectSchemaName)
- {
- for (i = 0; i < ntups; i++)
- if (strcmp(nsinfo[i].nspname, selectSchemaName) == 0)
- break;
-
- /* Didn't find a match */
- if (i == ntups)
- {
- write_msg(NULL, "specified schema \"%s\" does not exist\n",
- selectSchemaName);
- exit_nicely();
- }
+ nsinfo[i].dobj.name);
}
PQclear(res);
* a system object or not. In 7.3 and later there is no guessing.
*/
static NamespaceInfo *
-findNamespace(const char *nsoid, const char *objoid)
+findNamespace(Oid nsoid, Oid objoid)
{
int i;
{
NamespaceInfo *nsinfo = &g_namespaces[i];
- if (strcmp(nsoid, nsinfo->oid) == 0)
+ if (nsoid == nsinfo->dobj.catId.oid)
return nsinfo;
}
- write_msg(NULL, "schema with OID %s does not exist\n", nsoid);
+ write_msg(NULL, "schema with OID %u does not exist\n", nsoid);
exit_nicely();
}
else
{
/* This code depends on the layout set up by getNamespaces. */
- if (atooid(objoid) > g_last_builtin_oid)
+ if (objoid > g_last_builtin_oid)
i = 0; /* user object */
else
i = 1; /* system object */
* TypeInfo* structure
*
* numTypes is set to the number of types read in
+ *
+ * NB: this must run after getFuncs() because we assume we can do
+ * findFuncByOid().
*/
TypeInfo *
getTypes(int *numTypes)
int i;
PQExpBuffer query = createPQExpBuffer();
TypeInfo *tinfo;
+ ShellTypeInfo *stinfo;
+ int i_tableoid;
int i_oid;
int i_typname;
int i_typnamespace;
- int i_usename;
+ int i_rolname;
+ int i_typinput;
+ int i_typoutput;
int i_typelem;
int i_typrelid;
int i_typrelkind;
int i_typisdefined;
/*
- * we include even the built-in types because those may be used as
- * array elements by user-defined types
+ * we include even the built-in types because those may be used as array
+ * elements by user-defined types
*
* we filter out the built-in types when we dump out the types
*
if (g_fout->remoteVersion >= 70300)
{
- appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
+ appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
"typnamespace, "
- "(select usename from pg_user where typowner = usesysid) as usename, "
- "typelem, typrelid, "
+ "(%s typowner) as rolname, "
+ "typinput::oid as typinput, "
+ "typoutput::oid as typoutput, typelem, typrelid, "
+ "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
+ "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
+ "typtype, typisdefined "
+ "FROM pg_type",
+ username_subquery);
+ }
+ else if (g_fout->remoteVersion >= 70100)
+ {
+ appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
+ "0::oid as typnamespace, "
+ "(%s typowner) as rolname, "
+ "typinput::oid as typinput, "
+ "typoutput::oid as typoutput, typelem, typrelid, "
"CASE WHEN typrelid = 0 THEN ' '::\"char\" "
"ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
"typtype, typisdefined "
- "FROM pg_type");
+ "FROM pg_type",
+ username_subquery);
}
else
{
- appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
+ appendPQExpBuffer(query, "SELECT "
+ "(SELECT oid FROM pg_class WHERE relname = 'pg_type') AS tableoid, "
+ "oid, typname, "
"0::oid as typnamespace, "
- "(select usename from pg_user where typowner = usesysid) as usename, "
- "typelem, typrelid, "
+ "(%s typowner) as rolname, "
+ "typinput::oid as typinput, "
+ "typoutput::oid as typoutput, typelem, typrelid, "
"CASE WHEN typrelid = 0 THEN ' '::\"char\" "
"ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
"typtype, typisdefined "
- "FROM pg_type");
+ "FROM pg_type",
+ username_subquery);
}
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain list of data types failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
tinfo = (TypeInfo *) malloc(ntups * sizeof(TypeInfo));
+ i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_typname = PQfnumber(res, "typname");
i_typnamespace = PQfnumber(res, "typnamespace");
- i_usename = PQfnumber(res, "usename");
+ i_rolname = PQfnumber(res, "rolname");
+ i_typinput = PQfnumber(res, "typinput");
+ i_typoutput = PQfnumber(res, "typoutput");
i_typelem = PQfnumber(res, "typelem");
i_typrelid = PQfnumber(res, "typrelid");
i_typrelkind = PQfnumber(res, "typrelkind");
for (i = 0; i < ntups; i++)
{
- tinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
- tinfo[i].typname = strdup(PQgetvalue(res, i, i_typname));
- tinfo[i].typnamespace = findNamespace(PQgetvalue(res, i, i_typnamespace),
- tinfo[i].oid);
- tinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
- tinfo[i].typelem = strdup(PQgetvalue(res, i, i_typelem));
- tinfo[i].typrelid = strdup(PQgetvalue(res, i, i_typrelid));
+ tinfo[i].dobj.objType = DO_TYPE;
+ tinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ tinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&tinfo[i].dobj);
+ tinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_typname));
+ tinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)),
+ tinfo[i].dobj.catId.oid);
+ tinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
+ tinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
+ tinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
tinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
tinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
+ tinfo[i].shellType = NULL;
+
+ /*
+ * If it's a table's rowtype, use special type code to facilitate
+ * sorting into the desired order. (We don't want to consider it an
+ * ordinary type because that would bring the table up into the
+ * datatype part of the dump order.)
+ */
+ if (OidIsValid(tinfo[i].typrelid) &&
+ tinfo[i].typrelkind != RELKIND_COMPOSITE_TYPE)
+ tinfo[i].dobj.objType = DO_TABLE_TYPE;
/*
* check for user-defined array types, omit system generated ones
*/
- if ((strcmp(tinfo[i].typelem, "0") != 0) &&
- tinfo[i].typname[0] != '_')
+ if (OidIsValid(tinfo[i].typelem) &&
+ tinfo[i].dobj.name[0] != '_')
tinfo[i].isArray = true;
else
tinfo[i].isArray = false;
else
tinfo[i].isDefined = false;
- if (strlen(tinfo[i].usename) == 0 && tinfo[i].isDefined)
+ /* Decide whether we want to dump it */
+ selectDumpableType(&tinfo[i]);
+
+ /*
+ * If it's a domain, fetch info about its constraints, if any
+ */
+ tinfo[i].nDomChecks = 0;
+ tinfo[i].domChecks = NULL;
+ if (tinfo[i].dobj.dump && tinfo[i].typtype == 'd')
+ getDomainConstraints(&(tinfo[i]));
+
+ /*
+ * If it's a base type, make a DumpableObject representing a shell
+ * definition of the type. We will need to dump that ahead of the
+ * I/O functions for the type.
+ *
+ * Note: the shell type doesn't have a catId. You might think it
+ * should copy the base type's catId, but then it might capture
+ * the pg_depend entries for the type, which we don't want.
+ */
+ if (tinfo[i].dobj.dump && tinfo[i].typtype == 'b')
+ {
+ stinfo = (ShellTypeInfo *) malloc(sizeof(ShellTypeInfo));
+ stinfo->dobj.objType = DO_SHELL_TYPE;
+ stinfo->dobj.catId = nilCatalogId;
+ AssignDumpId(&stinfo->dobj);
+ stinfo->dobj.name = strdup(tinfo[i].dobj.name);
+ stinfo->dobj.namespace = tinfo[i].dobj.namespace;
+ stinfo->baseType = &(tinfo[i]);
+ tinfo[i].shellType = stinfo;
+
+ /*
+ * Initially mark the shell type as not to be dumped. We'll
+ * only dump it if the I/O functions need to be dumped; this
+ * is taken care of while sorting dependencies.
+ */
+ stinfo->dobj.dump = false;
+
+ /*
+ * However, if dumping from pre-7.3, there will be no dependency
+ * info so we have to fake it here. We only need to worry about
+ * typinput and typoutput since the other functions only exist
+ * post-7.3.
+ */
+ if (g_fout->remoteVersion < 70300)
+ {
+ Oid typinput;
+ Oid typoutput;
+ FuncInfo *funcInfo;
+
+ typinput = atooid(PQgetvalue(res, i, i_typinput));
+ typoutput = atooid(PQgetvalue(res, i, i_typoutput));
+
+ funcInfo = findFuncByOid(typinput);
+ if (funcInfo && funcInfo->dobj.dump)
+ {
+ /* base type depends on function */
+ addObjectDependency(&tinfo[i].dobj,
+ funcInfo->dobj.dumpId);
+ /* function depends on shell type */
+ addObjectDependency(&funcInfo->dobj,
+ stinfo->dobj.dumpId);
+ /* mark shell type as to be dumped */
+ stinfo->dobj.dump = true;
+ }
+
+ funcInfo = findFuncByOid(typoutput);
+ if (funcInfo && funcInfo->dobj.dump)
+ {
+ /* base type depends on function */
+ addObjectDependency(&tinfo[i].dobj,
+ funcInfo->dobj.dumpId);
+ /* function depends on shell type */
+ addObjectDependency(&funcInfo->dobj,
+ stinfo->dobj.dumpId);
+ /* mark shell type as to be dumped */
+ stinfo->dobj.dump = true;
+ }
+ }
+ }
+
+ if (strlen(tinfo[i].rolname) == 0 && tinfo[i].isDefined)
write_msg(NULL, "WARNING: owner of data type \"%s\" appears to be invalid\n",
- tinfo[i].typname);
+ tinfo[i].dobj.name);
}
*numTypes = ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
OprInfo *oprinfo;
+ int i_tableoid;
int i_oid;
int i_oprname;
int i_oprnamespace;
- int i_usename;
+ int i_rolname;
int i_oprcode;
/*
if (g_fout->remoteVersion >= 70300)
{
- appendPQExpBuffer(query, "SELECT pg_operator.oid, oprname, "
+ appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
"oprnamespace, "
- "(select usename from pg_user where oprowner = usesysid) as usename, "
+ "(%s oprowner) as rolname, "
"oprcode::oid as oprcode "
- "from pg_operator");
+ "FROM pg_operator",
+ username_subquery);
+ }
+ else if (g_fout->remoteVersion >= 70100)
+ {
+ appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
+ "0::oid as oprnamespace, "
+ "(%s oprowner) as rolname, "
+ "oprcode::oid as oprcode "
+ "FROM pg_operator",
+ username_subquery);
}
else
{
- appendPQExpBuffer(query, "SELECT pg_operator.oid, oprname, "
+ appendPQExpBuffer(query, "SELECT "
+ "(SELECT oid FROM pg_class WHERE relname = 'pg_operator') AS tableoid, "
+ "oid, oprname, "
"0::oid as oprnamespace, "
- "(select usename from pg_user where oprowner = usesysid) as usename, "
+ "(%s oprowner) as rolname, "
"oprcode::oid as oprcode "
- "from pg_operator");
+ "FROM pg_operator",
+ username_subquery);
}
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain list of operators failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numOprs = ntups;
oprinfo = (OprInfo *) malloc(ntups * sizeof(OprInfo));
+ i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_oprname = PQfnumber(res, "oprname");
i_oprnamespace = PQfnumber(res, "oprnamespace");
- i_usename = PQfnumber(res, "usename");
+ i_rolname = PQfnumber(res, "rolname");
i_oprcode = PQfnumber(res, "oprcode");
for (i = 0; i < ntups; i++)
{
- oprinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
- oprinfo[i].oprname = strdup(PQgetvalue(res, i, i_oprname));
- oprinfo[i].oprnamespace = findNamespace(PQgetvalue(res, i, i_oprnamespace),
- oprinfo[i].oid);
- oprinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
- oprinfo[i].oprcode = strdup(PQgetvalue(res, i, i_oprcode));
+ oprinfo[i].dobj.objType = DO_OPERATOR;
+ oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&oprinfo[i].dobj);
+ oprinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_oprname));
+ oprinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)),
+ oprinfo[i].dobj.catId.oid);
+ oprinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
+ oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
- if (strlen(oprinfo[i].usename) == 0)
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(oprinfo[i].dobj));
+
+ if (strlen(oprinfo[i].rolname) == 0)
write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n",
- oprinfo[i].oprname);
+ oprinfo[i].dobj.name);
}
PQclear(res);
}
/*
- * getOpclasses:
- * read all opclasses in the system catalogs and return them in the
- * OpclassInfo* structure
+ * getConversions:
+ * read all conversions in the system catalogs and return them in the
+ * ConvInfo* structure
*
- * numOpclasses is set to the number of opclasses read in
+ * numConversions is set to the number of conversions read in
*/
-OpclassInfo *
-getOpclasses(int *numOpclasses)
+ConvInfo *
+getConversions(int *numConversions)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
- OpclassInfo *opcinfo;
+ ConvInfo *convinfo;
+ int i_tableoid;
int i_oid;
- int i_opcname;
- int i_opcnamespace;
- int i_usename;
+ int i_conname;
+ int i_connamespace;
+ int i_rolname;
+
+ /* Conversions didn't exist pre-7.3 */
+ if (g_fout->remoteVersion < 70300)
+ {
+ *numConversions = 0;
+ return NULL;
+ }
/*
- * find all opclasses, including builtin opclasses; we filter out
- * system-defined opclasses at dump-out time.
+ * find all conversions, including builtin conversions; we filter out
+ * system-defined conversions at dump-out time.
*/
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
- if (g_fout->remoteVersion >= 70300)
- {
- appendPQExpBuffer(query, "SELECT pg_opclass.oid, opcname, "
- "opcnamespace, "
- "(select usename from pg_user where opcowner = usesysid) as usename "
- "from pg_opclass");
- }
- else
- {
- appendPQExpBuffer(query, "SELECT pg_opclass.oid, opcname, "
- "0::oid as opcnamespace, "
- "''::name as usename "
- "from pg_opclass");
- }
+ appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
+ "connamespace, "
+ "(%s conowner) as rolname "
+ "FROM pg_conversion",
+ username_subquery);
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain list of operator classes failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
- *numOpclasses = ntups;
+ *numConversions = ntups;
- opcinfo = (OpclassInfo *) malloc(ntups * sizeof(OpclassInfo));
+ convinfo = (ConvInfo *) malloc(ntups * sizeof(ConvInfo));
+ i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
- i_opcname = PQfnumber(res, "opcname");
- i_opcnamespace = PQfnumber(res, "opcnamespace");
- i_usename = PQfnumber(res, "usename");
+ i_conname = PQfnumber(res, "conname");
+ i_connamespace = PQfnumber(res, "connamespace");
+ i_rolname = PQfnumber(res, "rolname");
for (i = 0; i < ntups; i++)
{
- opcinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
- opcinfo[i].opcname = strdup(PQgetvalue(res, i, i_opcname));
- opcinfo[i].opcnamespace = findNamespace(PQgetvalue(res, i, i_opcnamespace),
- opcinfo[i].oid);
- opcinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
+ convinfo[i].dobj.objType = DO_CONVERSION;
+ convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&convinfo[i].dobj);
+ convinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_conname));
+ convinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_connamespace)),
+ convinfo[i].dobj.catId.oid);
+ convinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(convinfo[i].dobj));
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return convinfo;
+}
+
+/*
+ * getOpclasses:
+ * read all opclasses in the system catalogs and return them in the
+ * OpclassInfo* structure
+ *
+ * numOpclasses is set to the number of opclasses read in
+ */
+OpclassInfo *
+getOpclasses(int *numOpclasses)
+{
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ OpclassInfo *opcinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_opcname;
+ int i_opcnamespace;
+ int i_rolname;
+
+ /*
+ * find all opclasses, including builtin opclasses; we filter out
+ * system-defined opclasses at dump-out time.
+ */
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ if (g_fout->remoteVersion >= 70300)
+ {
+ appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
+ "opcnamespace, "
+ "(%s opcowner) as rolname "
+ "FROM pg_opclass",
+ username_subquery);
+ }
+ else if (g_fout->remoteVersion >= 70100)
+ {
+ appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
+ "0::oid as opcnamespace, "
+ "''::name as rolname "
+ "FROM pg_opclass");
+ }
+ else
+ {
+ appendPQExpBuffer(query, "SELECT "
+ "(SELECT oid FROM pg_class WHERE relname = 'pg_opclass') AS tableoid, "
+ "oid, opcname, "
+ "0::oid as opcnamespace, "
+ "''::name as rolname "
+ "FROM pg_opclass");
+ }
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+ *numOpclasses = ntups;
+
+ opcinfo = (OpclassInfo *) malloc(ntups * sizeof(OpclassInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_opcname = PQfnumber(res, "opcname");
+ i_opcnamespace = PQfnumber(res, "opcnamespace");
+ i_rolname = PQfnumber(res, "rolname");
+
+ for (i = 0; i < ntups; i++)
+ {
+ opcinfo[i].dobj.objType = DO_OPCLASS;
+ opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&opcinfo[i].dobj);
+ opcinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_opcname));
+ opcinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)),
+ opcinfo[i].dobj.catId.oid);
+ opcinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(opcinfo[i].dobj));
if (g_fout->remoteVersion >= 70300)
{
- if (strlen(opcinfo[i].usename) == 0)
+ if (strlen(opcinfo[i].rolname) == 0)
write_msg(NULL, "WARNING: owner of operator class \"%s\" appears to be invalid\n",
- opcinfo[i].opcname);
+ opcinfo[i].dobj.name);
}
}
int i;
PQExpBuffer query = createPQExpBuffer();
AggInfo *agginfo;
-
+ int i_tableoid;
int i_oid;
int i_aggname;
int i_aggnamespace;
- int i_aggbasetype;
- int i_usename;
+ int i_pronargs;
+ int i_proargtypes;
+ int i_rolname;
int i_aggacl;
/* Make sure we are in proper schema */
/* find all user-defined aggregates */
- if (g_fout->remoteVersion >= 70300)
+ if (g_fout->remoteVersion >= 80200)
+ {
+ appendPQExpBuffer(query, "SELECT tableoid, oid, proname as aggname, "
+ "pronamespace as aggnamespace, "
+ "pronargs, proargtypes, "
+ "(%s proowner) as rolname, "
+ "proacl as aggacl "
+ "FROM pg_proc "
+ "WHERE proisagg "
+ "AND pronamespace != "
+ "(select oid from pg_namespace where nspname = 'pg_catalog')",
+ username_subquery);
+ }
+ else if (g_fout->remoteVersion >= 70300)
{
- appendPQExpBuffer(query, "SELECT pg_proc.oid, proname as aggname, "
+ appendPQExpBuffer(query, "SELECT tableoid, oid, proname as aggname, "
"pronamespace as aggnamespace, "
- "proargtypes[0] as aggbasetype, "
- "(select usename from pg_user where proowner = usesysid) as usename, "
+ "CASE WHEN proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype THEN 0 ELSE 1 END as pronargs, "
+ "proargtypes, "
+ "(%s proowner) as rolname, "
"proacl as aggacl "
"FROM pg_proc "
"WHERE proisagg "
"AND pronamespace != "
- "(select oid from pg_namespace where nspname = 'pg_catalog')");
+ "(select oid from pg_namespace where nspname = 'pg_catalog')",
+ username_subquery);
+ }
+ else if (g_fout->remoteVersion >= 70100)
+ {
+ appendPQExpBuffer(query, "SELECT tableoid, oid, aggname, "
+ "0::oid as aggnamespace, "
+ "CASE WHEN aggbasetype = 0 THEN 0 ELSE 1 END as pronargs, "
+ "aggbasetype as proargtypes, "
+ "(%s aggowner) as rolname, "
+ "'{=X}' as aggacl "
+ "FROM pg_aggregate "
+ "where oid > '%u'::oid",
+ username_subquery,
+ g_last_builtin_oid);
}
else
{
- appendPQExpBuffer(query, "SELECT pg_aggregate.oid, aggname, "
+ appendPQExpBuffer(query, "SELECT "
+ "(SELECT oid FROM pg_class WHERE relname = 'pg_aggregate') AS tableoid, "
+ "oid, aggname, "
"0::oid as aggnamespace, "
- "aggbasetype, "
- "(select usename from pg_user where aggowner = usesysid) as usename, "
+ "CASE WHEN aggbasetype = 0 THEN 0 ELSE 1 END as pronargs, "
+ "aggbasetype as proargtypes, "
+ "(%s aggowner) as rolname, "
"'{=X}' as aggacl "
- "from pg_aggregate "
+ "FROM pg_aggregate "
"where oid > '%u'::oid",
+ username_subquery,
g_last_builtin_oid);
}
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain list of aggregate functions failed: %s",
- PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numAggs = ntups;
agginfo = (AggInfo *) malloc(ntups * sizeof(AggInfo));
+ i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_aggname = PQfnumber(res, "aggname");
i_aggnamespace = PQfnumber(res, "aggnamespace");
- i_aggbasetype = PQfnumber(res, "aggbasetype");
- i_usename = PQfnumber(res, "usename");
+ i_pronargs = PQfnumber(res, "pronargs");
+ i_proargtypes = PQfnumber(res, "proargtypes");
+ i_rolname = PQfnumber(res, "rolname");
i_aggacl = PQfnumber(res, "aggacl");
for (i = 0; i < ntups; i++)
{
- agginfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
- agginfo[i].aggname = strdup(PQgetvalue(res, i, i_aggname));
- agginfo[i].aggnamespace = findNamespace(PQgetvalue(res, i, i_aggnamespace),
- agginfo[i].oid);
- agginfo[i].aggbasetype = strdup(PQgetvalue(res, i, i_aggbasetype));
- agginfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
- if (strlen(agginfo[i].usename) == 0)
+ agginfo[i].aggfn.dobj.objType = DO_AGG;
+ agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&agginfo[i].aggfn.dobj);
+ agginfo[i].aggfn.dobj.name = strdup(PQgetvalue(res, i, i_aggname));
+ agginfo[i].aggfn.dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)),
+ agginfo[i].aggfn.dobj.catId.oid);
+ agginfo[i].aggfn.rolname = strdup(PQgetvalue(res, i, i_rolname));
+ if (strlen(agginfo[i].aggfn.rolname) == 0)
write_msg(NULL, "WARNING: owner of aggregate function \"%s\" appears to be invalid\n",
- agginfo[i].aggname);
- agginfo[i].aggacl = strdup(PQgetvalue(res, i, i_aggacl));
- agginfo[i].anybasetype = false; /* computed when it's dumped */
- agginfo[i].fmtbasetype = NULL; /* computed when it's dumped */
+ agginfo[i].aggfn.dobj.name);
+ agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
+ agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
+ agginfo[i].aggfn.proacl = strdup(PQgetvalue(res, i, i_aggacl));
+ agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
+ if (agginfo[i].aggfn.nargs == 0)
+ agginfo[i].aggfn.argtypes = NULL;
+ else
+ {
+ agginfo[i].aggfn.argtypes = (Oid *) malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
+ if (g_fout->remoteVersion >= 70300)
+ parseOidArray(PQgetvalue(res, i, i_proargtypes),
+ agginfo[i].aggfn.argtypes,
+ agginfo[i].aggfn.nargs);
+ else /* it's just aggbasetype */
+ agginfo[i].aggfn.argtypes[0] = atooid(PQgetvalue(res, i, i_proargtypes));
+ }
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(agginfo[i].aggfn.dobj));
}
PQclear(res);
int i;
PQExpBuffer query = createPQExpBuffer();
FuncInfo *finfo;
-
+ int i_tableoid;
int i_oid;
int i_proname;
int i_pronamespace;
- int i_usename;
+ int i_rolname;
int i_prolang;
int i_pronargs;
int i_proargtypes;
if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query,
- "SELECT pg_proc.oid, proname, prolang, "
+ "SELECT tableoid, oid, proname, prolang, "
"pronargs, proargtypes, prorettype, proacl, "
"pronamespace, "
- "(select usename from pg_user where proowner = usesysid) as usename "
+ "(%s proowner) as rolname "
"FROM pg_proc "
"WHERE NOT proisagg "
"AND pronamespace != "
- "(select oid from pg_namespace where nspname = 'pg_catalog')");
+ "(select oid from pg_namespace"
+ " where nspname = 'pg_catalog')",
+ username_subquery);
+ }
+ else if (g_fout->remoteVersion >= 70100)
+ {
+ appendPQExpBuffer(query,
+ "SELECT tableoid, oid, proname, prolang, "
+ "pronargs, proargtypes, prorettype, "
+ "'{=X}' as proacl, "
+ "0::oid as pronamespace, "
+ "(%s proowner) as rolname "
+ "FROM pg_proc "
+ "where pg_proc.oid > '%u'::oid",
+ username_subquery,
+ g_last_builtin_oid);
}
else
{
appendPQExpBuffer(query,
- "SELECT pg_proc.oid, proname, prolang, "
+ "SELECT "
+ "(SELECT oid FROM pg_class "
+ " WHERE relname = 'pg_proc') AS tableoid, "
+ "oid, proname, prolang, "
"pronargs, proargtypes, prorettype, "
"'{=X}' as proacl, "
"0::oid as pronamespace, "
- "(select usename from pg_user where proowner = usesysid) as usename "
+ "(%s proowner) as rolname "
"FROM pg_proc "
"where pg_proc.oid > '%u'::oid",
+ username_subquery,
g_last_builtin_oid);
}
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain list of functions failed: %s",
- PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numFuncs = ntups;
- finfo = (FuncInfo *) malloc(ntups * sizeof(FuncInfo));
-
- memset((char *) finfo, 0, ntups * sizeof(FuncInfo));
+ finfo = (FuncInfo *) calloc(ntups, sizeof(FuncInfo));
+ i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_proname = PQfnumber(res, "proname");
i_pronamespace = PQfnumber(res, "pronamespace");
- i_usename = PQfnumber(res, "usename");
+ i_rolname = PQfnumber(res, "rolname");
i_prolang = PQfnumber(res, "prolang");
i_pronargs = PQfnumber(res, "pronargs");
i_proargtypes = PQfnumber(res, "proargtypes");
for (i = 0; i < ntups; i++)
{
- finfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
- finfo[i].proname = strdup(PQgetvalue(res, i, i_proname));
- finfo[i].pronamespace = findNamespace(PQgetvalue(res, i, i_pronamespace),
- finfo[i].oid);
- finfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
+ finfo[i].dobj.objType = DO_FUNC;
+ finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&finfo[i].dobj);
+ finfo[i].dobj.name = strdup(PQgetvalue(res, i, i_proname));
+ finfo[i].dobj.namespace =
+ findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)),
+ finfo[i].dobj.catId.oid);
+ finfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
- finfo[i].prorettype = strdup(PQgetvalue(res, i, i_prorettype));
+ finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
finfo[i].proacl = strdup(PQgetvalue(res, i, i_proacl));
finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
if (finfo[i].nargs == 0)
finfo[i].argtypes = NULL;
else
{
- finfo[i].argtypes = malloc(finfo[i].nargs * sizeof(finfo[i].argtypes[0]));
- parseNumericArray(PQgetvalue(res, i, i_proargtypes),
- finfo[i].argtypes,
- finfo[i].nargs);
+ finfo[i].argtypes = (Oid *) malloc(finfo[i].nargs * sizeof(Oid));
+ parseOidArray(PQgetvalue(res, i, i_proargtypes),
+ finfo[i].argtypes, finfo[i].nargs);
}
- finfo[i].dumped = false;
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(finfo[i].dobj));
- if (strlen(finfo[i].usename) == 0)
- write_msg(NULL, "WARNING: owner of function \"%s\" appears to be invalid\n",
- finfo[i].proname);
+ if (strlen(finfo[i].rolname) == 0)
+ write_msg(NULL,
+ "WARNING: owner of function \"%s\" appears to be invalid\n",
+ finfo[i].dobj.name);
}
PQclear(res);
PQExpBuffer delqry = createPQExpBuffer();
PQExpBuffer lockquery = createPQExpBuffer();
TableInfo *tblinfo;
-
+ int i_reltableoid;
int i_reloid;
int i_relname;
int i_relnamespace;
int i_relkind;
int i_relacl;
- int i_usename;
+ int i_rolname;
int i_relchecks;
int i_reltriggers;
int i_relhasindex;
int i_relhasoids;
int i_owning_tab;
int i_owning_col;
+ int i_reltablespace;
+ int i_reloptions;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
* We include system catalogs, so that we can work if a user table is
* defined to inherit from a system catalog (pretty weird, but...)
*
- * We ignore tables that are not type 'r' (ordinary relation) or 'S'
- * (sequence) or 'v' (view).
+ * We ignore tables that are not type 'r' (ordinary relation), 'S'
+ * (sequence), 'v' (view), or 'c' (composite type).
+ *
+ * Composite-type table entries won't be dumped as such, but we have to
+ * make a DumpableObject for them so that we can track dependencies of the
+ * composite type (pg_depend entries for columns of the composite type
+ * link to the pg_class entry not the pg_type entry).
*
* Note: in this phase we should collect only a minimal amount of
- * information about each table, basically just enough to decide if it
- * is interesting. We must fetch all tables in this phase because
- * otherwise we cannot correctly identify inherited columns, serial
- * columns, etc.
+ * information about each table, basically just enough to decide if it is
+ * interesting. We must fetch all tables in this phase because otherwise
+ * we cannot correctly identify inherited columns, owned sequences, etc.
*/
- if (g_fout->remoteVersion >= 70300)
+ if (g_fout->remoteVersion >= 80200)
{
/*
* Left join to pick up dependency info linking sequences to their
- * serial column, if any
+ * owning column, if any (note this dependency is AUTO as of 8.2)
*/
appendPQExpBuffer(query,
- "SELECT c.oid, relname, relacl, relkind, "
- "relnamespace, "
- "(select usename from pg_user where relowner = usesysid) as usename, "
+ "SELECT c.tableoid, c.oid, relname, "
+ "relacl, relkind, relnamespace, "
+ "(%s relowner) as rolname, "
"relchecks, reltriggers, "
"relhasindex, relhasrules, relhasoids, "
"d.refobjid as owning_tab, "
- "d.refobjsubid as owning_col "
+ "d.refobjsubid as owning_col, "
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
+ "array_to_string(c.reloptions, ', ') as reloptions "
"from pg_class c "
"left join pg_depend d on "
"(c.relkind = '%c' and "
- "d.classid = c.tableoid and d.objid = c.oid and "
+ "d.classid = c.tableoid and d.objid = c.oid and "
"d.objsubid = 0 and "
- "d.refclassid = c.tableoid and d.deptype = 'i') "
- "where relkind in ('%c', '%c', '%c') "
+ "d.refclassid = c.tableoid and d.deptype = 'a') "
+ "where relkind in ('%c', '%c', '%c', '%c') "
+ "order by c.oid",
+ username_subquery,
+ RELKIND_SEQUENCE,
+ RELKIND_RELATION, RELKIND_SEQUENCE,
+ RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
+ }
+ else if (g_fout->remoteVersion >= 80000)
+ {
+ /*
+ * Left join to pick up dependency info linking sequences to their
+ * owning column, if any
+ */
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, relname, "
+ "relacl, relkind, relnamespace, "
+ "(%s relowner) as rolname, "
+ "relchecks, reltriggers, "
+ "relhasindex, relhasrules, relhasoids, "
+ "d.refobjid as owning_tab, "
+ "d.refobjsubid as owning_col, "
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
+ "NULL as reloptions "
+ "from pg_class c "
+ "left join pg_depend d on "
+ "(c.relkind = '%c' and "
+ "d.classid = c.tableoid and d.objid = c.oid and "
+ "d.objsubid = 0 and "
+ "d.refclassid = c.tableoid and d.deptype = 'i') "
+ "where relkind in ('%c', '%c', '%c', '%c') "
+ "order by c.oid",
+ username_subquery,
+ RELKIND_SEQUENCE,
+ RELKIND_RELATION, RELKIND_SEQUENCE,
+ RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
+ }
+ else if (g_fout->remoteVersion >= 70300)
+ {
+ /*
+ * Left join to pick up dependency info linking sequences to their
+ * owning column, if any
+ */
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, relname, "
+ "relacl, relkind, relnamespace, "
+ "(%s relowner) as rolname, "
+ "relchecks, reltriggers, "
+ "relhasindex, relhasrules, relhasoids, "
+ "d.refobjid as owning_tab, "
+ "d.refobjsubid as owning_col, "
+ "NULL as reltablespace, "
+ "NULL as reloptions "
+ "from pg_class c "
+ "left join pg_depend d on "
+ "(c.relkind = '%c' and "
+ "d.classid = c.tableoid and d.objid = c.oid and "
+ "d.objsubid = 0 and "
+ "d.refclassid = c.tableoid and d.deptype = 'i') "
+ "where relkind in ('%c', '%c', '%c', '%c') "
"order by c.oid",
+ username_subquery,
RELKIND_SEQUENCE,
- RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
+ RELKIND_RELATION, RELKIND_SEQUENCE,
+ RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
}
else if (g_fout->remoteVersion >= 70200)
{
appendPQExpBuffer(query,
- "SELECT pg_class.oid, relname, relacl, relkind, "
+ "SELECT tableoid, oid, relname, relacl, relkind, "
"0::oid as relnamespace, "
- "(select usename from pg_user where relowner = usesysid) as usename, "
+ "(%s relowner) as rolname, "
"relchecks, reltriggers, "
"relhasindex, relhasrules, relhasoids, "
"NULL::oid as owning_tab, "
- "NULL::int4 as owning_col "
+ "NULL::int4 as owning_col, "
+ "NULL as reltablespace, "
+ "NULL as reloptions "
"from pg_class "
"where relkind in ('%c', '%c', '%c') "
"order by oid",
- RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
+ username_subquery,
+ RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
}
else if (g_fout->remoteVersion >= 70100)
{
/* all tables have oids in 7.1 */
appendPQExpBuffer(query,
- "SELECT pg_class.oid, relname, relacl, relkind, "
+ "SELECT tableoid, oid, relname, relacl, relkind, "
"0::oid as relnamespace, "
- "(select usename from pg_user where relowner = usesysid) as usename, "
+ "(%s relowner) as rolname, "
"relchecks, reltriggers, "
"relhasindex, relhasrules, "
"'t'::bool as relhasoids, "
"NULL::oid as owning_tab, "
- "NULL::int4 as owning_col "
+ "NULL::int4 as owning_col, "
+ "NULL as reltablespace, "
+ "NULL as reloptions "
"from pg_class "
"where relkind in ('%c', '%c', '%c') "
"order by oid",
- RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
+ username_subquery,
+ RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
}
else
{
/*
- * Before 7.1, view relkind was not set to 'v', so we must check
- * if we have a view by looking for a rule in pg_rewrite.
+ * Before 7.1, view relkind was not set to 'v', so we must check if we
+ * have a view by looking for a rule in pg_rewrite.
*/
appendPQExpBuffer(query,
- "SELECT c.oid, relname, relacl, "
+ "SELECT "
+ "(SELECT oid FROM pg_class WHERE relname = 'pg_class') AS tableoid, "
+ "oid, relname, relacl, "
"CASE WHEN relhasrules and relkind = 'r' "
- " and EXISTS(SELECT rulename FROM pg_rewrite r WHERE "
- " r.ev_class = c.oid AND r.ev_type = '1') "
+ " and EXISTS(SELECT rulename FROM pg_rewrite r WHERE "
+ " r.ev_class = c.oid AND r.ev_type = '1') "
"THEN '%c'::\"char\" "
"ELSE relkind END AS relkind,"
"0::oid as relnamespace, "
- "(select usename from pg_user where relowner = usesysid) as usename, "
+ "(%s relowner) as rolname, "
"relchecks, reltriggers, "
"relhasindex, relhasrules, "
"'t'::bool as relhasoids, "
"NULL::oid as owning_tab, "
- "NULL::int4 as owning_col "
+ "NULL::int4 as owning_col, "
+ "NULL as reltablespace, "
+ "NULL as reloptions "
"from pg_class c "
"where relkind in ('%c', '%c') "
"order by oid",
RELKIND_VIEW,
+ username_subquery,
RELKIND_RELATION, RELKIND_SEQUENCE);
}
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain list of tables failed: %s",
- PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numTables = ntups;
/*
- * Extract data from result and lock dumpable tables. We do the
- * locking before anything else, to minimize the window wherein a
- * table could disappear under us.
+ * Extract data from result and lock dumpable tables. We do the locking
+ * before anything else, to minimize the window wherein a table could
+ * disappear under us.
*
- * Note that we have to save info about all tables here, even when
- * dumping only one, because we don't yet know which tables might be
- * inheritance ancestors of the target table.
+ * Note that we have to save info about all tables here, even when dumping
+ * only one, because we don't yet know which tables might be inheritance
+ * ancestors of the target table.
*/
- tblinfo = (TableInfo *) malloc(ntups * sizeof(TableInfo));
- memset(tblinfo, 0, ntups * sizeof(TableInfo));
+ tblinfo = (TableInfo *) calloc(ntups, sizeof(TableInfo));
+ i_reltableoid = PQfnumber(res, "tableoid");
i_reloid = PQfnumber(res, "oid");
i_relname = PQfnumber(res, "relname");
i_relnamespace = PQfnumber(res, "relnamespace");
i_relacl = PQfnumber(res, "relacl");
i_relkind = PQfnumber(res, "relkind");
- i_usename = PQfnumber(res, "usename");
+ i_rolname = PQfnumber(res, "rolname");
i_relchecks = PQfnumber(res, "relchecks");
i_reltriggers = PQfnumber(res, "reltriggers");
i_relhasindex = PQfnumber(res, "relhasindex");
i_relhasoids = PQfnumber(res, "relhasoids");
i_owning_tab = PQfnumber(res, "owning_tab");
i_owning_col = PQfnumber(res, "owning_col");
+ i_reltablespace = PQfnumber(res, "reltablespace");
+ i_reloptions = PQfnumber(res, "reloptions");
for (i = 0; i < ntups; i++)
{
- tblinfo[i].oid = strdup(PQgetvalue(res, i, i_reloid));
- tblinfo[i].relname = strdup(PQgetvalue(res, i, i_relname));
- tblinfo[i].relnamespace = findNamespace(PQgetvalue(res, i, i_relnamespace),
- tblinfo[i].oid);
- tblinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
+ tblinfo[i].dobj.objType = DO_TABLE;
+ tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
+ tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
+ AssignDumpId(&tblinfo[i].dobj);
+ tblinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_relname));
+ tblinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)),
+ tblinfo[i].dobj.catId.oid);
+ tblinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
tblinfo[i].relacl = strdup(PQgetvalue(res, i, i_relacl));
tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
tblinfo[i].ntrig = atoi(PQgetvalue(res, i, i_reltriggers));
if (PQgetisnull(res, i, i_owning_tab))
{
- tblinfo[i].owning_tab = NULL;
+ tblinfo[i].owning_tab = InvalidOid;
tblinfo[i].owning_col = 0;
}
else
{
- tblinfo[i].owning_tab = strdup(PQgetvalue(res, i, i_owning_tab));
+ tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
}
+ tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace));
+ tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions));
/* other fields were zeroed above */
/*
- * Decide whether we want to dump this table. Sequences owned by
- * serial columns are never dumpable on their own; we will
- * transpose their owning table's dump flag to them below.
+ * Decide whether we want to dump this table.
*/
- if (tblinfo[i].owning_tab == NULL)
- selectDumpableTable(&tblinfo[i]);
+ if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
+ tblinfo[i].dobj.dump = false;
else
- tblinfo[i].dump = false;
- tblinfo[i].interesting = tblinfo[i].dump;
+ selectDumpableTable(&tblinfo[i]);
+ tblinfo[i].interesting = tblinfo[i].dobj.dump;
/*
- * Read-lock target tables to make sure they aren't DROPPED or
- * altered in schema before we get around to dumping them.
+ * Read-lock target tables to make sure they aren't DROPPED or altered
+ * in schema before we get around to dumping them.
*
- * Note that we don't explicitly lock parents of the target tables;
- * we assume our lock on the child is enough to prevent schema
+ * Note that we don't explicitly lock parents of the target tables; we
+ * assume our lock on the child is enough to prevent schema
* alterations to parent tables.
*
* NOTE: it'd be kinda nice to lock views and sequences too, not only
* plain tables, but the backend doesn't presently allow that.
*/
- if (tblinfo[i].dump && tblinfo[i].relkind == RELKIND_RELATION)
+ if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION)
{
- PGresult *lres;
-
resetPQExpBuffer(lockquery);
appendPQExpBuffer(lockquery,
"LOCK TABLE %s IN ACCESS SHARE MODE",
- fmtQualifiedId(tblinfo[i].relnamespace->nspname,
- tblinfo[i].relname));
- lres = PQexec(g_conn, lockquery->data);
- if (!lres || PQresultStatus(lres) != PGRES_COMMAND_OK)
- {
- write_msg(NULL, "attempt to lock table \"%s\" failed: %s",
- tblinfo[i].relname, PQerrorMessage(g_conn));
- exit_nicely();
- }
- PQclear(lres);
+ fmtQualifiedId(tblinfo[i].dobj.namespace->dobj.name,
+ tblinfo[i].dobj.name));
+ do_sql_command(g_conn, lockquery->data);
}
/* Emit notice if join for owner failed */
- if (strlen(tblinfo[i].usename) == 0)
+ if (strlen(tblinfo[i].rolname) == 0)
write_msg(NULL, "WARNING: owner of table \"%s\" appears to be invalid\n",
- tblinfo[i].relname);
+ tblinfo[i].dobj.name);
}
+ PQclear(res);
+
/*
- * If the user is attempting to dump a specific table, check to ensure
- * that the specified table actually exists (and is a table or a view,
- * not a sequence).
+ * Force sequences that are "owned" by table columns to be dumped
+ * whenever their owning table is being dumped.
*/
- if (selectTableName)
+ for (i = 0; i < ntups; i++)
{
- for (i = 0; i < ntups; i++)
- if (strcmp(tblinfo[i].relname, selectTableName) == 0 &&
- tblinfo[i].relkind != 'S')
- break;
+ TableInfo *seqinfo = &tblinfo[i];
+ int j;
- /* Didn't find a match */
- if (i == ntups)
+ if (!OidIsValid(seqinfo->owning_tab))
+ continue; /* not an owned sequence */
+ if (seqinfo->dobj.dump)
+ continue; /* no need to search */
+
+ /* can't use findTableByOid yet, unfortunately */
+ for (j = 0; j < ntups; j++)
{
- write_msg(NULL, "specified table \"%s\" does not exist\n",
- selectTableName);
- exit_nicely();
+ if (tblinfo[j].dobj.catId.oid == seqinfo->owning_tab)
+ {
+ if (tblinfo[j].dobj.dump)
+ {
+ seqinfo->interesting = true;
+ seqinfo->dobj.dump = true;
+ }
+ break;
+ }
}
}
- PQclear(res);
destroyPQExpBuffer(query);
destroyPQExpBuffer(delqry);
destroyPQExpBuffer(lockquery);
appendPQExpBuffer(query, "SELECT inhrelid, inhparent from pg_inherits");
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain inheritance relationships failed: %s",
- PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
for (i = 0; i < ntups; i++)
{
- inhinfo[i].inhrelid = strdup(PQgetvalue(res, i, i_inhrelid));
- inhinfo[i].inhparent = strdup(PQgetvalue(res, i, i_inhparent));
+ inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
+ inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
}
PQclear(res);
}
/*
- * getTableAttrs -
- * for each interesting table, read its attributes types and names
- *
- * this is implemented in a very inefficient way right now, looping
- * through the tblinfo and doing a join per table to find the attrs and their
- * types
+ * getIndexes
+ * get information about every index on a dumpable table
*
- * modifies tblinfo
+ * Note: index data is not returned directly to the caller, but it
+ * does get entered into the DumpableObject tables.
*/
void
-getTableAttrs(TableInfo *tblinfo, int numTables)
+getIndexes(TableInfo tblinfo[], int numTables)
{
int i,
- j,
- k;
- PQExpBuffer q = createPQExpBuffer();
- int i_attnum;
- int i_attname;
- int i_atttypname;
- int i_atttypmod;
- int i_attstattarget;
- int i_attstorage;
- int i_typstorage;
- int i_attnotnull;
- int i_atthasdef;
- int i_attisdropped;
- int i_attislocal;
+ j;
+ PQExpBuffer query = createPQExpBuffer();
PGresult *res;
+ IndxInfo *indxinfo;
+ ConstraintInfo *constrinfo;
+ int i_tableoid,
+ i_oid,
+ i_indexname,
+ i_indexdef,
+ i_indnkeys,
+ i_indkey,
+ i_indisclustered,
+ i_contype,
+ i_conname,
+ i_contableoid,
+ i_conoid,
+ i_tablespace,
+ i_options;
int ntups;
- bool hasdefaults;
for (i = 0; i < numTables; i++)
{
TableInfo *tbinfo = &tblinfo[i];
- /* Don't bother to collect info for sequences */
- if (tbinfo->relkind == RELKIND_SEQUENCE)
+ /* Only plain tables have indexes */
+ if (tbinfo->relkind != RELKIND_RELATION || !tbinfo->hasindex)
continue;
- /* Don't bother with uninteresting tables, either */
- if (!tbinfo->interesting)
+ /* Ignore indexes of tables not to be dumped */
+ if (!tbinfo->dobj.dump)
continue;
- /*
- * Make sure we are in proper schema for this table; this allows
- * correct retrieval of formatted type names and default exprs
- */
- selectSourceSchema(tbinfo->relnamespace->nspname);
+ if (g_verbose)
+ write_msg(NULL, "reading indexes for table \"%s\"\n",
+ tbinfo->dobj.name);
- /* find all the user attributes and their types */
+ /* Make sure we are in proper schema so indexdef is right */
+ selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
/*
- * we must read the attribute names in attribute number order!
- * because we will use the attnum to index into the attnames array
- * later. We actually ask to order by "attrelid, attnum" because
- * (at least up to 7.3) the planner is not smart enough to realize
- * it needn't re-sort the output of an indexscan on
- * pg_attribute_relid_attnum_index.
+ * The point of the messy-looking outer join is to find a constraint
+ * that is related by an internal dependency link to the index. If we
+ * find one, create a CONSTRAINT entry linked to the INDEX entry. We
+ * assume an index won't have more than one internal dependency.
*/
- if (g_verbose)
- write_msg(NULL, "finding the columns and types of table \"%s\"\n",
- tbinfo->relname);
-
- resetPQExpBuffer(q);
-
- if (g_fout->remoteVersion >= 70300)
- {
- /* need left join here to not fail on dropped columns ... */
- appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, a.attstattarget, a.attstorage, t.typstorage, "
- "a.attnotnull, a.atthasdef, a.attisdropped, a.attislocal, "
- "pg_catalog.format_type(t.oid,a.atttypmod) as atttypname "
- "from pg_catalog.pg_attribute a left join pg_catalog.pg_type t "
- "on a.atttypid = t.oid "
- "where a.attrelid = '%s'::pg_catalog.oid "
- "and a.attnum > 0::pg_catalog.int2 "
- "order by a.attrelid, a.attnum",
- tbinfo->oid);
- }
- else if (g_fout->remoteVersion >= 70100)
+ resetPQExpBuffer(query);
+ if (g_fout->remoteVersion >= 80200)
{
- /*
- * attstattarget doesn't exist in 7.1. It does exist in 7.2,
- * but we don't dump it because we can't tell whether it's
- * been explicitly set or was just a default.
- */
- appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, -1 as attstattarget, a.attstorage, t.typstorage, "
- "a.attnotnull, a.atthasdef, false as attisdropped, null as attislocal, "
- "format_type(t.oid,a.atttypmod) as atttypname "
- "from pg_attribute a left join pg_type t "
- "on a.atttypid = t.oid "
- "where a.attrelid = '%s'::oid "
- "and a.attnum > 0::int2 "
- "order by a.attrelid, a.attnum",
- tbinfo->oid);
+ appendPQExpBuffer(query,
+ "SELECT t.tableoid, t.oid, "
+ "t.relname as indexname, "
+ "pg_catalog.pg_get_indexdef(i.indexrelid) as indexdef, "
+ "t.relnatts as indnkeys, "
+ "i.indkey, i.indisclustered, "
+ "c.contype, c.conname, "
+ "c.tableoid as contableoid, "
+ "c.oid as conoid, "
+ "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) as tablespace, "
+ "array_to_string(t.reloptions, ', ') as options "
+ "FROM pg_catalog.pg_index i "
+ "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
+ "LEFT JOIN pg_catalog.pg_depend d "
+ "ON (d.classid = t.tableoid "
+ "AND d.objid = t.oid "
+ "AND d.deptype = 'i') "
+ "LEFT JOIN pg_catalog.pg_constraint c "
+ "ON (d.refclassid = c.tableoid "
+ "AND d.refobjid = c.oid) "
+ "WHERE i.indrelid = '%u'::pg_catalog.oid "
+ "ORDER BY indexname",
+ tbinfo->dobj.catId.oid);
}
- else
+ else if (g_fout->remoteVersion >= 80000)
{
- /* format_type not available before 7.1 */
- appendPQExpBuffer(q, "SELECT attnum, attname, atttypmod, -1 as attstattarget, attstorage, attstorage as typstorage, "
- "attnotnull, atthasdef, false as attisdropped, null as attislocal, "
- "(select typname from pg_type where oid = atttypid) as atttypname "
- "from pg_attribute a "
- "where attrelid = '%s'::oid "
- "and attnum > 0::int2 "
- "order by attrelid, attnum",
- tbinfo->oid);
+ appendPQExpBuffer(query,
+ "SELECT t.tableoid, t.oid, "
+ "t.relname as indexname, "
+ "pg_catalog.pg_get_indexdef(i.indexrelid) as indexdef, "
+ "t.relnatts as indnkeys, "
+ "i.indkey, i.indisclustered, "
+ "c.contype, c.conname, "
+ "c.tableoid as contableoid, "
+ "c.oid as conoid, "
+ "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) as tablespace, "
+ "null as options "
+ "FROM pg_catalog.pg_index i "
+ "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
+ "LEFT JOIN pg_catalog.pg_depend d "
+ "ON (d.classid = t.tableoid "
+ "AND d.objid = t.oid "
+ "AND d.deptype = 'i') "
+ "LEFT JOIN pg_catalog.pg_constraint c "
+ "ON (d.refclassid = c.tableoid "
+ "AND d.refobjid = c.oid) "
+ "WHERE i.indrelid = '%u'::pg_catalog.oid "
+ "ORDER BY indexname",
+ tbinfo->dobj.catId.oid);
}
-
- res = PQexec(g_conn, q->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
+ else if (g_fout->remoteVersion >= 70300)
{
- write_msg(NULL, "query to get table columns failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ appendPQExpBuffer(query,
+ "SELECT t.tableoid, t.oid, "
+ "t.relname as indexname, "
+ "pg_catalog.pg_get_indexdef(i.indexrelid) as indexdef, "
+ "t.relnatts as indnkeys, "
+ "i.indkey, i.indisclustered, "
+ "c.contype, c.conname, "
+ "c.tableoid as contableoid, "
+ "c.oid as conoid, "
+ "NULL as tablespace, "
+ "null as options "
+ "FROM pg_catalog.pg_index i "
+ "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
+ "LEFT JOIN pg_catalog.pg_depend d "
+ "ON (d.classid = t.tableoid "
+ "AND d.objid = t.oid "
+ "AND d.deptype = 'i') "
+ "LEFT JOIN pg_catalog.pg_constraint c "
+ "ON (d.refclassid = c.tableoid "
+ "AND d.refobjid = c.oid) "
+ "WHERE i.indrelid = '%u'::pg_catalog.oid "
+ "ORDER BY indexname",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (g_fout->remoteVersion >= 70100)
+ {
+ appendPQExpBuffer(query,
+ "SELECT t.tableoid, t.oid, "
+ "t.relname as indexname, "
+ "pg_get_indexdef(i.indexrelid) as indexdef, "
+ "t.relnatts as indnkeys, "
+ "i.indkey, false as indisclustered, "
+ "CASE WHEN i.indisprimary THEN 'p'::char "
+ "ELSE '0'::char END as contype, "
+ "t.relname as conname, "
+ "0::oid as contableoid, "
+ "t.oid as conoid, "
+ "NULL as tablespace, "
+ "null as options "
+ "FROM pg_index i, pg_class t "
+ "WHERE t.oid = i.indexrelid "
+ "AND i.indrelid = '%u'::oid "
+ "ORDER BY indexname",
+ tbinfo->dobj.catId.oid);
+ }
+ else
+ {
+ appendPQExpBuffer(query,
+ "SELECT "
+ "(SELECT oid FROM pg_class WHERE relname = 'pg_class') AS tableoid, "
+ "t.oid, "
+ "t.relname as indexname, "
+ "pg_get_indexdef(i.indexrelid) as indexdef, "
+ "t.relnatts as indnkeys, "
+ "i.indkey, false as indisclustered, "
+ "CASE WHEN i.indisprimary THEN 'p'::char "
+ "ELSE '0'::char END as contype, "
+ "t.relname as conname, "
+ "0::oid as contableoid, "
+ "t.oid as conoid, "
+ "NULL as tablespace, "
+ "null as options "
+ "FROM pg_index i, pg_class t "
+ "WHERE t.oid = i.indexrelid "
+ "AND i.indrelid = '%u'::oid "
+ "ORDER BY indexname",
+ tbinfo->dobj.catId.oid);
+ }
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_indexname = PQfnumber(res, "indexname");
+ i_indexdef = PQfnumber(res, "indexdef");
+ i_indnkeys = PQfnumber(res, "indnkeys");
+ i_indkey = PQfnumber(res, "indkey");
+ i_indisclustered = PQfnumber(res, "indisclustered");
+ i_contype = PQfnumber(res, "contype");
+ i_conname = PQfnumber(res, "conname");
+ i_contableoid = PQfnumber(res, "contableoid");
+ i_conoid = PQfnumber(res, "conoid");
+ i_tablespace = PQfnumber(res, "tablespace");
+ i_options = PQfnumber(res, "options");
+
+ indxinfo = (IndxInfo *) malloc(ntups * sizeof(IndxInfo));
+ constrinfo = (ConstraintInfo *) malloc(ntups * sizeof(ConstraintInfo));
+
+ for (j = 0; j < ntups; j++)
+ {
+ char contype;
+
+ indxinfo[j].dobj.objType = DO_INDEX;
+ indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
+ indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
+ AssignDumpId(&indxinfo[j].dobj);
+ indxinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_indexname));
+ indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
+ indxinfo[j].indextable = tbinfo;
+ indxinfo[j].indexdef = strdup(PQgetvalue(res, j, i_indexdef));
+ indxinfo[j].indnkeys = atoi(PQgetvalue(res, j, i_indnkeys));
+ indxinfo[j].tablespace = strdup(PQgetvalue(res, j, i_tablespace));
+ indxinfo[j].options = strdup(PQgetvalue(res, j, i_options));
+
+ /*
+ * In pre-7.4 releases, indkeys may contain more entries than
+ * indnkeys says (since indnkeys will be 1 for a functional
+ * index). We don't actually care about this case since we don't
+ * examine indkeys except for indexes associated with PRIMARY and
+ * UNIQUE constraints, which are never functional indexes. But we
+ * have to allocate enough space to keep parseOidArray from
+ * complaining.
+ */
+ indxinfo[j].indkeys = (Oid *) malloc(INDEX_MAX_KEYS * sizeof(Oid));
+ parseOidArray(PQgetvalue(res, j, i_indkey),
+ indxinfo[j].indkeys, INDEX_MAX_KEYS);
+ indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
+ contype = *(PQgetvalue(res, j, i_contype));
+
+ if (contype == 'p' || contype == 'u')
+ {
+ /*
+ * If we found a constraint matching the index, create an
+ * entry for it.
+ *
+ * In a pre-7.3 database, we take this path iff the index was
+ * marked indisprimary.
+ */
+ constrinfo[j].dobj.objType = DO_CONSTRAINT;
+ constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
+ constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
+ AssignDumpId(&constrinfo[j].dobj);
+ constrinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_conname));
+ constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
+ constrinfo[j].contable = tbinfo;
+ constrinfo[j].condomain = NULL;
+ constrinfo[j].contype = contype;
+ constrinfo[j].condef = NULL;
+ constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
+ constrinfo[j].coninherited = false;
+ constrinfo[j].separate = true;
+
+ indxinfo[j].indexconstraint = constrinfo[j].dobj.dumpId;
+
+ /* If pre-7.3 DB, better make sure table comes first */
+ addObjectDependency(&constrinfo[j].dobj,
+ tbinfo->dobj.dumpId);
+ }
+ else
+ {
+ /* Plain secondary index */
+ indxinfo[j].indexconstraint = 0;
+ }
+ }
+
+ PQclear(res);
+ }
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * getConstraints
+ *
+ * Get info about constraints on dumpable tables.
+ *
+ * Currently handles foreign keys only.
+ * Unique and primary key constraints are handled with indexes,
+ * while check constraints are processed in getTableAttrs().
+ */
+void
+getConstraints(TableInfo tblinfo[], int numTables)
+{
+ int i,
+ j;
+ ConstraintInfo *constrinfo;
+ PQExpBuffer query;
+ PGresult *res;
+ int i_condef,
+ i_contableoid,
+ i_conoid,
+ i_conname;
+ int ntups;
+
+ /* pg_constraint was created in 7.3, so nothing to do if older */
+ if (g_fout->remoteVersion < 70300)
+ return;
+
+ query = createPQExpBuffer();
+
+ for (i = 0; i < numTables; i++)
+ {
+ TableInfo *tbinfo = &tblinfo[i];
+
+ if (tbinfo->ntrig == 0 || !tbinfo->dobj.dump)
+ continue;
+
+ if (g_verbose)
+ write_msg(NULL, "reading foreign key constraints for table \"%s\"\n",
+ tbinfo->dobj.name);
+
+ /*
+ * select table schema to ensure constraint expr is qualified if
+ * needed
+ */
+ selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
+
+ resetPQExpBuffer(query);
+ appendPQExpBuffer(query,
+ "SELECT tableoid, oid, conname, "
+ "pg_catalog.pg_get_constraintdef(oid) as condef "
+ "FROM pg_catalog.pg_constraint "
+ "WHERE conrelid = '%u'::pg_catalog.oid "
+ "AND contype = 'f'",
+ tbinfo->dobj.catId.oid);
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_contableoid = PQfnumber(res, "tableoid");
+ i_conoid = PQfnumber(res, "oid");
+ i_conname = PQfnumber(res, "conname");
+ i_condef = PQfnumber(res, "condef");
+
+ constrinfo = (ConstraintInfo *) malloc(ntups * sizeof(ConstraintInfo));
+
+ for (j = 0; j < ntups; j++)
+ {
+ constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
+ constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
+ constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
+ AssignDumpId(&constrinfo[j].dobj);
+ constrinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_conname));
+ constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
+ constrinfo[j].contable = tbinfo;
+ constrinfo[j].condomain = NULL;
+ constrinfo[j].contype = 'f';
+ constrinfo[j].condef = strdup(PQgetvalue(res, j, i_condef));
+ constrinfo[j].conindex = 0;
+ constrinfo[j].coninherited = false;
+ constrinfo[j].separate = true;
+ }
+
+ PQclear(res);
+ }
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * getDomainConstraints
+ *
+ * Get info about constraints on a domain.
+ */
+static void
+getDomainConstraints(TypeInfo *tinfo)
+{
+ int i;
+ ConstraintInfo *constrinfo;
+ PQExpBuffer query;
+ PGresult *res;
+ int i_tableoid,
+ i_oid,
+ i_conname,
+ i_consrc;
+ int ntups;
+
+ /* pg_constraint was created in 7.3, so nothing to do if older */
+ if (g_fout->remoteVersion < 70300)
+ return;
+
+ /*
+ * select appropriate schema to ensure names in constraint are properly
+ * qualified
+ */
+ selectSourceSchema(tinfo->dobj.namespace->dobj.name);
+
+ query = createPQExpBuffer();
+
+ if (g_fout->remoteVersion >= 70400)
+ appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
+ "pg_catalog.pg_get_constraintdef(oid) AS consrc "
+ "FROM pg_catalog.pg_constraint "
+ "WHERE contypid = '%u'::pg_catalog.oid "
+ "ORDER BY conname",
+ tinfo->dobj.catId.oid);
+ else
+ appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
+ "'CHECK (' || consrc || ')' AS consrc "
+ "FROM pg_catalog.pg_constraint "
+ "WHERE contypid = '%u'::pg_catalog.oid "
+ "ORDER BY conname",
+ tinfo->dobj.catId.oid);
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_conname = PQfnumber(res, "conname");
+ i_consrc = PQfnumber(res, "consrc");
+
+ constrinfo = (ConstraintInfo *) malloc(ntups * sizeof(ConstraintInfo));
+
+ tinfo->nDomChecks = ntups;
+ tinfo->domChecks = constrinfo;
+
+ for (i = 0; i < ntups; i++)
+ {
+ constrinfo[i].dobj.objType = DO_CONSTRAINT;
+ constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&constrinfo[i].dobj);
+ constrinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_conname));
+ constrinfo[i].dobj.namespace = tinfo->dobj.namespace;
+ constrinfo[i].contable = NULL;
+ constrinfo[i].condomain = tinfo;
+ constrinfo[i].contype = 'c';
+ constrinfo[i].condef = strdup(PQgetvalue(res, i, i_consrc));
+ constrinfo[i].conindex = 0;
+ constrinfo[i].coninherited = false;
+ constrinfo[i].separate = false;
+
+ /*
+ * Make the domain depend on the constraint, ensuring it won't be
+ * output till any constraint dependencies are OK.
+ */
+ addObjectDependency(&tinfo->dobj,
+ constrinfo[i].dobj.dumpId);
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * getRules
+ * get basic information about every rule in the system
+ *
+ * numRules is set to the number of rules read in
+ */
+RuleInfo *
+getRules(int *numRules)
+{
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ RuleInfo *ruleinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_rulename;
+ int i_ruletable;
+ int i_ev_type;
+ int i_is_instead;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ if (g_fout->remoteVersion >= 70100)
+ {
+ appendPQExpBuffer(query, "SELECT "
+ "tableoid, oid, rulename, "
+ "ev_class as ruletable, ev_type, is_instead "
+ "FROM pg_rewrite "
+ "ORDER BY oid");
+ }
+ else
+ {
+ appendPQExpBuffer(query, "SELECT "
+ "(SELECT oid FROM pg_class WHERE relname = 'pg_rewrite') AS tableoid, "
+ "oid, rulename, "
+ "ev_class as ruletable, ev_type, is_instead "
+ "FROM pg_rewrite "
+ "ORDER BY oid");
+ }
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ *numRules = ntups;
+
+ ruleinfo = (RuleInfo *) malloc(ntups * sizeof(RuleInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_rulename = PQfnumber(res, "rulename");
+ i_ruletable = PQfnumber(res, "ruletable");
+ i_ev_type = PQfnumber(res, "ev_type");
+ i_is_instead = PQfnumber(res, "is_instead");
+
+ for (i = 0; i < ntups; i++)
+ {
+ Oid ruletableoid;
+
+ ruleinfo[i].dobj.objType = DO_RULE;
+ ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&ruleinfo[i].dobj);
+ ruleinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_rulename));
+ ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
+ ruleinfo[i].ruletable = findTableByOid(ruletableoid);
+ if (ruleinfo[i].ruletable == NULL)
+ {
+ write_msg(NULL, "failed sanity check, parent table OID %u of pg_rewrite entry OID %u not found\n",
+ ruletableoid,
+ ruleinfo[i].dobj.catId.oid);
+ exit_nicely();
+ }
+ ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
+ ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
+ ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
+ ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
+ if (ruleinfo[i].ruletable)
+ {
+ /*
+ * If the table is a view, force its ON SELECT rule to be sorted
+ * before the view itself --- this ensures that any dependencies
+ * for the rule affect the table's positioning. Other rules are
+ * forced to appear after their table.
+ */
+ if (ruleinfo[i].ruletable->relkind == RELKIND_VIEW &&
+ ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
+ {
+ addObjectDependency(&ruleinfo[i].ruletable->dobj,
+ ruleinfo[i].dobj.dumpId);
+ /* We'll merge the rule into CREATE VIEW, if possible */
+ ruleinfo[i].separate = false;
+ }
+ else
+ {
+ addObjectDependency(&ruleinfo[i].dobj,
+ ruleinfo[i].ruletable->dobj.dumpId);
+ ruleinfo[i].separate = true;
+ }
+ }
+ else
+ ruleinfo[i].separate = true;
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return ruleinfo;
+}
+
+/*
+ * getTriggers
+ * get information about every trigger on a dumpable table
+ *
+ * Note: trigger data is not returned directly to the caller, but it
+ * does get entered into the DumpableObject tables.
+ */
+void
+getTriggers(TableInfo tblinfo[], int numTables)
+{
+ int i,
+ j;
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+ TriggerInfo *tginfo;
+ int i_tableoid,
+ i_oid,
+ i_tgname,
+ i_tgfname,
+ i_tgtype,
+ i_tgnargs,
+ i_tgargs,
+ i_tgisconstraint,
+ i_tgconstrname,
+ i_tgconstrrelid,
+ i_tgconstrrelname,
+ i_tgenabled,
+ i_tgdeferrable,
+ i_tginitdeferred;
+ int ntups;
+
+ for (i = 0; i < numTables; i++)
+ {
+ TableInfo *tbinfo = &tblinfo[i];
+
+ if (tbinfo->ntrig == 0 || !tbinfo->dobj.dump)
+ continue;
+
+ if (g_verbose)
+ write_msg(NULL, "reading triggers for table \"%s\"\n",
+ tbinfo->dobj.name);
+
+ /*
+ * select table schema to ensure regproc name is qualified if needed
+ */
+ selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
+
+ resetPQExpBuffer(query);
+ if (g_fout->remoteVersion >= 70300)
+ {
+ /*
+ * We ignore triggers that are tied to a foreign-key constraint
+ */
+ appendPQExpBuffer(query,
+ "SELECT tgname, "
+ "tgfoid::pg_catalog.regproc as tgfname, "
+ "tgtype, tgnargs, tgargs, tgenabled, "
+ "tgisconstraint, tgconstrname, tgdeferrable, "
+ "tgconstrrelid, tginitdeferred, tableoid, oid, "
+ "tgconstrrelid::pg_catalog.regclass as tgconstrrelname "
+ "from pg_catalog.pg_trigger t "
+ "where tgrelid = '%u'::pg_catalog.oid "
+ "and (not tgisconstraint "
+ " OR NOT EXISTS"
+ " (SELECT 1 FROM pg_catalog.pg_depend d "
+ " JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
+ " WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (g_fout->remoteVersion >= 70100)
+ {
+ appendPQExpBuffer(query,
+ "SELECT tgname, tgfoid::regproc as tgfname, "
+ "tgtype, tgnargs, tgargs, tgenabled, "
+ "tgisconstraint, tgconstrname, tgdeferrable, "
+ "tgconstrrelid, tginitdeferred, tableoid, oid, "
+ "(select relname from pg_class where oid = tgconstrrelid) "
+ " as tgconstrrelname "
+ "from pg_trigger "
+ "where tgrelid = '%u'::oid",
+ tbinfo->dobj.catId.oid);
+ }
+ else
+ {
+ appendPQExpBuffer(query,
+ "SELECT tgname, tgfoid::regproc as tgfname, "
+ "tgtype, tgnargs, tgargs, tgenabled, "
+ "tgisconstraint, tgconstrname, tgdeferrable, "
+ "tgconstrrelid, tginitdeferred, "
+ "(SELECT oid FROM pg_class WHERE relname = 'pg_trigger') AS tableoid, "
+
+ "oid, "
+ "(select relname from pg_class where oid = tgconstrrelid) "
+ " as tgconstrrelname "
+ "from pg_trigger "
+ "where tgrelid = '%u'::oid",
+ tbinfo->dobj.catId.oid);
+ }
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ /*
+ * We may have less triggers than recorded due to having ignored
+ * foreign-key triggers
+ */
+ if (ntups > tbinfo->ntrig)
+ {
+ write_msg(NULL, "expected %d triggers on table \"%s\" but found %d\n",
+ tbinfo->ntrig, tbinfo->dobj.name, ntups);
+ exit_nicely();
+ }
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_tgname = PQfnumber(res, "tgname");
+ i_tgfname = PQfnumber(res, "tgfname");
+ i_tgtype = PQfnumber(res, "tgtype");
+ i_tgnargs = PQfnumber(res, "tgnargs");
+ i_tgargs = PQfnumber(res, "tgargs");
+ i_tgisconstraint = PQfnumber(res, "tgisconstraint");
+ i_tgconstrname = PQfnumber(res, "tgconstrname");
+ i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
+ i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
+ i_tgenabled = PQfnumber(res, "tgenabled");
+ i_tgdeferrable = PQfnumber(res, "tgdeferrable");
+ i_tginitdeferred = PQfnumber(res, "tginitdeferred");
+
+ tginfo = (TriggerInfo *) malloc(ntups * sizeof(TriggerInfo));
+
+ for (j = 0; j < ntups; j++)
+ {
+ tginfo[j].dobj.objType = DO_TRIGGER;
+ tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
+ tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
+ AssignDumpId(&tginfo[j].dobj);
+ tginfo[j].dobj.name = strdup(PQgetvalue(res, j, i_tgname));
+ tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
+ tginfo[j].tgtable = tbinfo;
+ tginfo[j].tgfname = strdup(PQgetvalue(res, j, i_tgfname));
+ tginfo[j].tgtype = atoi(PQgetvalue(res, j, i_tgtype));
+ tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
+ tginfo[j].tgargs = strdup(PQgetvalue(res, j, i_tgargs));
+ tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't';
+ tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled)) == 't';
+ tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't';
+ tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't';
+
+ if (tginfo[j].tgisconstraint)
+ {
+ tginfo[j].tgconstrname = strdup(PQgetvalue(res, j, i_tgconstrname));
+ tginfo[j].tgconstrrelid = atooid(PQgetvalue(res, j, i_tgconstrrelid));
+ if (OidIsValid(tginfo[j].tgconstrrelid))
+ {
+ if (PQgetisnull(res, j, i_tgconstrrelname))
+ {
+ write_msg(NULL, "query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)\n",
+ tginfo[j].dobj.name, tbinfo->dobj.name,
+ tginfo[j].tgconstrrelid);
+ exit_nicely();
+ }
+ tginfo[j].tgconstrrelname = strdup(PQgetvalue(res, j, i_tgconstrrelname));
+ }
+ else
+ tginfo[j].tgconstrrelname = NULL;
+ }
+ else
+ {
+ tginfo[j].tgconstrname = NULL;
+ tginfo[j].tgconstrrelid = InvalidOid;
+ tginfo[j].tgconstrrelname = NULL;
+ }
+ }
+
+ PQclear(res);
+ }
+
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * getProcLangs
+ * get basic information about every procedural language in the system
+ *
+ * numProcLangs is set to the number of langs read in
+ *
+ * NB: this must run after getFuncs() because we assume we can do
+ * findFuncByOid().
+ */
+ProcLangInfo *
+getProcLangs(int *numProcLangs)
+{
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ ProcLangInfo *planginfo;
+ int i_tableoid;
+ int i_oid;
+ int i_lanname;
+ int i_lanpltrusted;
+ int i_lanplcallfoid;
+ int i_lanvalidator;
+ int i_lanacl;
+ int i_lanowner;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ if (g_fout->remoteVersion >= 80100)
+ {
+ /* Languages are owned by the bootstrap superuser, OID 10 */
+ appendPQExpBuffer(query, "SELECT tableoid, oid, *, "
+ "(%s '10') as lanowner "
+ "FROM pg_language "
+ "WHERE lanispl "
+ "ORDER BY oid",
+ username_subquery);
+ }
+ else if (g_fout->remoteVersion >= 70400)
+ {
+ /* Languages are owned by the bootstrap superuser, sysid 1 */
+ appendPQExpBuffer(query, "SELECT tableoid, oid, *, "
+ "(%s '1') as lanowner "
+ "FROM pg_language "
+ "WHERE lanispl "
+ "ORDER BY oid",
+ username_subquery);
+ }
+ else if (g_fout->remoteVersion >= 70100)
+ {
+ /* No clear notion of an owner at all before 7.4 ... */
+ appendPQExpBuffer(query, "SELECT tableoid, oid, * FROM pg_language "
+ "WHERE lanispl "
+ "ORDER BY oid");
+ }
+ else
+ {
+ appendPQExpBuffer(query, "SELECT "
+ "(SELECT oid FROM pg_class WHERE relname = 'pg_language') AS tableoid, "
+ "oid, * FROM pg_language "
+ "WHERE lanispl "
+ "ORDER BY oid");
+ }
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ *numProcLangs = ntups;
+
+ planginfo = (ProcLangInfo *) malloc(ntups * sizeof(ProcLangInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_lanname = PQfnumber(res, "lanname");
+ i_lanpltrusted = PQfnumber(res, "lanpltrusted");
+ i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
+ /* these may fail and return -1: */
+ i_lanvalidator = PQfnumber(res, "lanvalidator");
+ i_lanacl = PQfnumber(res, "lanacl");
+ i_lanowner = PQfnumber(res, "lanowner");
+
+ for (i = 0; i < ntups; i++)
+ {
+ planginfo[i].dobj.objType = DO_PROCLANG;
+ planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&planginfo[i].dobj);
+
+ planginfo[i].dobj.name = strdup(PQgetvalue(res, i, i_lanname));
+ planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
+ planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
+ if (i_lanvalidator >= 0)
+ planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
+ else
+ planginfo[i].lanvalidator = InvalidOid;
+ if (i_lanacl >= 0)
+ planginfo[i].lanacl = strdup(PQgetvalue(res, i, i_lanacl));
+ else
+ planginfo[i].lanacl = strdup("{=U}");
+ if (i_lanowner >= 0)
+ planginfo[i].lanowner = strdup(PQgetvalue(res, i, i_lanowner));
+ else
+ planginfo[i].lanowner = strdup("");
+
+ if (g_fout->remoteVersion < 70300)
+ {
+ /*
+ * We need to make a dependency to ensure the function will be
+ * dumped first. (In 7.3 and later the regular dependency
+ * mechanism will handle this for us.)
+ */
+ FuncInfo *funcInfo = findFuncByOid(planginfo[i].lanplcallfoid);
+
+ if (funcInfo)
+ addObjectDependency(&planginfo[i].dobj,
+ funcInfo->dobj.dumpId);
+ }
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return planginfo;
+}
+
+/*
+ * getCasts
+ * get basic information about every cast in the system
+ *
+ * numCasts is set to the number of casts read in
+ */
+CastInfo *
+getCasts(int *numCasts)
+{
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ CastInfo *castinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_castsource;
+ int i_casttarget;
+ int i_castfunc;
+ int i_castcontext;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ if (g_fout->remoteVersion >= 70300)
+ {
+ appendPQExpBuffer(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext "
+ "FROM pg_cast ORDER BY 3,4");
+ }
+ else
+ {
+ appendPQExpBuffer(query, "SELECT 0 as tableoid, p.oid, "
+ "t1.oid as castsource, t2.oid as casttarget, "
+ "p.oid as castfunc, 'e' as castcontext "
+ "FROM pg_type t1, pg_type t2, pg_proc p "
+ "WHERE p.pronargs = 1 AND "
+ "p.proargtypes[0] = t1.oid AND "
+ "p.prorettype = t2.oid AND p.proname = t2.typname "
+ "ORDER BY 3,4");
+ }
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ *numCasts = ntups;
+
+ castinfo = (CastInfo *) malloc(ntups * sizeof(CastInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_castsource = PQfnumber(res, "castsource");
+ i_casttarget = PQfnumber(res, "casttarget");
+ i_castfunc = PQfnumber(res, "castfunc");
+ i_castcontext = PQfnumber(res, "castcontext");
+
+ for (i = 0; i < ntups; i++)
+ {
+ PQExpBufferData namebuf;
+ TypeInfo *sTypeInfo;
+ TypeInfo *tTypeInfo;
+
+ castinfo[i].dobj.objType = DO_CAST;
+ castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&castinfo[i].dobj);
+ castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
+ castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
+ castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
+ castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
+
+ /*
+ * Try to name cast as concatenation of typnames. This is only used
+ * for purposes of sorting. If we fail to find either type, the name
+ * will be an empty string.
+ */
+ initPQExpBuffer(&namebuf);
+ sTypeInfo = findTypeByOid(castinfo[i].castsource);
+ tTypeInfo = findTypeByOid(castinfo[i].casttarget);
+ if (sTypeInfo && tTypeInfo)
+ appendPQExpBuffer(&namebuf, "%s %s",
+ sTypeInfo->dobj.name, tTypeInfo->dobj.name);
+ castinfo[i].dobj.name = namebuf.data;
+
+ if (OidIsValid(castinfo[i].castfunc))
+ {
+ /*
+ * We need to make a dependency to ensure the function will be
+ * dumped first. (In 7.3 and later the regular dependency
+ * mechanism will handle this for us.)
+ */
+ FuncInfo *funcInfo;
+
+ funcInfo = findFuncByOid(castinfo[i].castfunc);
+ if (funcInfo)
+ addObjectDependency(&castinfo[i].dobj,
+ funcInfo->dobj.dumpId);
+ }
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return castinfo;
+}
+
+/*
+ * getTableAttrs -
+ * for each interesting table, read info about its attributes
+ * (names, types, default values, CHECK constraints, etc)
+ *
+ * This is implemented in a very inefficient way right now, looping
+ * through the tblinfo and doing a join per table to find the attrs and their
+ * types. However, because we want type names and so forth to be named
+ * relative to the schema of each table, we couldn't do it in just one
+ * query. (Maybe one query per schema?)
+ *
+ * modifies tblinfo
+ */
+void
+getTableAttrs(TableInfo *tblinfo, int numTables)
+{
+ int i,
+ j;
+ PQExpBuffer q = createPQExpBuffer();
+ int i_attnum;
+ int i_attname;
+ int i_atttypname;
+ int i_atttypmod;
+ int i_attstattarget;
+ int i_attstorage;
+ int i_typstorage;
+ int i_attnotnull;
+ int i_atthasdef;
+ int i_attisdropped;
+ int i_attislocal;
+ PGresult *res;
+ int ntups;
+ bool hasdefaults;
+
+ for (i = 0; i < numTables; i++)
+ {
+ TableInfo *tbinfo = &tblinfo[i];
+
+ /* Don't bother to collect info for sequences */
+ if (tbinfo->relkind == RELKIND_SEQUENCE)
+ continue;
+
+ /* Don't bother with uninteresting tables, either */
+ if (!tbinfo->interesting)
+ continue;
+
+ /*
+ * Make sure we are in proper schema for this table; this allows
+ * correct retrieval of formatted type names and default exprs
+ */
+ selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
+
+ /* find all the user attributes and their types */
+
+ /*
+ * we must read the attribute names in attribute number order! because
+ * we will use the attnum to index into the attnames array later. We
+ * actually ask to order by "attrelid, attnum" because (at least up to
+ * 7.3) the planner is not smart enough to realize it needn't re-sort
+ * the output of an indexscan on pg_attribute_relid_attnum_index.
+ */
+ if (g_verbose)
+ write_msg(NULL, "finding the columns and types of table \"%s\"\n",
+ tbinfo->dobj.name);
+
+ resetPQExpBuffer(q);
+
+ if (g_fout->remoteVersion >= 70300)
+ {
+ /* need left join here to not fail on dropped columns ... */
+ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, a.attstattarget, a.attstorage, t.typstorage, "
+ "a.attnotnull, a.atthasdef, a.attisdropped, a.attislocal, "
+ "pg_catalog.format_type(t.oid,a.atttypmod) as atttypname "
+ "from pg_catalog.pg_attribute a left join pg_catalog.pg_type t "
+ "on a.atttypid = t.oid "
+ "where a.attrelid = '%u'::pg_catalog.oid "
+ "and a.attnum > 0::pg_catalog.int2 "
+ "order by a.attrelid, a.attnum",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (g_fout->remoteVersion >= 70100)
+ {
+ /*
+ * attstattarget doesn't exist in 7.1. It does exist in 7.2, but
+ * we don't dump it because we can't tell whether it's been
+ * explicitly set or was just a default.
+ */
+ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, -1 as attstattarget, a.attstorage, t.typstorage, "
+ "a.attnotnull, a.atthasdef, false as attisdropped, false as attislocal, "
+ "format_type(t.oid,a.atttypmod) as atttypname "
+ "from pg_attribute a left join pg_type t "
+ "on a.atttypid = t.oid "
+ "where a.attrelid = '%u'::oid "
+ "and a.attnum > 0::int2 "
+ "order by a.attrelid, a.attnum",
+ tbinfo->dobj.catId.oid);
+ }
+ else
+ {
+ /* format_type not available before 7.1 */
+ appendPQExpBuffer(q, "SELECT attnum, attname, atttypmod, -1 as attstattarget, attstorage, attstorage as typstorage, "
+ "attnotnull, atthasdef, false as attisdropped, false as attislocal, "
+ "(select typname from pg_type where oid = atttypid) as atttypname "
+ "from pg_attribute a "
+ "where attrelid = '%u'::oid "
+ "and attnum > 0::int2 "
+ "order by attrelid, attnum",
+ tbinfo->dobj.catId.oid);
+ }
+
+ res = PQexec(g_conn, q->data);
+ check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
tbinfo->typstorage = (char *) malloc(ntups * sizeof(char));
tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool));
tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool));
- tbinfo->attisserial = (bool *) malloc(ntups * sizeof(bool));
tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
- tbinfo->adef_expr = (char **) malloc(ntups * sizeof(char *));
+ tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
if (j + 1 != atoi(PQgetvalue(res, j, i_attnum)))
{
write_msg(NULL, "invalid column numbering in table \"%s\"\n",
- tbinfo->relname);
+ tbinfo->dobj.name);
exit_nicely();
}
tbinfo->attnames[j] = strdup(PQgetvalue(res, j, i_attname));
tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
- tbinfo->attisserial[j] = false; /* fix below */
tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
- tbinfo->adef_expr[j] = NULL; /* fix below */
+ tbinfo->attrdefs[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
/* these flags will be set in flagInhAttrs() */
PQclear(res);
+ /*
+ * Get info about column defaults
+ */
if (hasdefaults)
{
+ AttrDefInfo *attrdefs;
int numDefaults;
if (g_verbose)
write_msg(NULL, "finding default expressions of table \"%s\"\n",
- tbinfo->relname);
+ tbinfo->dobj.name);
resetPQExpBuffer(q);
if (g_fout->remoteVersion >= 70300)
{
- appendPQExpBuffer(q, "SELECT adnum, "
- "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
+ appendPQExpBuffer(q, "SELECT tableoid, oid, adnum, "
+ "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
"FROM pg_catalog.pg_attrdef "
- "WHERE adrelid = '%s'::pg_catalog.oid",
- tbinfo->oid);
+ "WHERE adrelid = '%u'::pg_catalog.oid",
+ tbinfo->dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 70200)
{
- appendPQExpBuffer(q, "SELECT adnum, "
+ /* 7.2 did not have OIDs in pg_attrdef */
+ appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, adnum, "
"pg_get_expr(adbin, adrelid) AS adsrc "
"FROM pg_attrdef "
- "WHERE adrelid = '%s'::oid",
- tbinfo->oid);
+ "WHERE adrelid = '%u'::oid",
+ tbinfo->dobj.catId.oid);
}
- else
+ else if (g_fout->remoteVersion >= 70100)
{
/* no pg_get_expr, so must rely on adsrc */
- appendPQExpBuffer(q, "SELECT adnum, adsrc FROM pg_attrdef "
- "WHERE adrelid = '%s'::oid",
- tbinfo->oid);
+ appendPQExpBuffer(q, "SELECT tableoid, oid, adnum, adsrc "
+ "FROM pg_attrdef "
+ "WHERE adrelid = '%u'::oid",
+ tbinfo->dobj.catId.oid);
}
- res = PQexec(g_conn, q->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
+ else
{
- write_msg(NULL, "query to get column default values failed: %s",
- PQerrorMessage(g_conn));
- exit_nicely();
+ /* no pg_get_expr, no tableoid either */
+ appendPQExpBuffer(q, "SELECT "
+ "(SELECT oid FROM pg_class WHERE relname = 'pg_attrdef') AS tableoid, "
+ "oid, adnum, adsrc "
+ "FROM pg_attrdef "
+ "WHERE adrelid = '%u'::oid",
+ tbinfo->dobj.catId.oid);
}
+ res = PQexec(g_conn, q->data);
+ check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);
numDefaults = PQntuples(res);
+ attrdefs = (AttrDefInfo *) malloc(numDefaults * sizeof(AttrDefInfo));
+
for (j = 0; j < numDefaults; j++)
{
- int adnum = atoi(PQgetvalue(res, j, 0));
+ int adnum;
+
+ attrdefs[j].dobj.objType = DO_ATTRDEF;
+ attrdefs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
+ attrdefs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
+ AssignDumpId(&attrdefs[j].dobj);
+ attrdefs[j].adtable = tbinfo;
+ attrdefs[j].adnum = adnum = atoi(PQgetvalue(res, j, 2));
+ attrdefs[j].adef_expr = strdup(PQgetvalue(res, j, 3));
+
+ attrdefs[j].dobj.name = strdup(tbinfo->dobj.name);
+ attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
+
+ attrdefs[j].dobj.dump = tbinfo->dobj.dump;
+
+ /*
+ * Defaults on a VIEW must always be dumped as separate ALTER
+ * TABLE commands. Defaults on regular tables are dumped as
+ * part of the CREATE TABLE if possible. To check if it's
+ * safe, we mark the default as needing to appear before the
+ * CREATE.
+ */
+ if (tbinfo->relkind == RELKIND_VIEW)
+ {
+ attrdefs[j].separate = true;
+ /* needed in case pre-7.3 DB: */
+ addObjectDependency(&attrdefs[j].dobj,
+ tbinfo->dobj.dumpId);
+ }
+ else
+ {
+ attrdefs[j].separate = false;
+ addObjectDependency(&tbinfo->dobj,
+ attrdefs[j].dobj.dumpId);
+ }
if (adnum <= 0 || adnum > ntups)
{
write_msg(NULL, "invalid adnum value %d for table \"%s\"\n",
- adnum, tbinfo->relname);
+ adnum, tbinfo->dobj.name);
exit_nicely();
}
- tbinfo->adef_expr[adnum - 1] = strdup(PQgetvalue(res, j, 1));
+ tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
}
PQclear(res);
}
/*
- * Check to see if any columns are serial columns. Our first
- * quick filter is that it must be integer or bigint with a
- * default. If so, we scan to see if we found a sequence linked
- * to this column. If we did, mark the column and sequence
- * appropriately.
+ * Get info about table CHECK constraints
*/
- for (j = 0; j < ntups; j++)
+ if (tbinfo->ncheck > 0)
{
- /*
- * Note assumption that format_type will show these types as
- * exactly "integer" and "bigint" regardless of schema path.
- * This is correct in 7.3 but needs to be watched.
- */
- if (strcmp(tbinfo->atttypnames[j], "integer") != 0 &&
- strcmp(tbinfo->atttypnames[j], "bigint") != 0)
- continue;
- if (tbinfo->adef_expr[j] == NULL)
- continue;
- for (k = 0; k < numTables; k++)
+ ConstraintInfo *constrs;
+ int numConstrs;
+
+ if (g_verbose)
+ write_msg(NULL, "finding check constraints for table \"%s\"\n",
+ tbinfo->dobj.name);
+
+ resetPQExpBuffer(q);
+ if (g_fout->remoteVersion >= 70400)
+ {
+ appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
+ "pg_catalog.pg_get_constraintdef(oid) AS consrc "
+ "FROM pg_catalog.pg_constraint "
+ "WHERE conrelid = '%u'::pg_catalog.oid "
+ " AND contype = 'c' "
+ "ORDER BY conname",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (g_fout->remoteVersion >= 70300)
+ {
+ /* no pg_get_constraintdef, must use consrc */
+ appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
+ "'CHECK (' || consrc || ')' AS consrc "
+ "FROM pg_catalog.pg_constraint "
+ "WHERE conrelid = '%u'::pg_catalog.oid "
+ " AND contype = 'c' "
+ "ORDER BY conname",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (g_fout->remoteVersion >= 70200)
{
- TableInfo *seqinfo = &tblinfo[k];
+ /* 7.2 did not have OIDs in pg_relcheck */
+ appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, "
+ "rcname AS conname, "
+ "'CHECK (' || rcsrc || ')' AS consrc "
+ "FROM pg_relcheck "
+ "WHERE rcrelid = '%u'::oid "
+ "ORDER BY rcname",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (g_fout->remoteVersion >= 70100)
+ {
+ appendPQExpBuffer(q, "SELECT tableoid, oid, "
+ "rcname AS conname, "
+ "'CHECK (' || rcsrc || ')' AS consrc "
+ "FROM pg_relcheck "
+ "WHERE rcrelid = '%u'::oid "
+ "ORDER BY rcname",
+ tbinfo->dobj.catId.oid);
+ }
+ else
+ {
+ /* no tableoid in 7.0 */
+ appendPQExpBuffer(q, "SELECT "
+ "(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
+ "oid, rcname AS conname, "
+ "'CHECK (' || rcsrc || ')' AS consrc "
+ "FROM pg_relcheck "
+ "WHERE rcrelid = '%u'::oid "
+ "ORDER BY rcname",
+ tbinfo->dobj.catId.oid);
+ }
+ res = PQexec(g_conn, q->data);
+ check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);
- if (seqinfo->owning_tab != NULL &&
- strcmp(seqinfo->owning_tab, tbinfo->oid) == 0 &&
- seqinfo->owning_col == j + 1)
- {
- /*
- * Found a match. Copy the table's interesting and
- * dumpable flags to the sequence.
- */
- tbinfo->attisserial[j] = true;
- seqinfo->interesting = tbinfo->interesting;
- seqinfo->dump = tbinfo->dump;
- break;
- }
+ numConstrs = PQntuples(res);
+ if (numConstrs != tbinfo->ncheck)
+ {
+ write_msg(NULL, "expected %d check constraints on table \"%s\" but found %d\n",
+ tbinfo->ncheck, tbinfo->dobj.name, numConstrs);
+ write_msg(NULL, "(The system catalogs might be corrupted.)\n");
+ exit_nicely();
+ }
+
+ constrs = (ConstraintInfo *) malloc(numConstrs * sizeof(ConstraintInfo));
+ tbinfo->checkexprs = constrs;
+
+ for (j = 0; j < numConstrs; j++)
+ {
+ constrs[j].dobj.objType = DO_CONSTRAINT;
+ constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
+ constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
+ AssignDumpId(&constrs[j].dobj);
+ constrs[j].dobj.name = strdup(PQgetvalue(res, j, 2));
+ constrs[j].dobj.namespace = tbinfo->dobj.namespace;
+ constrs[j].contable = tbinfo;
+ constrs[j].condomain = NULL;
+ constrs[j].contype = 'c';
+ constrs[j].condef = strdup(PQgetvalue(res, j, 3));
+ constrs[j].conindex = 0;
+ constrs[j].coninherited = false;
+ constrs[j].separate = false;
+
+ constrs[j].dobj.dump = tbinfo->dobj.dump;
+
+ /*
+ * Mark the constraint as needing to appear before the table
+ * --- this is so that any other dependencies of the
+ * constraint will be emitted before we try to create the
+ * table.
+ */
+ addObjectDependency(&tbinfo->dobj,
+ constrs[j].dobj.dumpId);
+
+ /*
+ * If the constraint is inherited, this will be detected
+ * later. We also detect later if the constraint must be
+ * split out from the table definition.
+ */
}
+ PQclear(res);
}
}
* dumpComment --
*
* This routine is used to dump any comments associated with the
- * oid handed to this routine. The routine takes a constant character
+ * object handed to this routine. The routine takes a constant character
* string for the target part of the comment-creation command, plus
* the namespace and owner of the object (for labeling the ArchiveEntry),
- * plus OID, class name, and subid which are the lookup key for pg_description.
+ * plus catalog ID and subid which are the lookup key for pg_description,
+ * plus the dump ID for the object (for setting a dependency).
* If a matching pg_description entry is found, it is dumped.
- * Additional dependencies can be passed for the comment, too --- this is
- * needed for VIEWs, whose comments are filed under the table OID but
- * which are dumped in order by their rule OID.
+ *
+ * Note: although this routine takes a dumpId for dependency purposes,
+ * that purpose is just to mark the dependency in the emitted dump file
+ * for possible future use by pg_restore. We do NOT use it for determining
+ * ordering of the comment in the dump file, because this routine is called
+ * after dependency sorting occurs. This routine should be called just after
+ * calling ArchiveEntry() for the specified object.
*/
-
static void
dumpComment(Archive *fout, const char *target,
const char *namespace, const char *owner,
- const char *oid, const char *classname, int subid,
- const char *((*deps)[]))
+ CatalogId catalogId, int subid, DumpId dumpId)
{
- PGresult *res;
- PQExpBuffer query;
- int i_description;
+ CommentItem *comments;
+ int ncomments;
/* Comments are SCHEMA not data */
if (dataOnly)
return;
- /*
- * Note we do NOT change source schema here; preserve the caller's
- * setting, instead.
- */
-
- /* Build query to find comment */
-
- query = createPQExpBuffer();
-
- if (fout->remoteVersion >= 70300)
- {
- appendPQExpBuffer(query, "SELECT description FROM pg_catalog.pg_description "
- "WHERE objoid = '%s'::pg_catalog.oid and classoid = "
- "'pg_catalog.%s'::pg_catalog.regclass "
- "and objsubid = %d",
- oid, classname, subid);
- }
- else if (fout->remoteVersion >= 70200)
- {
- appendPQExpBuffer(query, "SELECT description FROM pg_description "
- "WHERE objoid = '%s'::oid and classoid = "
- "(SELECT oid FROM pg_class where relname = '%s') "
- "and objsubid = %d",
- oid, classname, subid);
- }
- else
- {
- /* Note: this will fail to find attribute comments in pre-7.2... */
- appendPQExpBuffer(query, "SELECT description FROM pg_description WHERE objoid = '%s'::oid", oid);
- }
-
- /* Execute query */
+ /* Search for comments associated with catalogId, using table */
+ ncomments = findComments(fout, catalogId.tableoid, catalogId.oid,
+ &comments);
- res = PQexec(g_conn, query->data);
- if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+ /* Is there one matching the subid? */
+ while (ncomments > 0)
{
- write_msg(NULL, "query to get comment on OID %s failed: %s",
- oid, PQerrorMessage(g_conn));
- exit_nicely();
+ if (comments->objsubid == subid)
+ break;
+ comments++;
+ ncomments--;
}
/* If a comment exists, build COMMENT ON statement */
-
- if (PQntuples(res) == 1)
+ if (ncomments > 0)
{
- i_description = PQfnumber(res, "description");
- resetPQExpBuffer(query);
+ PQExpBuffer query = createPQExpBuffer();
+
appendPQExpBuffer(query, "COMMENT ON %s IS ", target);
- appendStringLiteral(query, PQgetvalue(res, 0, i_description), false);
+ appendStringLiteralAH(query, comments->descr, fout);
appendPQExpBuffer(query, ";\n");
- ArchiveEntry(fout, oid, target, namespace, owner,
- "COMMENT", deps,
- query->data, "", NULL, NULL, NULL);
- }
+ ArchiveEntry(fout, nilCatalogId, createDumpId(),
+ target, namespace, NULL, owner, false,
+ "COMMENT", query->data, "", NULL,
+ &(dumpId), 1,
+ NULL, NULL);
- PQclear(res);
- destroyPQExpBuffer(query);
+ destroyPQExpBuffer(query);
+ }
}
/*
* dumpTableComment --
*
* As above, but dump comments for both the specified table (or view)
- * and its columns. For speed, we want to do this with only one query.
+ * and its columns.
*/
static void
dumpTableComment(Archive *fout, TableInfo *tbinfo,
- const char *reltypename,
- const char *((*deps)[]))
+ const char *reltypename)
{
- PGresult *res;
+ CommentItem *comments;
+ int ncomments;
PQExpBuffer query;
PQExpBuffer target;
- int i_description;
- int i_objsubid;
- int ntups;
- int i;
/* Comments are SCHEMA not data */
if (dataOnly)
return;
- /*
- * Note we do NOT change source schema here; preserve the caller's
- * setting, instead.
- */
+ /* Search for comments associated with relation, using table */
+ ncomments = findComments(fout,
+ tbinfo->dobj.catId.tableoid,
+ tbinfo->dobj.catId.oid,
+ &comments);
- /* Build query to find comments */
+ /* If comments exist, build COMMENT ON statements */
+ if (ncomments <= 0)
+ return;
query = createPQExpBuffer();
target = createPQExpBuffer();
- if (fout->remoteVersion >= 70300)
- {
- appendPQExpBuffer(query, "SELECT description, objsubid FROM pg_catalog.pg_description "
- "WHERE objoid = '%s'::pg_catalog.oid and classoid = "
- "'pg_catalog.pg_class'::pg_catalog.regclass "
- "ORDER BY objoid, classoid, objsubid",
- tbinfo->oid);
- }
- else if (fout->remoteVersion >= 70200)
- {
- appendPQExpBuffer(query, "SELECT description, objsubid FROM pg_description "
- "WHERE objoid = '%s'::oid and classoid = "
- "(SELECT oid FROM pg_class where relname = 'pg_class') "
- "ORDER BY objoid, classoid, objsubid",
- tbinfo->oid);
- }
- else
- {
- /* Note: this will fail to find attribute comments in pre-7.2... */
- appendPQExpBuffer(query, "SELECT description, 0 as objsubid FROM pg_description WHERE objoid = '%s'::oid", tbinfo->oid);
- }
-
- /* Execute query */
-
- res = PQexec(g_conn, query->data);
- if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to get comments on table %s failed: %s",
- tbinfo->relname, PQerrorMessage(g_conn));
- exit_nicely();
- }
- i_description = PQfnumber(res, "description");
- i_objsubid = PQfnumber(res, "objsubid");
-
- /* If comments exist, build COMMENT ON statements */
-
- ntups = PQntuples(res);
- for (i = 0; i < ntups; i++)
+ while (ncomments > 0)
{
- const char *descr = PQgetvalue(res, i, i_description);
- int objsubid = atoi(PQgetvalue(res, i, i_objsubid));
+ const char *descr = comments->descr;
+ int objsubid = comments->objsubid;
if (objsubid == 0)
{
resetPQExpBuffer(target);
appendPQExpBuffer(target, "%s %s", reltypename,
- fmtId(tbinfo->relname));
+ fmtId(tbinfo->dobj.name));
resetPQExpBuffer(query);
appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data);
- appendStringLiteral(query, descr, false);
+ appendStringLiteralAH(query, descr, fout);
appendPQExpBuffer(query, ";\n");
- ArchiveEntry(fout, tbinfo->oid, target->data,
- tbinfo->relnamespace->nspname, tbinfo->usename,
- "COMMENT", deps,
- query->data, "", NULL, NULL, NULL);
+ ArchiveEntry(fout, nilCatalogId, createDumpId(),
+ target->data,
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname,
+ false, "COMMENT", query->data, "", NULL,
+ &(tbinfo->dobj.dumpId), 1,
+ NULL, NULL);
}
else if (objsubid > 0 && objsubid <= tbinfo->numatts)
{
resetPQExpBuffer(target);
appendPQExpBuffer(target, "COLUMN %s.",
- fmtId(tbinfo->relname));
+ fmtId(tbinfo->dobj.name));
appendPQExpBuffer(target, "%s",
fmtId(tbinfo->attnames[objsubid - 1]));
resetPQExpBuffer(query);
appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data);
- appendStringLiteral(query, descr, false);
+ appendStringLiteralAH(query, descr, fout);
appendPQExpBuffer(query, ";\n");
- ArchiveEntry(fout, tbinfo->oid, target->data,
- tbinfo->relnamespace->nspname, tbinfo->usename,
- "COMMENT", deps,
- query->data, "", NULL, NULL, NULL);
+ ArchiveEntry(fout, nilCatalogId, createDumpId(),
+ target->data,
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname,
+ false, "COMMENT", query->data, "", NULL,
+ &(tbinfo->dobj.dumpId), 1,
+ NULL, NULL);
}
+
+ comments++;
+ ncomments--;
}
- PQclear(res);
destroyPQExpBuffer(query);
destroyPQExpBuffer(target);
}
/*
- * dumpDBComment --
+ * findComments --
*
- * This routine is used to dump any comments associated with the
- * database to which we are currently connected.
+ * Find the comment(s), if any, associated with the given object. All the
+ * objsubid values associated with the given classoid/objoid are found with
+ * one search.
*/
-void
-dumpDBComment(Archive *fout)
+static int
+findComments(Archive *fout, Oid classoid, Oid objoid,
+ CommentItem **items)
+{
+ /* static storage for table of comments */
+ static CommentItem *comments = NULL;
+ static int ncomments = -1;
+
+ CommentItem *middle = NULL;
+ CommentItem *low;
+ CommentItem *high;
+ int nmatch;
+
+ /* Get comments if we didn't already */
+ if (ncomments < 0)
+ ncomments = collectComments(fout, &comments);
+
+ /*
+ * Pre-7.2, pg_description does not contain classoid, so collectComments
+ * just stores a zero. If there's a collision on object OID, well, you
+ * get duplicate comments.
+ */
+ if (fout->remoteVersion < 70200)
+ classoid = 0;
+
+ /*
+ * Do binary search to find some item matching the object.
+ */
+ low = &comments[0];
+ high = &comments[ncomments - 1];
+ while (low <= high)
+ {
+ middle = low + (high - low) / 2;
+
+ if (classoid < middle->classoid)
+ high = middle - 1;
+ else if (classoid > middle->classoid)
+ low = middle + 1;
+ else if (objoid < middle->objoid)
+ high = middle - 1;
+ else if (objoid > middle->objoid)
+ low = middle + 1;
+ else
+ break; /* found a match */
+ }
+
+ if (low > high) /* no matches */
+ {
+ *items = NULL;
+ return 0;
+ }
+
+ /*
+ * Now determine how many items match the object. The search loop
+ * invariant still holds: only items between low and high inclusive could
+ * match.
+ */
+ nmatch = 1;
+ while (middle > low)
+ {
+ if (classoid != middle[-1].classoid ||
+ objoid != middle[-1].objoid)
+ break;
+ middle--;
+ nmatch++;
+ }
+
+ *items = middle;
+
+ middle += nmatch;
+ while (middle <= high)
+ {
+ if (classoid != middle->classoid ||
+ objoid != middle->objoid)
+ break;
+ middle++;
+ nmatch++;
+ }
+
+ return nmatch;
+}
+
+/*
+ * collectComments --
+ *
+ * Construct a table of all comments available for database objects.
+ * We used to do per-object queries for the comments, but it's much faster
+ * to pull them all over at once, and on most databases the memory cost
+ * isn't high.
+ *
+ * The table is sorted by classoid/objid/objsubid for speed in lookup.
+ */
+static int
+collectComments(Archive *fout, CommentItem **items)
{
PGresult *res;
PQExpBuffer query;
- int i_oid;
+ int i_description;
+ int i_classoid;
+ int i_objoid;
+ int i_objsubid;
+ int ntups;
+ int i;
+ CommentItem *comments;
+
+ /*
+ * Note we do NOT change source schema here; preserve the caller's
+ * setting, instead.
+ */
+
+ query = createPQExpBuffer();
+
+ if (fout->remoteVersion >= 70300)
+ {
+ appendPQExpBuffer(query, "SELECT description, classoid, objoid, objsubid "
+ "FROM pg_catalog.pg_description "
+ "ORDER BY classoid, objoid, objsubid");
+ }
+ else if (fout->remoteVersion >= 70200)
+ {
+ appendPQExpBuffer(query, "SELECT description, classoid, objoid, objsubid "
+ "FROM pg_description "
+ "ORDER BY classoid, objoid, objsubid");
+ }
+ else
+ {
+ /* Note: this will fail to find attribute comments in pre-7.2... */
+ appendPQExpBuffer(query, "SELECT description, 0 as classoid, objoid, 0 as objsubid "
+ "FROM pg_description "
+ "ORDER BY objoid");
+ }
- /* Make sure we are in proper schema */
- selectSourceSchema("pg_catalog");
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
- /* Build query to find comment */
+ /* Construct lookup table containing OIDs in numeric form */
- query = createPQExpBuffer();
- appendPQExpBuffer(query, "SELECT oid FROM pg_database WHERE datname = ");
- appendStringLiteral(query, PQdb(g_conn), true);
+ i_description = PQfnumber(res, "description");
+ i_classoid = PQfnumber(res, "classoid");
+ i_objoid = PQfnumber(res, "objoid");
+ i_objsubid = PQfnumber(res, "objsubid");
- /* Execute query */
+ ntups = PQntuples(res);
- res = PQexec(g_conn, query->data);
- if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+ comments = (CommentItem *) malloc(ntups * sizeof(CommentItem));
+
+ for (i = 0; i < ntups; i++)
{
- write_msg(NULL, "query to get database OID failed: %s",
- PQerrorMessage(g_conn));
- exit_nicely();
+ comments[i].descr = PQgetvalue(res, i, i_description);
+ comments[i].classoid = atooid(PQgetvalue(res, i, i_classoid));
+ comments[i].objoid = atooid(PQgetvalue(res, i, i_objoid));
+ comments[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid));
}
- /* If a comment exists, build COMMENT ON statement */
+ /* Do NOT free the PGresult since we are keeping pointers into it */
+ destroyPQExpBuffer(query);
+
+ *items = comments;
+ return ntups;
+}
- if (PQntuples(res) != 0)
+/*
+ * dumpDumpableObject
+ *
+ * This routine and its subsidiaries are responsible for creating
+ * ArchiveEntries (TOC objects) for each object to be dumped.
+ */
+static void
+dumpDumpableObject(Archive *fout, DumpableObject *dobj)
+{
+ switch (dobj->objType)
{
- i_oid = PQfnumber(res, "oid");
- resetPQExpBuffer(query);
- appendPQExpBuffer(query, "DATABASE %s", fmtId(PQdb(g_conn)));
- dumpComment(fout, query->data, NULL, "",
- PQgetvalue(res, 0, i_oid), "pg_database", 0, NULL);
+ case DO_NAMESPACE:
+ dumpNamespace(fout, (NamespaceInfo *) dobj);
+ break;
+ case DO_TYPE:
+ dumpType(fout, (TypeInfo *) dobj);
+ break;
+ case DO_SHELL_TYPE:
+ dumpShellType(fout, (ShellTypeInfo *) dobj);
+ break;
+ case DO_FUNC:
+ dumpFunc(fout, (FuncInfo *) dobj);
+ break;
+ case DO_AGG:
+ dumpAgg(fout, (AggInfo *) dobj);
+ break;
+ case DO_OPERATOR:
+ dumpOpr(fout, (OprInfo *) dobj);
+ break;
+ case DO_OPCLASS:
+ dumpOpclass(fout, (OpclassInfo *) dobj);
+ break;
+ case DO_CONVERSION:
+ dumpConversion(fout, (ConvInfo *) dobj);
+ break;
+ case DO_TABLE:
+ dumpTable(fout, (TableInfo *) dobj);
+ break;
+ case DO_ATTRDEF:
+ dumpAttrDef(fout, (AttrDefInfo *) dobj);
+ break;
+ case DO_INDEX:
+ dumpIndex(fout, (IndxInfo *) dobj);
+ break;
+ case DO_RULE:
+ dumpRule(fout, (RuleInfo *) dobj);
+ break;
+ case DO_TRIGGER:
+ dumpTrigger(fout, (TriggerInfo *) dobj);
+ break;
+ case DO_CONSTRAINT:
+ dumpConstraint(fout, (ConstraintInfo *) dobj);
+ break;
+ case DO_FK_CONSTRAINT:
+ dumpConstraint(fout, (ConstraintInfo *) dobj);
+ break;
+ case DO_PROCLANG:
+ dumpProcLang(fout, (ProcLangInfo *) dobj);
+ break;
+ case DO_CAST:
+ dumpCast(fout, (CastInfo *) dobj);
+ break;
+ case DO_TABLE_DATA:
+ dumpTableData(fout, (TableDataInfo *) dobj);
+ break;
+ case DO_TABLE_TYPE:
+ /* table rowtypes are never dumped separately */
+ break;
+ case DO_BLOBS:
+ ArchiveEntry(fout, dobj->catId, dobj->dumpId,
+ dobj->name, NULL, NULL, "",
+ false, "BLOBS", "", "", NULL,
+ NULL, 0,
+ dumpBlobs, NULL);
+ break;
+ case DO_BLOB_COMMENTS:
+ ArchiveEntry(fout, dobj->catId, dobj->dumpId,
+ dobj->name, NULL, NULL, "",
+ false, "BLOB COMMENTS", "", "", NULL,
+ NULL, 0,
+ dumpBlobComments, NULL);
+ break;
}
-
- PQclear(res);
- destroyPQExpBuffer(query);
}
/*
- * dumpNamespaces
- * writes out to fout the queries to recreate user-defined namespaces
+ * dumpNamespace
+ * writes out to fout the queries to recreate a user-defined namespace
*/
-void
-dumpNamespaces(Archive *fout, NamespaceInfo *nsinfo, int numNamespaces)
+static void
+dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
{
- PQExpBuffer q = createPQExpBuffer();
- PQExpBuffer delq = createPQExpBuffer();
- int i;
+ PQExpBuffer q;
+ PQExpBuffer delq;
char *qnspname;
- for (i = 0; i < numNamespaces; i++)
- {
- NamespaceInfo *nspinfo = &nsinfo[i];
+ /* Skip if not to be dumped */
+ if (!nspinfo->dobj.dump || dataOnly)
+ return;
- /* skip if not to be dumped */
- if (!nspinfo->dump)
- continue;
+ /* don't dump dummy namespace from pre-7.3 source */
+ if (strlen(nspinfo->dobj.name) == 0)
+ return;
- /* don't dump dummy namespace from pre-7.3 source */
- if (strlen(nspinfo->nspname) == 0)
- continue;
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
- qnspname = strdup(fmtId(nspinfo->nspname));
+ qnspname = strdup(fmtId(nspinfo->dobj.name));
- /*
- * If it's the PUBLIC namespace, don't emit a CREATE SCHEMA record
- * for it, since we expect PUBLIC to exist already in the
- * destination database. And emit ACL info only if the ACL isn't
- * the standard value for PUBLIC.
- */
- if (strcmp(nspinfo->nspname, "public") == 0)
- {
- if (!aclsSkip && strcmp(nspinfo->nspacl, "{=UC}") != 0)
- dumpACL(fout, "SCHEMA", qnspname, nspinfo->nspname, NULL,
- nspinfo->usename, nspinfo->nspacl,
- nspinfo->oid);
- }
- else
- {
- resetPQExpBuffer(q);
- resetPQExpBuffer(delq);
+ appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
- appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
+ appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
- appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
+ ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
+ nspinfo->dobj.name,
+ NULL, NULL,
+ nspinfo->rolname,
+ false, "SCHEMA", q->data, delq->data, NULL,
+ nspinfo->dobj.dependencies, nspinfo->dobj.nDeps,
+ NULL, NULL);
- ArchiveEntry(fout, nspinfo->oid, nspinfo->nspname,
- NULL,
- nspinfo->usename, "SCHEMA", NULL,
- q->data, delq->data, NULL, NULL, NULL);
+ /* Dump Schema Comments */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "SCHEMA %s", qnspname);
+ dumpComment(fout, q->data,
+ NULL, nspinfo->rolname,
+ nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
- /* Dump Schema Comments */
- resetPQExpBuffer(q);
- appendPQExpBuffer(q, "SCHEMA %s", qnspname);
- dumpComment(fout, q->data,
- NULL, nspinfo->usename,
- nspinfo->oid, "pg_namespace", 0, NULL);
-
- if (!aclsSkip)
- dumpACL(fout, "SCHEMA", qnspname, nspinfo->nspname, NULL,
- nspinfo->usename, nspinfo->nspacl,
- nspinfo->oid);
- }
+ dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
+ qnspname, nspinfo->dobj.name, NULL,
+ nspinfo->rolname, nspinfo->nspacl);
- free(qnspname);
- }
+ free(qnspname);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
}
/*
- * dumpOneBaseType
+ * dumpType
+ * writes out to fout the queries to recreate a user-defined type
+ */
+static void
+dumpType(Archive *fout, TypeInfo *tinfo)
+{
+ /* Skip if not to be dumped */
+ if (!tinfo->dobj.dump || dataOnly)
+ return;
+
+ /* Dump out in proper style */
+ if (tinfo->typtype == 'b')
+ dumpBaseType(fout, tinfo);
+ else if (tinfo->typtype == 'd')
+ dumpDomain(fout, tinfo);
+ else if (tinfo->typtype == 'c')
+ dumpCompositeType(fout, tinfo);
+}
+
+/*
+ * dumpBaseType
* writes out to fout the queries to recreate a user-defined base type
- * as requested by dumpTypes
*/
static void
-dumpOneBaseType(Archive *fout, TypeInfo *tinfo,
- FuncInfo *g_finfo, int numFuncs,
- TypeInfo *g_tinfo, int numTypes)
+dumpBaseType(Archive *fout, TypeInfo *tinfo)
{
PQExpBuffer q = createPQExpBuffer();
PQExpBuffer delq = createPQExpBuffer();
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int ntups;
- int funcInd;
char *typlen;
char *typinput;
char *typoutput;
char *typreceive;
char *typsend;
- char *typinputoid;
- char *typoutputoid;
- char *typreceiveoid;
- char *typsendoid;
+ char *typanalyze;
+ Oid typinputoid;
+ Oid typoutputoid;
+ Oid typreceiveoid;
+ Oid typsendoid;
+ Oid typanalyzeoid;
char *typdelim;
- char *typdefault;
char *typbyval;
char *typalign;
char *typstorage;
- const char *((*deps)[]);
- int depIdx = 0;
-
- deps = malloc(sizeof(char *) * 10);
+ char *typdefault;
+ bool typdefault_is_literal = false;
/* Set proper schema search path so regproc references list correctly */
- selectSourceSchema(tinfo->typnamespace->nspname);
+ selectSourceSchema(tinfo->dobj.namespace->dobj.name);
/* Fetch type-specific details */
- if (fout->remoteVersion >= 70400)
+ if (fout->remoteVersion >= 80000)
+ {
+ appendPQExpBuffer(query, "SELECT typlen, "
+ "typinput, typoutput, typreceive, typsend, "
+ "typanalyze, "
+ "typinput::pg_catalog.oid as typinputoid, "
+ "typoutput::pg_catalog.oid as typoutputoid, "
+ "typreceive::pg_catalog.oid as typreceiveoid, "
+ "typsend::pg_catalog.oid as typsendoid, "
+ "typanalyze::pg_catalog.oid as typanalyzeoid, "
+ "typdelim, typbyval, typalign, typstorage, "
+ "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) as typdefaultbin, typdefault "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid",
+ tinfo->dobj.catId.oid);
+ }
+ else if (fout->remoteVersion >= 70400)
{
appendPQExpBuffer(query, "SELECT typlen, "
"typinput, typoutput, typreceive, typsend, "
+ "'-' as typanalyze, "
"typinput::pg_catalog.oid as typinputoid, "
"typoutput::pg_catalog.oid as typoutputoid, "
"typreceive::pg_catalog.oid as typreceiveoid, "
"typsend::pg_catalog.oid as typsendoid, "
- "typdelim, typdefault, typbyval, typalign, "
- "typstorage "
+ "0 as typanalyzeoid, "
+ "typdelim, typbyval, typalign, typstorage, "
+ "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) as typdefaultbin, typdefault "
"FROM pg_catalog.pg_type "
- "WHERE oid = '%s'::pg_catalog.oid",
- tinfo->oid);
+ "WHERE oid = '%u'::pg_catalog.oid",
+ tinfo->dobj.catId.oid);
}
else if (fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query, "SELECT typlen, "
"typinput, typoutput, "
"'-' as typreceive, '-' as typsend, "
+ "'-' as typanalyze, "
"typinput::pg_catalog.oid as typinputoid, "
"typoutput::pg_catalog.oid as typoutputoid, "
"0 as typreceiveoid, 0 as typsendoid, "
- "typdelim, typdefault, typbyval, typalign, "
- "typstorage "
+ "0 as typanalyzeoid, "
+ "typdelim, typbyval, typalign, typstorage, "
+ "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) as typdefaultbin, typdefault "
"FROM pg_catalog.pg_type "
- "WHERE oid = '%s'::pg_catalog.oid",
- tinfo->oid);
+ "WHERE oid = '%u'::pg_catalog.oid",
+ tinfo->dobj.catId.oid);
}
- else if (fout->remoteVersion >= 70100)
+ else if (fout->remoteVersion >= 70200)
{
/*
* Note: although pre-7.3 catalogs contain typreceive and typsend,
appendPQExpBuffer(query, "SELECT typlen, "
"typinput, typoutput, "
"'-' as typreceive, '-' as typsend, "
+ "'-' as typanalyze, "
+ "typinput::oid as typinputoid, "
+ "typoutput::oid as typoutputoid, "
+ "0 as typreceiveoid, 0 as typsendoid, "
+ "0 as typanalyzeoid, "
+ "typdelim, typbyval, typalign, typstorage, "
+ "NULL as typdefaultbin, typdefault "
+ "FROM pg_type "
+ "WHERE oid = '%u'::oid",
+ tinfo->dobj.catId.oid);
+ }
+ else if (fout->remoteVersion >= 70100)
+ {
+ /*
+ * Ignore pre-7.2 typdefault; the field exists but has an unusable
+ * representation.
+ */
+ appendPQExpBuffer(query, "SELECT typlen, "
+ "typinput, typoutput, "
+ "'-' as typreceive, '-' as typsend, "
+ "'-' as typanalyze, "
"typinput::oid as typinputoid, "
"typoutput::oid as typoutputoid, "
"0 as typreceiveoid, 0 as typsendoid, "
- "typdelim, typdefault, typbyval, typalign, "
- "typstorage "
+ "0 as typanalyzeoid, "
+ "typdelim, typbyval, typalign, typstorage, "
+ "NULL as typdefaultbin, NULL as typdefault "
"FROM pg_type "
- "WHERE oid = '%s'::oid",
- tinfo->oid);
+ "WHERE oid = '%u'::oid",
+ tinfo->dobj.catId.oid);
}
else
{
appendPQExpBuffer(query, "SELECT typlen, "
"typinput, typoutput, "
"'-' as typreceive, '-' as typsend, "
+ "'-' as typanalyze, "
"typinput::oid as typinputoid, "
"typoutput::oid as typoutputoid, "
"0 as typreceiveoid, 0 as typsendoid, "
- "typdelim, typdefault, typbyval, typalign, "
- "'p'::char as typstorage "
+ "0 as typanalyzeoid, "
+ "typdelim, typbyval, typalign, "
+ "'p'::char as typstorage, "
+ "NULL as typdefaultbin, NULL as typdefault "
"FROM pg_type "
- "WHERE oid = '%s'::oid",
- tinfo->oid);
+ "WHERE oid = '%u'::oid",
+ tinfo->dobj.catId.oid);
}
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain information on data type \"%s\" failed: %s",
- tinfo->typname, PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
/* Expecting a single result only */
ntups = PQntuples(res);
typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
- typinputoid = PQgetvalue(res, 0, PQfnumber(res, "typinputoid"));
- typoutputoid = PQgetvalue(res, 0, PQfnumber(res, "typoutputoid"));
- typreceiveoid = PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid"));
- typsendoid = PQgetvalue(res, 0, PQfnumber(res, "typsendoid"));
+ typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
+ typinputoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typinputoid")));
+ typoutputoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typoutputoid")));
+ typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
+ typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
+ typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
- if (PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
- typdefault = NULL;
- else
- typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
-
- /*
- * Before we create a type, we need to create the input and output
- * functions for it, if they haven't been created already. So make
- * sure there are dependency entries for this. But don't include
- * dependencies if the functions aren't going to be dumped.
- */
- funcInd = findFuncByOid(g_finfo, numFuncs, typinputoid);
- if (funcInd >= 0 && g_finfo[funcInd].pronamespace->dump)
- (*deps)[depIdx++] = strdup(typinputoid);
-
- funcInd = findFuncByOid(g_finfo, numFuncs, typoutputoid);
- if (funcInd >= 0 && g_finfo[funcInd].pronamespace->dump)
- (*deps)[depIdx++] = strdup(typoutputoid);
-
- if (strcmp(typreceiveoid, "0") != 0)
- {
- funcInd = findFuncByOid(g_finfo, numFuncs, typreceiveoid);
- if (funcInd >= 0 && g_finfo[funcInd].pronamespace->dump)
- (*deps)[depIdx++] = strdup(typreceiveoid);
- }
-
- if (strcmp(typsendoid, "0") != 0)
+ if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
+ typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
+ else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
{
- funcInd = findFuncByOid(g_finfo, numFuncs, typsendoid);
- if (funcInd >= 0 && g_finfo[funcInd].pronamespace->dump)
- (*deps)[depIdx++] = strdup(typsendoid);
+ typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
+ typdefault_is_literal = true; /* it needs quotes */
}
+ else
+ typdefault = NULL;
/*
- * DROP must be fully qualified in case same name appears in
- * pg_catalog
+ * DROP must be fully qualified in case same name appears in pg_catalog.
+ * The reason we include CASCADE is that the circular dependency between
+ * the type and its I/O functions makes it impossible to drop the type
+ * any other way.
*/
appendPQExpBuffer(delq, "DROP TYPE %s.",
- fmtId(tinfo->typnamespace->nspname));
+ fmtId(tinfo->dobj.namespace->dobj.name));
appendPQExpBuffer(delq, "%s CASCADE;\n",
- fmtId(tinfo->typname));
+ fmtId(tinfo->dobj.name));
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
" INTERNALLENGTH = %s",
- fmtId(tinfo->typname),
+ fmtId(tinfo->dobj.name),
(strcmp(typlen, "-1") == 0) ? "variable" : typlen);
if (fout->remoteVersion >= 70300)
{
- /* regproc result is correctly quoted in 7.3 */
+ /* regproc result is correctly quoted as of 7.3 */
appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
- if (strcmp(typreceiveoid, "0") != 0)
+ if (OidIsValid(typreceiveoid))
appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
- if (strcmp(typsendoid, "0") != 0)
+ if (OidIsValid(typsendoid))
appendPQExpBuffer(q, ",\n SEND = %s", typsend);
+ if (OidIsValid(typanalyzeoid))
+ appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
}
else
{
/* cannot combine these because fmtId uses static result area */
appendPQExpBuffer(q, ",\n INPUT = %s", fmtId(typinput));
appendPQExpBuffer(q, ",\n OUTPUT = %s", fmtId(typoutput));
- /* no chance that receive/send need be printed */
+ /* no chance that receive/send/analyze need be printed */
}
if (typdefault != NULL)
{
appendPQExpBuffer(q, ",\n DEFAULT = ");
- appendStringLiteral(q, typdefault, true);
+ if (typdefault_is_literal)
+ appendStringLiteralAH(q, typdefault, fout);
+ else
+ appendPQExpBufferStr(q, typdefault);
}
if (tinfo->isArray)
char *elemType;
/* reselect schema in case changed by function dump */
- selectSourceSchema(tinfo->typnamespace->nspname);
+ selectSourceSchema(tinfo->dobj.namespace->dobj.name);
elemType = getFormattedTypeName(tinfo->typelem, zeroAsOpaque);
appendPQExpBuffer(q, ",\n ELEMENT = %s", elemType);
free(elemType);
-
- (*deps)[depIdx++] = strdup(tinfo->typelem);
}
if (typdelim && strcmp(typdelim, ",") != 0)
{
appendPQExpBuffer(q, ",\n DELIMITER = ");
- appendStringLiteral(q, typdelim, true);
+ appendStringLiteralAH(q, typdelim, fout);
}
if (strcmp(typalign, "c") == 0)
appendPQExpBuffer(q, "\n);\n");
- (*deps)[depIdx++] = NULL; /* End of List */
-
- ArchiveEntry(fout, tinfo->oid, tinfo->typname,
- tinfo->typnamespace->nspname,
- tinfo->usename, "TYPE", deps,
- q->data, delq->data, NULL, NULL, NULL);
+ ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
+ tinfo->dobj.name,
+ tinfo->dobj.namespace->dobj.name,
+ NULL,
+ tinfo->rolname, false,
+ "TYPE", q->data, delq->data, NULL,
+ tinfo->dobj.dependencies, tinfo->dobj.nDeps,
+ NULL, NULL);
/* Dump Type Comments */
resetPQExpBuffer(q);
- appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->typname));
+ appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->dobj.name));
dumpComment(fout, q->data,
- tinfo->typnamespace->nspname, tinfo->usename,
- tinfo->oid, "pg_type", 0, NULL);
+ tinfo->dobj.namespace->dobj.name, tinfo->rolname,
+ tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
PQclear(res);
destroyPQExpBuffer(q);
}
/*
- * dumpOneDomain
+ * dumpDomain
* writes out to fout the queries to recreate a user-defined domain
- * as requested by dumpTypes
*/
static void
-dumpOneDomain(Archive *fout, TypeInfo *tinfo)
+dumpDomain(Archive *fout, TypeInfo *tinfo)
{
PQExpBuffer q = createPQExpBuffer();
PQExpBuffer delq = createPQExpBuffer();
PQExpBuffer query = createPQExpBuffer();
- PQExpBuffer chkquery = createPQExpBuffer();
PGresult *res;
int ntups;
int i;
char *typnotnull;
char *typdefn;
char *typdefault;
- char *typbasetype;
- const char *((*deps)[]);
- int depIdx = 0;
-
- deps = malloc(sizeof(char *) * 10);
+ bool typdefault_is_literal = false;
/* Set proper schema search path so type references list correctly */
- selectSourceSchema(tinfo->typnamespace->nspname);
+ selectSourceSchema(tinfo->dobj.namespace->dobj.name);
/* Fetch domain specific details */
/* We assume here that remoteVersion must be at least 70300 */
appendPQExpBuffer(query, "SELECT typnotnull, "
- "pg_catalog.format_type(typbasetype, typtypmod) as typdefn, "
- "typdefault, typbasetype "
+ "pg_catalog.format_type(typbasetype, typtypmod) as typdefn, "
+ "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) as typdefaultbin, typdefault "
"FROM pg_catalog.pg_type "
- "WHERE oid = '%s'::pg_catalog.oid",
- tinfo->oid);
+ "WHERE oid = '%u'::pg_catalog.oid",
+ tinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain domain information failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
/* Expecting a single result only */
ntups = PQntuples(res);
typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
- if (PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
- typdefault = NULL;
- else
+ if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
+ typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
+ else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
+ {
typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
- typbasetype = PQgetvalue(res, 0, PQfnumber(res, "typbasetype"));
-
- /*
- * DROP must be fully qualified in case same name appears in
- * pg_catalog
- */
- appendPQExpBuffer(delq, "DROP DOMAIN %s.",
- fmtId(tinfo->typnamespace->nspname));
- appendPQExpBuffer(delq, "%s;\n",
- fmtId(tinfo->typname));
+ typdefault_is_literal = true; /* it needs quotes */
+ }
+ else
+ typdefault = NULL;
appendPQExpBuffer(q,
"CREATE DOMAIN %s AS %s",
- fmtId(tinfo->typname),
+ fmtId(tinfo->dobj.name),
typdefn);
- /* Depends on the base type */
- (*deps)[depIdx++] = strdup(typbasetype);
-
if (typnotnull[0] == 't')
appendPQExpBuffer(q, " NOT NULL");
- if (typdefault)
- appendPQExpBuffer(q, " DEFAULT %s", typdefault);
+ if (typdefault != NULL)
+ {
+ appendPQExpBuffer(q, " DEFAULT ");
+ if (typdefault_is_literal)
+ appendStringLiteralAH(q, typdefault, fout);
+ else
+ appendPQExpBufferStr(q, typdefault);
+ }
PQclear(res);
/*
- * Fetch and process CHECK constraints for the domain
+ * Add any CHECK constraints for the domain
*/
- if (g_fout->remoteVersion >= 70400)
- appendPQExpBuffer(chkquery, "SELECT conname, "
- "pg_catalog.pg_get_constraintdef(oid) AS consrc "
- "FROM pg_catalog.pg_constraint "
- "WHERE contypid = '%s'::pg_catalog.oid",
- tinfo->oid);
- else
- appendPQExpBuffer(chkquery, "SELECT conname, 'CHECK (' || consrc || ')' AS consrc "
- "FROM pg_catalog.pg_constraint "
- "WHERE contypid = '%s'::pg_catalog.oid",
- tinfo->oid);
-
- res = PQexec(g_conn, chkquery->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
+ for (i = 0; i < tinfo->nDomChecks; i++)
{
- write_msg(NULL, "query to obtain domain constraint information failed: %s",
- PQerrorMessage(g_conn));
- exit_nicely();
- }
-
- ntups = PQntuples(res);
- for (i = 0; i < ntups; i++)
- {
- char *conname;
- char *consrc;
+ ConstraintInfo *domcheck = &(tinfo->domChecks[i]);
- conname = PQgetvalue(res, i, PQfnumber(res, "conname"));
- consrc = PQgetvalue(res, i, PQfnumber(res, "consrc"));
-
- appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
- fmtId(conname), consrc);
+ if (!domcheck->separate)
+ appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
+ fmtId(domcheck->dobj.name), domcheck->condef);
}
appendPQExpBuffer(q, ";\n");
- (*deps)[depIdx++] = NULL; /* End of List */
+ /*
+ * DROP must be fully qualified in case same name appears in pg_catalog
+ */
+ appendPQExpBuffer(delq, "DROP DOMAIN %s.",
+ fmtId(tinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, "%s;\n",
+ fmtId(tinfo->dobj.name));
- ArchiveEntry(fout, tinfo->oid, tinfo->typname,
- tinfo->typnamespace->nspname,
- tinfo->usename, "DOMAIN", deps,
- q->data, delq->data, NULL, NULL, NULL);
+ ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
+ tinfo->dobj.name,
+ tinfo->dobj.namespace->dobj.name,
+ NULL,
+ tinfo->rolname, false,
+ "DOMAIN", q->data, delq->data, NULL,
+ tinfo->dobj.dependencies, tinfo->dobj.nDeps,
+ NULL, NULL);
/* Dump Domain Comments */
resetPQExpBuffer(q);
- appendPQExpBuffer(q, "DOMAIN %s", fmtId(tinfo->typname));
+ appendPQExpBuffer(q, "DOMAIN %s", fmtId(tinfo->dobj.name));
dumpComment(fout, q->data,
- tinfo->typnamespace->nspname, tinfo->usename,
- tinfo->oid, "pg_type", 0, NULL);
+ tinfo->dobj.namespace->dobj.name, tinfo->rolname,
+ tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
- PQclear(res);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
destroyPQExpBuffer(query);
- destroyPQExpBuffer(chkquery);
}
/*
- * dumpOneCompositeType
+ * dumpCompositeType
* writes out to fout the queries to recreate a user-defined stand-alone
- * composite type as requested by dumpTypes
+ * composite type
*/
static void
-dumpOneCompositeType(Archive *fout, TypeInfo *tinfo)
+dumpCompositeType(Archive *fout, TypeInfo *tinfo)
{
PQExpBuffer q = createPQExpBuffer();
PQExpBuffer delq = createPQExpBuffer();
int i;
/* Set proper schema search path so type references list correctly */
- selectSourceSchema(tinfo->typnamespace->nspname);
+ selectSourceSchema(tinfo->dobj.namespace->dobj.name);
/* Fetch type specific details */
/* We assume here that remoteVersion must be at least 70300 */
appendPQExpBuffer(query, "SELECT a.attname, "
- "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn "
- "FROM pg_catalog.pg_type t, pg_catalog.pg_attribute a "
- "WHERE t.oid = '%s'::pg_catalog.oid "
+ "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn "
+ "FROM pg_catalog.pg_type t, pg_catalog.pg_attribute a "
+ "WHERE t.oid = '%u'::pg_catalog.oid "
"AND a.attrelid = t.typrelid "
"AND NOT a.attisdropped "
"ORDER BY a.attnum ",
- tinfo->oid);
+ tinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain data type information failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
/* Expecting at least a single result */
ntups = PQntuples(res);
i_atttypdefn = PQfnumber(res, "atttypdefn");
appendPQExpBuffer(q, "CREATE TYPE %s AS (",
- fmtId(tinfo->typname));
+ fmtId(tinfo->dobj.name));
for (i = 0; i < ntups; i++)
{
appendPQExpBuffer(q, "\n);\n");
/*
- * DROP must be fully qualified in case same name appears in
- * pg_catalog
+ * DROP must be fully qualified in case same name appears in pg_catalog
*/
appendPQExpBuffer(delq, "DROP TYPE %s.",
- fmtId(tinfo->typnamespace->nspname));
+ fmtId(tinfo->dobj.namespace->dobj.name));
appendPQExpBuffer(delq, "%s;\n",
- fmtId(tinfo->typname));
+ fmtId(tinfo->dobj.name));
+
+ ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
+ tinfo->dobj.name,
+ tinfo->dobj.namespace->dobj.name,
+ NULL,
+ tinfo->rolname, false,
+ "TYPE", q->data, delq->data, NULL,
+ tinfo->dobj.dependencies, tinfo->dobj.nDeps,
+ NULL, NULL);
- ArchiveEntry(fout, tinfo->oid, tinfo->typname,
- tinfo->typnamespace->nspname,
- tinfo->usename, "TYPE", NULL,
- q->data, delq->data, NULL, NULL, NULL);
/* Dump Type Comments */
resetPQExpBuffer(q);
- appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->typname));
+ appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->dobj.name));
dumpComment(fout, q->data,
- tinfo->typnamespace->nspname, tinfo->usename,
- tinfo->oid, "pg_type", 0, NULL);
+ tinfo->dobj.namespace->dobj.name, tinfo->rolname,
+ tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
PQclear(res);
destroyPQExpBuffer(q);
}
/*
- * dumpTypes
- * writes out to fout the queries to recreate all the user-defined types
+ * dumpShellType
+ * writes out to fout the queries to create a shell type
+ *
+ * We dump a shell definition in advance of the I/O functions for the type.
*/
-void
-dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs,
- TypeInfo *tinfo, int numTypes)
+static void
+dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
{
- int i;
+ PQExpBuffer q;
- for (i = 0; i < numTypes; i++)
- {
- /* Dump only types in dumpable namespaces */
- if (!tinfo[i].typnamespace->dump)
- continue;
+ /* Skip if not to be dumped */
+ if (!stinfo->dobj.dump || dataOnly)
+ return;
- /* skip complex types, except for standalone composite types */
- if (atooid(tinfo[i].typrelid) != 0 && tinfo[i].typrelkind != 'c')
- continue;
+ q = createPQExpBuffer();
- /* skip undefined placeholder types */
- if (!tinfo[i].isDefined)
- continue;
+ /*
+ * Note the lack of a DROP command for the shell type; any required DROP
+ * is driven off the base type entry, instead. This interacts with
+ * _printTocEntry()'s use of the presence of a DROP command to decide
+ * whether an entry needs an ALTER OWNER command. We don't want to
+ * alter the shell type's owner immediately on creation; that should
+ * happen only after it's filled in, otherwise the backend complains.
+ */
- /* skip all array types that start w/ underscore */
- if ((tinfo[i].typname[0] == '_') &&
- atooid(tinfo[i].typelem) != 0)
- continue;
+ appendPQExpBuffer(q, "CREATE TYPE %s;\n",
+ fmtId(stinfo->dobj.name));
- /* Dump out in proper style */
- if (tinfo[i].typtype == 'b')
- dumpOneBaseType(fout, &tinfo[i],
- finfo, numFuncs, tinfo, numTypes);
- else if (tinfo[i].typtype == 'd')
- dumpOneDomain(fout, &tinfo[i]);
- else if (tinfo[i].typtype == 'c')
- dumpOneCompositeType(fout, &tinfo[i]);
- }
+ ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
+ stinfo->dobj.name,
+ stinfo->dobj.namespace->dobj.name,
+ NULL,
+ stinfo->baseType->rolname, false,
+ "SHELL TYPE", q->data, "", NULL,
+ stinfo->dobj.dependencies, stinfo->dobj.nDeps,
+ NULL, NULL);
+
+ destroyPQExpBuffer(q);
}
/*
- * dumpProcLangs
- * writes out to fout the queries to recreate user-defined procedural languages
+ * Determine whether we want to dump definitions for procedural languages.
+ * Since the languages themselves don't have schemas, we can't rely on
+ * the normal schema-based selection mechanism. We choose to dump them
+ * whenever neither --schema nor --table was given. (Before 8.1, we used
+ * the dump flag of the PL's call handler function, but in 8.1 this will
+ * probably always be false since call handlers are created in pg_catalog.)
+ *
+ * For some backwards compatibility with the older behavior, we forcibly
+ * dump a PL if its handler function (and validator if any) are in a
+ * dumpable namespace. That case is not checked here.
*/
-void
-dumpProcLangs(Archive *fout, FuncInfo finfo[], int numFuncs)
+static bool
+shouldDumpProcLangs(void)
{
- PGresult *res;
- PQExpBuffer query = createPQExpBuffer();
- PQExpBuffer defqry = createPQExpBuffer();
- PQExpBuffer delqry = createPQExpBuffer();
- int ntups;
- int i_oid;
- int i_lanname;
- int i_lanpltrusted;
- int i_lanplcallfoid;
- int i_lanvalidator = -1;
- int i_lanacl = -1;
- char *lanoid;
- char *lanname;
- char *lanacl;
- const char *lanplcallfoid;
- const char *lanvalidator;
- const char *((*deps)[]);
- int depIdx;
- int i,
- fidx,
- vidx = -1;
+ if (matchingTables != NULL || matchingSchemas != NULL)
+ return false;
+ /* And they're schema not data */
+ if (dataOnly)
+ return false;
+ return true;
+}
- /* Make sure we are in proper schema */
- selectSourceSchema("pg_catalog");
+/*
+ * dumpProcLang
+ * writes out to fout the queries to recreate a user-defined
+ * procedural language
+ */
+static void
+dumpProcLang(Archive *fout, ProcLangInfo *plang)
+{
+ PQExpBuffer defqry;
+ PQExpBuffer delqry;
+ bool useParams;
+ char *qlanname;
+ char *lanschema;
+ FuncInfo *funcInfo;
+ FuncInfo *validatorInfo = NULL;
- appendPQExpBuffer(query, "SELECT oid, * FROM pg_language "
- "WHERE lanispl "
- "ORDER BY oid");
- res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain list of procedural languages failed: %s",
- PQerrorMessage(g_conn));
- exit_nicely();
- }
- ntups = PQntuples(res);
+ if (dataOnly)
+ return;
- i_lanname = PQfnumber(res, "lanname");
- i_lanpltrusted = PQfnumber(res, "lanpltrusted");
- i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
- i_oid = PQfnumber(res, "oid");
- if (fout->remoteVersion >= 70300)
- {
- i_lanvalidator = PQfnumber(res, "lanvalidator");
- i_lanacl = PQfnumber(res, "lanacl");
- }
+ /*
+ * Try to find the support function(s). It is not an error if we don't
+ * find them --- if the functions are in the pg_catalog schema, as is
+ * standard in 8.1 and up, then we won't have loaded them. (In this case
+ * we will emit a parameterless CREATE LANGUAGE command, which will
+ * require PL template knowledge in the backend to reload.)
+ */
- for (i = 0; i < ntups; i++)
- {
- lanoid = PQgetvalue(res, i, i_oid);
- lanplcallfoid = PQgetvalue(res, i, i_lanplcallfoid);
- lanname = PQgetvalue(res, i, i_lanname);
- if (fout->remoteVersion >= 70300)
- {
- lanvalidator = PQgetvalue(res, i, i_lanvalidator);
- lanacl = PQgetvalue(res, i, i_lanacl);
- }
- else
- {
- lanvalidator = "0";
- lanacl = "{=U}";
- }
+ funcInfo = findFuncByOid(plang->lanplcallfoid);
+ if (funcInfo != NULL && !funcInfo->dobj.dump)
+ funcInfo = NULL; /* treat not-dumped same as not-found */
- fidx = findFuncByOid(finfo, numFuncs, lanplcallfoid);
- if (fidx < 0)
- {
- write_msg(NULL, "handler procedure for procedural language \"%s\" not found\n",
- lanname);
- exit_nicely();
- }
+ if (OidIsValid(plang->lanvalidator))
+ {
+ validatorInfo = findFuncByOid(plang->lanvalidator);
+ if (validatorInfo != NULL && !validatorInfo->dobj.dump)
+ validatorInfo = NULL;
+ }
- if (strcmp(lanvalidator, "0") != 0)
- {
- vidx = findFuncByOid(finfo, numFuncs, lanvalidator);
- if (vidx < 0)
- {
- write_msg(NULL, "validator procedure for procedural language \"%s\" not found\n",
- lanname);
- exit_nicely();
- }
- }
+ /*
+ * If the functions are dumpable then emit a traditional CREATE LANGUAGE
+ * with parameters. Otherwise, dump only if shouldDumpProcLangs() says to
+ * dump it.
+ */
+ useParams = (funcInfo != NULL &&
+ (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
- /*
- * Current theory is to dump PLs iff their underlying functions
- * will be dumped (are in a dumpable namespace, or have a
- * non-system OID in pre-7.3 databases). Actually, we treat the
- * PL itself as being in the underlying function's namespace,
- * though it isn't really. This avoids searchpath problems for
- * the HANDLER clause.
- */
- if (!finfo[fidx].pronamespace->dump)
- continue;
+ if (!useParams && !shouldDumpProcLangs())
+ return;
- resetPQExpBuffer(defqry);
- resetPQExpBuffer(delqry);
+ defqry = createPQExpBuffer();
+ delqry = createPQExpBuffer();
- /* Make a dependency to ensure function is dumped first */
- deps = malloc(sizeof(char *) * 10);
- depIdx = 0;
+ qlanname = strdup(fmtId(plang->dobj.name));
- (*deps)[depIdx++] = strdup(lanplcallfoid);
+ /*
+ * If dumping a HANDLER clause, treat the language as being in the handler
+ * function's schema; this avoids cluttering the HANDLER clause. Otherwise
+ * it doesn't really have a schema.
+ */
+ if (useParams)
+ lanschema = funcInfo->dobj.namespace->dobj.name;
+ else
+ lanschema = NULL;
- appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
- fmtId(lanname));
+ appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
+ qlanname);
- appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
- (PQgetvalue(res, i, i_lanpltrusted)[0] == 't') ?
- "TRUSTED " : "",
- fmtId(lanname));
+ appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
+ (useParams && plang->lanpltrusted) ? "TRUSTED " : "",
+ qlanname);
+ if (useParams)
+ {
appendPQExpBuffer(defqry, " HANDLER %s",
- fmtId(finfo[fidx].proname));
- if (strcmp(lanvalidator, "0") != 0)
+ fmtId(funcInfo->dobj.name));
+ if (OidIsValid(plang->lanvalidator))
{
appendPQExpBuffer(defqry, " VALIDATOR ");
/* Cope with possibility that validator is in different schema */
- if (finfo[vidx].pronamespace != finfo[fidx].pronamespace)
+ if (validatorInfo->dobj.namespace != funcInfo->dobj.namespace)
appendPQExpBuffer(defqry, "%s.",
- fmtId(finfo[vidx].pronamespace->nspname));
+ fmtId(validatorInfo->dobj.namespace->dobj.name));
appendPQExpBuffer(defqry, "%s",
- fmtId(finfo[vidx].proname));
- (*deps)[depIdx++] = strdup(lanvalidator);
+ fmtId(validatorInfo->dobj.name));
}
- appendPQExpBuffer(defqry, ";\n");
-
- (*deps)[depIdx++] = NULL; /* End of List */
+ }
+ appendPQExpBuffer(defqry, ";\n");
- ArchiveEntry(fout, lanoid, lanname,
- finfo[fidx].pronamespace->nspname, "",
- "PROCEDURAL LANGUAGE", deps,
- defqry->data, delqry->data, NULL, NULL, NULL);
+ ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
+ plang->dobj.name,
+ lanschema, NULL, plang->lanowner,
+ false, "PROCEDURAL LANGUAGE",
+ defqry->data, delqry->data, NULL,
+ plang->dobj.dependencies, plang->dobj.nDeps,
+ NULL, NULL);
- if (!aclsSkip)
- {
- char *tmp = strdup(fmtId(lanname));
+ /* Dump Proc Lang Comments */
+ resetPQExpBuffer(defqry);
+ appendPQExpBuffer(defqry, "LANGUAGE %s", qlanname);
+ dumpComment(fout, defqry->data,
+ NULL, "",
+ plang->dobj.catId, 0, plang->dobj.dumpId);
- dumpACL(fout, "ACL LANGUAGE", tmp, lanname,
- finfo[fidx].pronamespace->nspname,
- NULL, lanacl, lanoid);
- free(tmp);
- }
- }
+ if (plang->lanpltrusted)
+ dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
+ qlanname, plang->dobj.name,
+ lanschema,
+ plang->lanowner, plang->lanacl);
- PQclear(res);
+ free(qlanname);
- destroyPQExpBuffer(query);
destroyPQExpBuffer(defqry);
destroyPQExpBuffer(delqry);
}
/*
- * dumpFuncs
- * writes out to fout the queries to recreate all the user-defined functions
+ * format_function_arguments: generate function name and argument list
+ *
+ * The argument type names are qualified if needed. The function name
+ * is never qualified.
+ *
+ * Any or all of allargtypes, argmodes, argnames may be NULL.
*/
-void
-dumpFuncs(Archive *fout, FuncInfo finfo[], int numFuncs)
+static char *
+format_function_arguments(FuncInfo *finfo, int nallargs,
+ char **allargtypes,
+ char **argmodes,
+ char **argnames)
{
- int i;
+ PQExpBufferData fn;
+ int j;
- for (i = 0; i < numFuncs; i++)
+ initPQExpBuffer(&fn);
+ appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
+ for (j = 0; j < nallargs; j++)
{
- /* Dump only funcs in dumpable namespaces */
- if (!finfo[i].pronamespace->dump)
- continue;
+ Oid typid;
+ char *typname;
+ const char *argmode;
+ const char *argname;
+
+ typid = allargtypes ? atooid(allargtypes[j]) : finfo->argtypes[j];
+ typname = getFormattedTypeName(typid, zeroAsOpaque);
+
+ if (argmodes)
+ {
+ switch (argmodes[j][0])
+ {
+ case 'i':
+ argmode = "";
+ break;
+ case 'o':
+ argmode = "OUT ";
+ break;
+ case 'b':
+ argmode = "INOUT ";
+ break;
+ default:
+ write_msg(NULL, "WARNING: bogus value in proargmodes array\n");
+ argmode = "";
+ break;
+ }
+ }
+ else
+ argmode = "";
+
+ argname = argnames ? argnames[j] : (char *) NULL;
+ if (argname && argname[0] == '\0')
+ argname = NULL;
- dumpOneFunc(fout, &finfo[i]);
- if (!aclsSkip)
- dumpFuncACL(fout, &finfo[i]);
+ appendPQExpBuffer(&fn, "%s%s%s%s%s",
+ (j > 0) ? ", " : "",
+ argmode,
+ argname ? fmtId(argname) : "",
+ argname ? " " : "",
+ typname);
+ free(typname);
}
+ appendPQExpBuffer(&fn, ")");
+ return fn.data;
}
/*
* format_function_signature: generate function name and argument list
*
- * The argument type names are qualified if needed. The function name
- * is never qualified.
+ * This is like format_function_arguments except that only a minimal
+ * list of input argument types is generated; this is sufficient to
+ * reference the function, but not to define it.
+ *
+ * If honor_quotes is false then the function name is never quoted.
+ * This is appropriate for use in TOC tags, but not in SQL commands.
*/
static char *
format_function_signature(FuncInfo *finfo, bool honor_quotes)
initPQExpBuffer(&fn);
if (honor_quotes)
- appendPQExpBuffer(&fn, "%s(", fmtId(finfo->proname));
+ appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
else
- appendPQExpBuffer(&fn, "%s(", finfo->proname);
+ appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
for (j = 0; j < finfo->nargs; j++)
{
char *typname;
typname = getFormattedTypeName(finfo->argtypes[j], zeroAsOpaque);
+
appendPQExpBuffer(&fn, "%s%s",
(j > 0) ? ", " : "",
typname);
}
-static void
-dumpFuncACL(Archive *fout, FuncInfo *finfo)
-{
- char *funcsig,
- *funcsig_tag;
-
- funcsig = format_function_signature(finfo, true);
- funcsig_tag = format_function_signature(finfo, false);
- dumpACL(fout, "FUNCTION", funcsig, funcsig_tag,
- finfo->pronamespace->nspname,
- finfo->usename, finfo->proacl, finfo->oid);
- free(funcsig);
- free(funcsig_tag);
-}
-
-
/*
- * dumpOneFunc:
- * dump out only one function
+ * dumpFunc:
+ * dump out one function
*/
static void
-dumpOneFunc(Archive *fout, FuncInfo *finfo)
+dumpFunc(Archive *fout, FuncInfo *finfo)
{
- PQExpBuffer query = createPQExpBuffer();
- PQExpBuffer q = createPQExpBuffer();
- PQExpBuffer delqry = createPQExpBuffer();
- PQExpBuffer asPart = createPQExpBuffer();
- PGresult *res = NULL;
- char *funcsig = NULL;
- char *funcsig_tag = NULL;
+ PQExpBuffer query;
+ PQExpBuffer q;
+ PQExpBuffer delqry;
+ PQExpBuffer asPart;
+ PGresult *res;
+ char *funcsig;
+ char *funcsig_tag;
int ntups;
char *proretset;
char *prosrc;
char *probin;
+ char *proallargtypes;
+ char *proargmodes;
+ char *proargnames;
char *provolatile;
char *proisstrict;
char *prosecdef;
char *lanname;
char *rettypename;
- char *funcproclang; /* Boolean : is this function a PLang
- * handler ? */
+ int nallargs;
+ char **allargtypes = NULL;
+ char **argmodes = NULL;
+ char **argnames = NULL;
- if (finfo->dumped)
- goto done;
+ /* Skip if not to be dumped */
+ if (!finfo->dobj.dump || dataOnly)
+ return;
- finfo->dumped = true;
+ query = createPQExpBuffer();
+ q = createPQExpBuffer();
+ delqry = createPQExpBuffer();
+ asPart = createPQExpBuffer();
/* Set proper schema search path so type references list correctly */
- selectSourceSchema(finfo->pronamespace->nspname);
+ selectSourceSchema(finfo->dobj.namespace->dobj.name);
/* Fetch function-specific details */
- if (g_fout->remoteVersion >= 70300)
+ if (g_fout->remoteVersion >= 80100)
+ {
+ appendPQExpBuffer(query,
+ "SELECT proretset, prosrc, probin, "
+ "proallargtypes, proargmodes, proargnames, "
+ "provolatile, proisstrict, prosecdef, "
+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
+ "FROM pg_catalog.pg_proc "
+ "WHERE oid = '%u'::pg_catalog.oid",
+ finfo->dobj.catId.oid);
+ }
+ else if (g_fout->remoteVersion >= 80000)
+ {
+ appendPQExpBuffer(query,
+ "SELECT proretset, prosrc, probin, "
+ "null as proallargtypes, "
+ "null as proargmodes, "
+ "proargnames, "
+ "provolatile, proisstrict, prosecdef, "
+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
+ "FROM pg_catalog.pg_proc "
+ "WHERE oid = '%u'::pg_catalog.oid",
+ finfo->dobj.catId.oid);
+ }
+ else if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query,
"SELECT proretset, prosrc, probin, "
+ "null as proallargtypes, "
+ "null as proargmodes, "
+ "null as proargnames, "
"provolatile, proisstrict, prosecdef, "
- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname, "
- "exists (SELECT 'x' FROM pg_catalog.pg_language WHERE lanplcallfoid = pg_catalog.pg_proc.oid) as funcproclang "
+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname "
"FROM pg_catalog.pg_proc "
- "WHERE oid = '%s'::pg_catalog.oid",
- finfo->oid);
+ "WHERE oid = '%u'::pg_catalog.oid",
+ finfo->dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 70100)
{
appendPQExpBuffer(query,
"SELECT proretset, prosrc, probin, "
- "case when proiscachable then 'i' else 'v' end as provolatile, "
+ "null as proallargtypes, "
+ "null as proargmodes, "
+ "null as proargnames, "
+ "case when proiscachable then 'i' else 'v' end as provolatile, "
"proisstrict, "
"'f'::boolean as prosecdef, "
- "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname, "
- "exists (SELECT 'x' FROM pg_language WHERE lanplcallfoid = pg_proc.oid) as funcproclang "
+ "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname "
"FROM pg_proc "
- "WHERE oid = '%s'::oid",
- finfo->oid);
+ "WHERE oid = '%u'::oid",
+ finfo->dobj.catId.oid);
}
else
{
appendPQExpBuffer(query,
"SELECT proretset, prosrc, probin, "
- "case when proiscachable then 'i' else 'v' end as provolatile, "
+ "null as proallargtypes, "
+ "null as proargmodes, "
+ "null as proargnames, "
+ "case when proiscachable then 'i' else 'v' end as provolatile, "
"'f'::boolean as proisstrict, "
"'f'::boolean as prosecdef, "
- "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname, "
- "exists (SELECT 'x' FROM pg_language WHERE lanplcallfoid = pg_proc.oid) as funcproclang "
+ "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname "
"FROM pg_proc "
- "WHERE oid = '%s'::oid",
- finfo->oid);
+ "WHERE oid = '%u'::oid",
+ finfo->dobj.catId.oid);
}
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain information on function \"%s\" failed: %s",
- finfo->proname, PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
/* Expecting a single result only */
ntups = PQntuples(res);
proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
+ proallargtypes = PQgetvalue(res, 0, PQfnumber(res, "proallargtypes"));
+ proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes"));
+ proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames"));
provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
- funcproclang = PQgetvalue(res, 0, PQfnumber(res, "funcproclang"));
/*
* See backend/commands/define.c for details of how the 'AS' clause is
if (strcmp(probin, "-") != 0)
{
appendPQExpBuffer(asPart, "AS ");
- appendStringLiteral(asPart, probin, true);
+ appendStringLiteralAH(asPart, probin, fout);
if (strcmp(prosrc, "-") != 0)
{
appendPQExpBuffer(asPart, ", ");
- appendStringLiteral(asPart, prosrc, false);
+
+ /*
+ * where we have bin, use dollar quoting if allowed and src
+ * contains quote or backslash; else use regular quoting.
+ */
+ if (disable_dollar_quoting ||
+ (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
+ appendStringLiteralAH(asPart, prosrc, fout);
+ else
+ appendStringLiteralDQ(asPart, prosrc, NULL);
}
}
else
if (strcmp(prosrc, "-") != 0)
{
appendPQExpBuffer(asPart, "AS ");
- appendStringLiteral(asPart, prosrc, false);
+ /* with no bin, dollar quote src unconditionally if allowed */
+ if (disable_dollar_quoting)
+ appendStringLiteralAH(asPart, prosrc, fout);
+ else
+ appendStringLiteralDQ(asPart, prosrc, NULL);
+ }
+ }
+
+ nallargs = finfo->nargs; /* unless we learn different from allargs */
+
+ if (proallargtypes && *proallargtypes)
+ {
+ int nitems = 0;
+
+ if (!parsePGArray(proallargtypes, &allargtypes, &nitems) ||
+ nitems < finfo->nargs)
+ {
+ write_msg(NULL, "WARNING: could not parse proallargtypes array\n");
+ if (allargtypes)
+ free(allargtypes);
+ allargtypes = NULL;
+ }
+ else
+ nallargs = nitems;
+ }
+
+ if (proargmodes && *proargmodes)
+ {
+ int nitems = 0;
+
+ if (!parsePGArray(proargmodes, &argmodes, &nitems) ||
+ nitems != nallargs)
+ {
+ write_msg(NULL, "WARNING: could not parse proargmodes array\n");
+ if (argmodes)
+ free(argmodes);
+ argmodes = NULL;
+ }
+ }
+
+ if (proargnames && *proargnames)
+ {
+ int nitems = 0;
+
+ if (!parsePGArray(proargnames, &argnames, &nitems) ||
+ nitems != nallargs)
+ {
+ write_msg(NULL, "WARNING: could not parse proargnames array\n");
+ if (argnames)
+ free(argnames);
+ argnames = NULL;
}
}
- funcsig = format_function_signature(finfo, true);
+ funcsig = format_function_arguments(finfo, nallargs, allargtypes,
+ argmodes, argnames);
funcsig_tag = format_function_signature(finfo, false);
/*
- * DROP must be fully qualified in case same name appears in
- * pg_catalog
+ * DROP must be fully qualified in case same name appears in pg_catalog
*/
appendPQExpBuffer(delqry, "DROP FUNCTION %s.%s;\n",
- fmtId(finfo->pronamespace->nspname),
+ fmtId(finfo->dobj.namespace->dobj.name),
funcsig);
rettypename = getFormattedTypeName(finfo->prorettype, zeroAsOpaque);
else if (provolatile[0] != PROVOLATILE_VOLATILE)
{
write_msg(NULL, "unrecognized provolatile value for function \"%s\"\n",
- finfo->proname);
+ finfo->dobj.name);
exit_nicely();
}
}
appendPQExpBuffer(q, ";\n");
- ArchiveEntry(fout, finfo->oid, funcsig_tag,
- finfo->pronamespace->nspname,
- finfo->usename, strcmp(funcproclang, "t") ? "FUNCTION" : "FUNC PROCEDURAL LANGUAGE", NULL,
- q->data, delqry->data,
- NULL, NULL, NULL);
+ ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
+ funcsig_tag,
+ finfo->dobj.namespace->dobj.name,
+ NULL,
+ finfo->rolname, false,
+ "FUNCTION", q->data, delqry->data, NULL,
+ finfo->dobj.dependencies, finfo->dobj.nDeps,
+ NULL, NULL);
/* Dump Function Comments */
-
resetPQExpBuffer(q);
appendPQExpBuffer(q, "FUNCTION %s", funcsig);
dumpComment(fout, q->data,
- finfo->pronamespace->nspname, finfo->usename,
- finfo->oid, "pg_proc", 0, NULL);
+ finfo->dobj.namespace->dobj.name, finfo->rolname,
+ finfo->dobj.catId, 0, finfo->dobj.dumpId);
+
+ dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
+ funcsig, funcsig_tag,
+ finfo->dobj.namespace->dobj.name,
+ finfo->rolname, finfo->proacl);
-done:
PQclear(res);
destroyPQExpBuffer(query);
destroyPQExpBuffer(asPart);
free(funcsig);
free(funcsig_tag);
+ if (allargtypes)
+ free(allargtypes);
+ if (argmodes)
+ free(argmodes);
+ if (argnames)
+ free(argnames);
}
/*
- * Dump all casts
+ * Dump a user-defined cast
*/
-void
-dumpCasts(Archive *fout,
- FuncInfo *finfo, int numFuncs,
- TypeInfo *tinfo, int numTypes)
+static void
+dumpCast(Archive *fout, CastInfo *cast)
{
- PGresult *res;
- PQExpBuffer query = createPQExpBuffer();
- PQExpBuffer defqry = createPQExpBuffer();
- PQExpBuffer delqry = createPQExpBuffer();
- int ntups;
- int i;
-
- /* Make sure we are in proper schema */
- selectSourceSchema("pg_catalog");
+ PQExpBuffer defqry;
+ PQExpBuffer delqry;
+ PQExpBuffer castsig;
+ FuncInfo *funcInfo = NULL;
+ TypeInfo *sourceInfo;
+ TypeInfo *targetInfo;
- if (fout->remoteVersion >= 70300)
- appendPQExpBuffer(query, "SELECT oid, castsource, casttarget, castfunc, castcontext FROM pg_cast ORDER BY 1,2,3;");
- else
- appendPQExpBuffer(query, "SELECT p.oid, t1.oid, t2.oid, p.oid, true FROM pg_type t1, pg_type t2, pg_proc p WHERE p.pronargs = 1 AND p.proargtypes[0] = t1.oid AND p.prorettype = t2.oid AND p.proname = t2.typname ORDER BY 1,2,3;");
+ if (dataOnly)
+ return;
- res = PQexec(g_conn, query->data);
- if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+ if (OidIsValid(cast->castfunc))
{
- write_msg(NULL, "query to obtain list of casts failed: %s",
- PQerrorMessage(g_conn));
- exit_nicely();
+ funcInfo = findFuncByOid(cast->castfunc);
+ if (funcInfo == NULL)
+ return;
}
- ntups = PQntuples(res);
- for (i = 0; i < ntups; i++)
- {
- char *castoid = PQgetvalue(res, i, 0);
- char *castsource = PQgetvalue(res, i, 1);
- char *casttarget = PQgetvalue(res, i, 2);
- char *castfunc = PQgetvalue(res, i, 3);
- char *castcontext = PQgetvalue(res, i, 4);
- int fidx = -1;
- const char *((*deps)[]);
-
- if (strcmp(castfunc, "0") != 0)
- fidx = findFuncByOid(finfo, numFuncs, castfunc);
-
- /*
- * We treat the cast as being in the namespace of the underlying
- * function. This doesn't handle binary compatible casts. Where
- * should those go?
- */
- if (fidx < 0 || !finfo[fidx].pronamespace->dump)
- continue;
-
- /* Make a dependency to ensure function is dumped first */
- if (fidx >= 0)
- {
- deps = malloc(sizeof(char *) * 2);
-
- (*deps)[0] = strdup(castfunc);
- (*deps)[1] = NULL; /* End of List */
- }
- else
- deps = NULL;
-
- resetPQExpBuffer(defqry);
- resetPQExpBuffer(delqry);
+ /*
+ * As per discussion we dump casts if one or more of the underlying
+ * objects (the conversion function and the two data types) are not
+ * builtin AND if all of the non-builtin objects are included in the dump.
+ * Builtin meaning, the namespace name does not start with "pg_".
+ */
+ sourceInfo = findTypeByOid(cast->castsource);
+ targetInfo = findTypeByOid(cast->casttarget);
- appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
- getFormattedTypeName(castsource, zeroAsNone),
- getFormattedTypeName(casttarget, zeroAsNone));
+ if (sourceInfo == NULL || targetInfo == NULL)
+ return;
- appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
- getFormattedTypeName(castsource, zeroAsNone),
- getFormattedTypeName(casttarget, zeroAsNone));
+ /*
+ * Skip this cast if all objects are from pg_
+ */
+ if ((funcInfo == NULL ||
+ strncmp(funcInfo->dobj.namespace->dobj.name, "pg_", 3) == 0) &&
+ strncmp(sourceInfo->dobj.namespace->dobj.name, "pg_", 3) == 0 &&
+ strncmp(targetInfo->dobj.namespace->dobj.name, "pg_", 3) == 0)
+ return;
- if (strcmp(castfunc, "0") == 0)
- appendPQExpBuffer(defqry, "WITHOUT FUNCTION");
- else
- appendPQExpBuffer(defqry, "WITH FUNCTION %s",
- format_function_signature(&finfo[fidx], true));
+ /*
+ * Skip cast if function isn't from pg_ and is not to be dumped.
+ */
+ if (funcInfo &&
+ strncmp(funcInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
+ !funcInfo->dobj.dump)
+ return;
- if (strcmp(castcontext, "a") == 0)
- appendPQExpBuffer(defqry, " AS ASSIGNMENT");
- else if (strcmp(castcontext, "i") == 0)
- appendPQExpBuffer(defqry, " AS IMPLICIT");
- appendPQExpBuffer(defqry, ";\n");
+ /*
+ * Same for the source type
+ */
+ if (strncmp(sourceInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
+ !sourceInfo->dobj.dump)
+ return;
- ArchiveEntry(fout, castoid,
- format_function_signature(&finfo[fidx], false),
- finfo[fidx].pronamespace->nspname, "",
- "CAST", deps,
- defqry->data, delqry->data,
- NULL, NULL, NULL);
- }
+ /*
+ * and the target type.
+ */
+ if (strncmp(targetInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 &&
+ !targetInfo->dobj.dump)
+ return;
- PQclear(res);
+ /* Make sure we are in proper schema (needed for getFormattedTypeName) */
+ selectSourceSchema("pg_catalog");
- destroyPQExpBuffer(query);
- destroyPQExpBuffer(defqry);
- destroyPQExpBuffer(delqry);
-}
+ defqry = createPQExpBuffer();
+ delqry = createPQExpBuffer();
+ castsig = createPQExpBuffer();
+ appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
+ getFormattedTypeName(cast->castsource, zeroAsNone),
+ getFormattedTypeName(cast->casttarget, zeroAsNone));
-/*
- * dumpOprs
- * writes out to fout the queries to recreate all the user-defined operators
- */
-void
-dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators)
-{
- int i;
+ appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
+ getFormattedTypeName(cast->castsource, zeroAsNone),
+ getFormattedTypeName(cast->casttarget, zeroAsNone));
- for (i = 0; i < numOperators; i++)
+ if (!OidIsValid(cast->castfunc))
+ appendPQExpBuffer(defqry, "WITHOUT FUNCTION");
+ else
{
- /* Dump only operators in dumpable namespaces */
- if (!oprinfo[i].oprnamespace->dump)
- continue;
-
/*
- * some operators are invalid because they were the result of user
- * defining operators before commutators exist
+ * Always qualify the function name, in case it is not in pg_catalog
+ * schema (format_function_signature won't qualify it).
*/
- if (strcmp(oprinfo[i].oprcode, "0") == 0)
- continue;
+ appendPQExpBuffer(defqry, "WITH FUNCTION %s.",
+ fmtId(funcInfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(defqry, "%s",
+ format_function_signature(funcInfo, true));
+ }
+
+ if (cast->castcontext == 'a')
+ appendPQExpBuffer(defqry, " AS ASSIGNMENT");
+ else if (cast->castcontext == 'i')
+ appendPQExpBuffer(defqry, " AS IMPLICIT");
+ appendPQExpBuffer(defqry, ";\n");
+
+ appendPQExpBuffer(castsig, "CAST (%s AS %s)",
+ getFormattedTypeName(cast->castsource, zeroAsNone),
+ getFormattedTypeName(cast->casttarget, zeroAsNone));
+
+ ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
+ castsig->data,
+ "pg_catalog", NULL, "",
+ false, "CAST", defqry->data, delqry->data, NULL,
+ cast->dobj.dependencies, cast->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump Cast Comments */
+ resetPQExpBuffer(defqry);
+ appendPQExpBuffer(defqry, "CAST (%s AS %s)",
+ getFormattedTypeName(cast->castsource, zeroAsNone),
+ getFormattedTypeName(cast->casttarget, zeroAsNone));
+ dumpComment(fout, defqry->data,
+ NULL, "",
+ cast->dobj.catId, 0, cast->dobj.dumpId);
- /* OK, dump it */
- dumpOneOpr(fout, &oprinfo[i],
- oprinfo, numOperators);
- }
+ destroyPQExpBuffer(defqry);
+ destroyPQExpBuffer(delqry);
+ destroyPQExpBuffer(castsig);
}
/*
- * dumpOneOpr
+ * dumpOpr
* write out a single operator definition
*/
static void
-dumpOneOpr(Archive *fout, OprInfo *oprinfo,
- OprInfo *g_oprinfo, int numOperators)
+dumpOpr(Archive *fout, OprInfo *oprinfo)
{
- PQExpBuffer query = createPQExpBuffer();
- PQExpBuffer q = createPQExpBuffer();
- PQExpBuffer delq = createPQExpBuffer();
- PQExpBuffer oprid = createPQExpBuffer();
- PQExpBuffer details = createPQExpBuffer();
+ PQExpBuffer query;
+ PQExpBuffer q;
+ PQExpBuffer delq;
+ PQExpBuffer oprid;
+ PQExpBuffer details;
const char *name;
PGresult *res;
int ntups;
char *oprltcmpop;
char *oprgtcmpop;
+ /* Skip if not to be dumped */
+ if (!oprinfo->dobj.dump || dataOnly)
+ return;
+
+ /*
+ * some operators are invalid because they were the result of user
+ * defining operators before commutators exist
+ */
+ if (!OidIsValid(oprinfo->oprcode))
+ return;
+
+ query = createPQExpBuffer();
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
+ oprid = createPQExpBuffer();
+ details = createPQExpBuffer();
+
/* Make sure we are in proper schema so regoperator works correctly */
- selectSourceSchema(oprinfo->oprnamespace->nspname);
+ selectSourceSchema(oprinfo->dobj.namespace->dobj.name);
if (g_fout->remoteVersion >= 70300)
{
"oprltcmpop::pg_catalog.regoperator, "
"oprgtcmpop::pg_catalog.regoperator "
"from pg_catalog.pg_operator "
- "where oid = '%s'::pg_catalog.oid",
- oprinfo->oid);
+ "where oid = '%u'::pg_catalog.oid",
+ oprinfo->dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 70100)
{
appendPQExpBuffer(query, "SELECT oprkind, oprcode, "
"CASE WHEN oprleft = 0 THEN '-' "
- "ELSE format_type(oprleft, NULL) END as oprleft, "
+ "ELSE format_type(oprleft, NULL) END as oprleft, "
"CASE WHEN oprright = 0 THEN '-' "
- "ELSE format_type(oprright, NULL) END as oprright, "
+ "ELSE format_type(oprright, NULL) END as oprright, "
"oprcom, oprnegate, oprrest, oprjoin, "
"oprcanhash, oprlsortop, oprrsortop, "
"0 as oprltcmpop, 0 as oprgtcmpop "
"from pg_operator "
- "where oid = '%s'::oid",
- oprinfo->oid);
+ "where oid = '%u'::oid",
+ oprinfo->dobj.catId.oid);
}
else
{
"oprcanhash, oprlsortop, oprrsortop, "
"0 as oprltcmpop, 0 as oprgtcmpop "
"from pg_operator "
- "where oid = '%s'::oid",
- oprinfo->oid);
+ "where oid = '%u'::oid",
+ oprinfo->dobj.catId.oid);
}
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain list of operators failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
/* Expecting a single result only */
ntups = PQntuples(res);
convertRegProcReference(oprcode));
appendPQExpBuffer(oprid, "%s (",
- oprinfo->oprname);
+ oprinfo->dobj.name);
/*
* right unary means there's a left arg and left unary means there's a
else
appendPQExpBuffer(oprid, ", NONE)");
- name = convertOperatorReference(oprcom, g_oprinfo, numOperators);
+ name = convertOperatorReference(oprcom);
if (name)
appendPQExpBuffer(details, ",\n COMMUTATOR = %s", name);
- name = convertOperatorReference(oprnegate, g_oprinfo, numOperators);
+ name = convertOperatorReference(oprnegate);
if (name)
appendPQExpBuffer(details, ",\n NEGATOR = %s", name);
if (name)
appendPQExpBuffer(details, ",\n JOIN = %s", name);
- name = convertOperatorReference(oprlsortop, g_oprinfo, numOperators);
+ name = convertOperatorReference(oprlsortop);
if (name)
appendPQExpBuffer(details, ",\n SORT1 = %s", name);
- name = convertOperatorReference(oprrsortop, g_oprinfo, numOperators);
+ name = convertOperatorReference(oprrsortop);
if (name)
appendPQExpBuffer(details, ",\n SORT2 = %s", name);
- name = convertOperatorReference(oprltcmpop, g_oprinfo, numOperators);
+ name = convertOperatorReference(oprltcmpop);
if (name)
appendPQExpBuffer(details, ",\n LTCMP = %s", name);
- name = convertOperatorReference(oprgtcmpop, g_oprinfo, numOperators);
+ name = convertOperatorReference(oprgtcmpop);
if (name)
appendPQExpBuffer(details, ",\n GTCMP = %s", name);
/*
- * DROP must be fully qualified in case same name appears in
- * pg_catalog
+ * DROP must be fully qualified in case same name appears in pg_catalog
*/
appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
- fmtId(oprinfo->oprnamespace->nspname),
+ fmtId(oprinfo->dobj.namespace->dobj.name),
oprid->data);
appendPQExpBuffer(q, "CREATE OPERATOR %s (\n%s\n);\n",
- oprinfo->oprname, details->data);
+ oprinfo->dobj.name, details->data);
- ArchiveEntry(fout, oprinfo->oid, oprinfo->oprname,
- oprinfo->oprnamespace->nspname, oprinfo->usename,
- "OPERATOR", NULL,
- q->data, delq->data,
- NULL, NULL, NULL);
+ ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
+ oprinfo->dobj.name,
+ oprinfo->dobj.namespace->dobj.name,
+ NULL,
+ oprinfo->rolname,
+ false, "OPERATOR", q->data, delq->data, NULL,
+ oprinfo->dobj.dependencies, oprinfo->dobj.nDeps,
+ NULL, NULL);
/* Dump Operator Comments */
-
resetPQExpBuffer(q);
appendPQExpBuffer(q, "OPERATOR %s", oprid->data);
dumpComment(fout, q->data,
- oprinfo->oprnamespace->nspname, oprinfo->usename,
- oprinfo->oid, "pg_operator", 0, NULL);
+ oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
+ oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
PQclear(res);
*
* Returns what to print, or NULL to print nothing
*
- * In 7.3 the input is a REGOPERATOR display; we have to strip the
- * argument-types part. In prior versions, the input is just a
- * numeric OID, which we search our operator list for.
+ * In 7.3 and up the input is a REGOPERATOR display; we have to strip the
+ * argument-types part, and add OPERATOR() decoration if the name is
+ * schema-qualified. In older versions, the input is just a numeric OID,
+ * which we search our operator list for.
*/
static const char *
-convertOperatorReference(const char *opr,
- OprInfo *g_oprinfo, int numOperators)
+convertOperatorReference(const char *opr)
{
- char *name;
+ OprInfo *oprInfo;
/* In all cases "0" means a null reference */
if (strcmp(opr, "0") == 0)
if (g_fout->remoteVersion >= 70300)
{
- char *paren;
+ char *name;
+ char *oname;
+ char *ptr;
bool inquote;
+ bool sawdot;
name = strdup(opr);
- /* find non-double-quoted left paren */
+ /* find non-double-quoted left paren, and check for non-quoted dot */
inquote = false;
- for (paren = name; *paren; paren++)
+ sawdot = false;
+ for (ptr = name; *ptr; ptr++)
{
- if (*paren == '(' && !inquote)
+ if (*ptr == '"')
+ inquote = !inquote;
+ else if (*ptr == '.' && !inquote)
+ sawdot = true;
+ else if (*ptr == '(' && !inquote)
{
- *paren = '\0';
+ *ptr = '\0';
break;
}
- if (*paren == '"')
- inquote = !inquote;
}
- return name;
+ /* If not schema-qualified, don't need to add OPERATOR() */
+ if (!sawdot)
+ return name;
+ oname = malloc(strlen(name) + 11);
+ sprintf(oname, "OPERATOR(%s)", name);
+ free(name);
+ return oname;
}
- name = findOprByOid(g_oprinfo, numOperators, opr);
- if (name == NULL)
+ oprInfo = findOprByOid(atooid(opr));
+ if (oprInfo == NULL)
+ {
write_msg(NULL, "WARNING: could not find operator with OID %s\n",
opr);
- return name;
-}
-
-
-/*
- * dumpOpclasses
- * writes out to fout the queries to recreate all the user-defined
- * operator classes
- */
-void
-dumpOpclasses(Archive *fout, OpclassInfo *opcinfo, int numOpclasses)
-{
- int i;
-
- for (i = 0; i < numOpclasses; i++)
- {
- /* Dump only opclasses in dumpable namespaces */
- if (!opcinfo[i].opcnamespace->dump)
- continue;
-
- /* OK, dump it */
- dumpOneOpclass(fout, &opcinfo[i]);
+ return NULL;
}
+ return oprInfo->dobj.name;
}
/*
- * dumpOneOpclass
+ * dumpOpclass
* write out a single operator class definition
*/
static void
-dumpOneOpclass(Archive *fout, OpclassInfo *opcinfo)
+dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
{
- PQExpBuffer query = createPQExpBuffer();
- PQExpBuffer q = createPQExpBuffer();
- PQExpBuffer delq = createPQExpBuffer();
+ PQExpBuffer query;
+ PQExpBuffer q;
+ PQExpBuffer delq;
PGresult *res;
int ntups;
int i_opcintype;
bool needComma;
int i;
+ /* Skip if not to be dumped */
+ if (!opcinfo->dobj.dump || dataOnly)
+ return;
+
/*
* XXX currently we do not implement dumping of operator classes from
* pre-7.3 databases. This could be done but it seems not worth the
if (g_fout->remoteVersion < 70300)
return;
+ query = createPQExpBuffer();
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
+
/* Make sure we are in proper schema so regoperator works correctly */
- selectSourceSchema(opcinfo->opcnamespace->nspname);
+ selectSourceSchema(opcinfo->dobj.namespace->dobj.name);
/* Get additional fields from the pg_opclass row */
appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
"opckeytype::pg_catalog.regtype, "
"opcdefault, "
- "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcamid) AS amname "
+ "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcamid) AS amname "
"FROM pg_catalog.pg_opclass "
- "WHERE oid = '%s'::pg_catalog.oid",
- opcinfo->oid);
+ "WHERE oid = '%u'::pg_catalog.oid",
+ opcinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain operator class details failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
/* Expecting a single result only */
ntups = PQntuples(res);
opcintype = PQgetvalue(res, 0, i_opcintype);
opckeytype = PQgetvalue(res, 0, i_opckeytype);
opcdefault = PQgetvalue(res, 0, i_opcdefault);
- amname = PQgetvalue(res, 0, i_amname);
+ /* amname will still be needed after we PQclear res */
+ amname = strdup(PQgetvalue(res, 0, i_amname));
/*
- * DROP must be fully qualified in case same name appears in
- * pg_catalog
+ * DROP must be fully qualified in case same name appears in pg_catalog
*/
appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
- fmtId(opcinfo->opcnamespace->nspname));
+ fmtId(opcinfo->dobj.namespace->dobj.name));
appendPQExpBuffer(delq, ".%s",
- fmtId(opcinfo->opcname));
+ fmtId(opcinfo->dobj.name));
appendPQExpBuffer(delq, " USING %s;\n",
fmtId(amname));
/* Build the fixed portion of the CREATE command */
appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
- fmtId(opcinfo->opcname));
+ fmtId(opcinfo->dobj.name));
if (strcmp(opcdefault, "t") == 0)
appendPQExpBuffer(q, "DEFAULT ");
appendPQExpBuffer(q, "FOR TYPE %s USING %s AS\n ",
appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
"amopopr::pg_catalog.regoperator "
"FROM pg_catalog.pg_amop "
- "WHERE amopclaid = '%s'::pg_catalog.oid "
+ "WHERE amopclaid = '%u'::pg_catalog.oid "
"ORDER BY amopstrategy",
- opcinfo->oid);
+ opcinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain operator class operators failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
appendPQExpBuffer(query, "SELECT amprocnum, "
"amproc::pg_catalog.regprocedure "
"FROM pg_catalog.pg_amproc "
- "WHERE amopclaid = '%s'::pg_catalog.oid "
+ "WHERE amopclaid = '%u'::pg_catalog.oid "
"ORDER BY amprocnum",
- opcinfo->oid);
+ opcinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain operator class functions failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
needComma = true;
}
- PQclear(res);
+ PQclear(res);
+
+ appendPQExpBuffer(q, ";\n");
+
+ ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
+ opcinfo->dobj.name,
+ opcinfo->dobj.namespace->dobj.name,
+ NULL,
+ opcinfo->rolname,
+ false, "OPERATOR CLASS", q->data, delq->data, NULL,
+ opcinfo->dobj.dependencies, opcinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump Operator Class Comments */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "OPERATOR CLASS %s",
+ fmtId(opcinfo->dobj.name));
+ appendPQExpBuffer(q, " USING %s",
+ fmtId(amname));
+ dumpComment(fout, q->data,
+ NULL, opcinfo->rolname,
+ opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
+
+ free(amname);
+ destroyPQExpBuffer(query);
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+}
+
+/*
+ * dumpConversion
+ * write out a single conversion definition
+ */
+static void
+dumpConversion(Archive *fout, ConvInfo *convinfo)
+{
+ PQExpBuffer query;
+ PQExpBuffer q;
+ PQExpBuffer delq;
+ PQExpBuffer details;
+ PGresult *res;
+ int ntups;
+ int i_conname;
+ int i_conforencoding;
+ int i_contoencoding;
+ int i_conproc;
+ int i_condefault;
+ const char *conname;
+ const char *conforencoding;
+ const char *contoencoding;
+ const char *conproc;
+ bool condefault;
+
+ /* Skip if not to be dumped */
+ if (!convinfo->dobj.dump || dataOnly)
+ return;
+
+ query = createPQExpBuffer();
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
+ details = createPQExpBuffer();
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(convinfo->dobj.namespace->dobj.name);
+
+ /* Get conversion-specific details */
+ appendPQExpBuffer(query, "SELECT conname, "
+ "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
+ "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
+ "conproc, condefault "
+ "FROM pg_catalog.pg_conversion c "
+ "WHERE c.oid = '%u'::pg_catalog.oid",
+ convinfo->dobj.catId.oid);
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ /* Expecting a single result only */
+ ntups = PQntuples(res);
+ if (ntups != 1)
+ {
+ write_msg(NULL, "Got %d rows instead of one from: %s",
+ ntups, query->data);
+ exit_nicely();
+ }
+
+ i_conname = PQfnumber(res, "conname");
+ i_conforencoding = PQfnumber(res, "conforencoding");
+ i_contoencoding = PQfnumber(res, "contoencoding");
+ i_conproc = PQfnumber(res, "conproc");
+ i_condefault = PQfnumber(res, "condefault");
- appendPQExpBuffer(q, ";\n");
+ conname = PQgetvalue(res, 0, i_conname);
+ conforencoding = PQgetvalue(res, 0, i_conforencoding);
+ contoencoding = PQgetvalue(res, 0, i_contoencoding);
+ conproc = PQgetvalue(res, 0, i_conproc);
+ condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
+
+ /*
+ * DROP must be fully qualified in case same name appears in pg_catalog
+ */
+ appendPQExpBuffer(delq, "DROP CONVERSION %s",
+ fmtId(convinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, ".%s;\n",
+ fmtId(convinfo->dobj.name));
+
+ appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
+ (condefault) ? "DEFAULT " : "",
+ fmtId(convinfo->dobj.name));
+ appendStringLiteralAH(q, conforencoding, fout);
+ appendPQExpBuffer(q, " TO ");
+ appendStringLiteralAH(q, contoencoding, fout);
+ /* regproc is automatically quoted in 7.3 and above */
+ appendPQExpBuffer(q, " FROM %s;\n", conproc);
+
+ ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
+ convinfo->dobj.name,
+ convinfo->dobj.namespace->dobj.name,
+ NULL,
+ convinfo->rolname,
+ false, "CONVERSION", q->data, delq->data, NULL,
+ convinfo->dobj.dependencies, convinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump Conversion Comments */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "CONVERSION %s", fmtId(convinfo->dobj.name));
+ dumpComment(fout, q->data,
+ convinfo->dobj.namespace->dobj.name, convinfo->rolname,
+ convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
- ArchiveEntry(fout, opcinfo->oid, opcinfo->opcname,
- opcinfo->opcnamespace->nspname, opcinfo->usename,
- "OPERATOR CLASS", NULL,
- q->data, delq->data,
- NULL, NULL, NULL);
+ PQclear(res);
destroyPQExpBuffer(query);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(details);
}
-
-/*
- * dumpAggs
- * writes out to fout the queries to create all the user-defined aggregates
- */
-void
-dumpAggs(Archive *fout, AggInfo agginfo[], int numAggs)
-{
- int i;
-
- for (i = 0; i < numAggs; i++)
- {
- /* Dump only aggs in dumpable namespaces */
- if (!agginfo[i].aggnamespace->dump)
- continue;
-
- dumpOneAgg(fout, &agginfo[i]);
- if (!aclsSkip)
- dumpAggACL(fout, &agginfo[i]);
- }
-}
-
-
/*
* format_aggregate_signature: generate aggregate name and argument list
*
format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes)
{
PQExpBufferData buf;
+ int j;
initPQExpBuffer(&buf);
if (honor_quotes)
appendPQExpBuffer(&buf, "%s",
- fmtId(agginfo->aggname));
+ fmtId(agginfo->aggfn.dobj.name));
else
- appendPQExpBuffer(&buf, "%s", agginfo->aggname);
+ appendPQExpBuffer(&buf, "%s", agginfo->aggfn.dobj.name);
- /* If using regtype or format_type, fmtbasetype is already quoted */
- if (fout->remoteVersion >= 70100)
- {
- if (agginfo->anybasetype)
- appendPQExpBuffer(&buf, "(*)");
- else
- appendPQExpBuffer(&buf, "(%s)", agginfo->fmtbasetype);
- }
+ if (agginfo->aggfn.nargs == 0)
+ appendPQExpBuffer(&buf, "(*)");
else
{
- if (agginfo->anybasetype)
- appendPQExpBuffer(&buf, "(*)");
- else
- appendPQExpBuffer(&buf, "(%s)",
- fmtId(agginfo->fmtbasetype));
- }
-
- return buf.data;
-}
+ appendPQExpBuffer(&buf, "(");
+ for (j = 0; j < agginfo->aggfn.nargs; j++)
+ {
+ char *typname;
+ typname = getFormattedTypeName(agginfo->aggfn.argtypes[j], zeroAsOpaque);
-static void
-dumpAggACL(Archive *fout, AggInfo *finfo)
-{
- char *aggsig,
- *aggsig_tag;
-
- aggsig = format_aggregate_signature(finfo, fout, true);
- aggsig_tag = format_aggregate_signature(finfo, fout, false);
- dumpACL(fout, "FUNCTION", aggsig, aggsig_tag,
- finfo->aggnamespace->nspname,
- finfo->usename, finfo->aggacl, finfo->oid);
- free(aggsig);
- free(aggsig_tag);
+ appendPQExpBuffer(&buf, "%s%s",
+ (j > 0) ? ", " : "",
+ typname);
+ free(typname);
+ }
+ appendPQExpBuffer(&buf, ")");
+ }
+ return buf.data;
}
-
/*
- * dumpOneAgg
+ * dumpAgg
* write out a single aggregate definition
*/
static void
-dumpOneAgg(Archive *fout, AggInfo *agginfo)
+dumpAgg(Archive *fout, AggInfo *agginfo)
{
- PQExpBuffer query = createPQExpBuffer();
- PQExpBuffer q = createPQExpBuffer();
- PQExpBuffer delq = createPQExpBuffer();
- PQExpBuffer details = createPQExpBuffer();
+ PQExpBuffer query;
+ PQExpBuffer q;
+ PQExpBuffer delq;
+ PQExpBuffer details;
char *aggsig;
char *aggsig_tag;
PGresult *res;
int ntups;
int i_aggtransfn;
int i_aggfinalfn;
+ int i_aggsortop;
int i_aggtranstype;
int i_agginitval;
- int i_anybasetype;
- int i_fmtbasetype;
int i_convertok;
const char *aggtransfn;
const char *aggfinalfn;
+ const char *aggsortop;
const char *aggtranstype;
const char *agginitval;
bool convertok;
+ /* Skip if not to be dumped */
+ if (!agginfo->aggfn.dobj.dump || dataOnly)
+ return;
+
+ query = createPQExpBuffer();
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
+ details = createPQExpBuffer();
+
/* Make sure we are in proper schema */
- selectSourceSchema(agginfo->aggnamespace->nspname);
+ selectSourceSchema(agginfo->aggfn.dobj.namespace->dobj.name);
/* Get aggregate-specific details */
- if (g_fout->remoteVersion >= 70300)
+ if (g_fout->remoteVersion >= 80100)
+ {
+ appendPQExpBuffer(query, "SELECT aggtransfn, "
+ "aggfinalfn, aggtranstype::pg_catalog.regtype, "
+ "aggsortop::pg_catalog.regoperator, "
+ "agginitval, "
+ "'t'::boolean as convertok "
+ "from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
+ "where a.aggfnoid = p.oid "
+ "and p.oid = '%u'::pg_catalog.oid",
+ agginfo->aggfn.dobj.catId.oid);
+ }
+ else if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(query, "SELECT aggtransfn, "
"aggfinalfn, aggtranstype::pg_catalog.regtype, "
+ "0 as aggsortop, "
"agginitval, "
- "proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype as anybasetype, "
- "proargtypes[0]::pg_catalog.regtype as fmtbasetype, "
"'t'::boolean as convertok "
- "from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
+ "from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
"where a.aggfnoid = p.oid "
- "and p.oid = '%s'::pg_catalog.oid",
- agginfo->oid);
+ "and p.oid = '%u'::pg_catalog.oid",
+ agginfo->aggfn.dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 70100)
{
appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
- "format_type(aggtranstype, NULL) as aggtranstype, "
+ "format_type(aggtranstype, NULL) as aggtranstype, "
+ "0 as aggsortop, "
"agginitval, "
- "aggbasetype = 0 as anybasetype, "
- "CASE WHEN aggbasetype = 0 THEN '-' "
- "ELSE format_type(aggbasetype, NULL) END as fmtbasetype, "
"'t'::boolean as convertok "
"from pg_aggregate "
- "where oid = '%s'::oid",
- agginfo->oid);
+ "where oid = '%u'::oid",
+ agginfo->aggfn.dobj.catId.oid);
}
else
{
appendPQExpBuffer(query, "SELECT aggtransfn1 as aggtransfn, "
"aggfinalfn, "
"(select typname from pg_type where oid = aggtranstype1) as aggtranstype, "
+ "0 as aggsortop, "
"agginitval1 as agginitval, "
- "aggbasetype = 0 as anybasetype, "
- "(select typname from pg_type where oid = aggbasetype) as fmtbasetype, "
"(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) as convertok "
"from pg_aggregate "
- "where oid = '%s'::oid",
- agginfo->oid);
+ "where oid = '%u'::oid",
+ agginfo->aggfn.dobj.catId.oid);
}
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain list of aggregate functions failed: %s",
- PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
/* Expecting a single result only */
ntups = PQntuples(res);
i_aggtransfn = PQfnumber(res, "aggtransfn");
i_aggfinalfn = PQfnumber(res, "aggfinalfn");
+ i_aggsortop = PQfnumber(res, "aggsortop");
i_aggtranstype = PQfnumber(res, "aggtranstype");
i_agginitval = PQfnumber(res, "agginitval");
- i_anybasetype = PQfnumber(res, "anybasetype");
- i_fmtbasetype = PQfnumber(res, "fmtbasetype");
i_convertok = PQfnumber(res, "convertok");
aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
+ aggsortop = PQgetvalue(res, 0, i_aggsortop);
aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
agginitval = PQgetvalue(res, 0, i_agginitval);
- /* we save anybasetype so that dumpAggACL can use it later */
- agginfo->anybasetype = (PQgetvalue(res, 0, i_anybasetype)[0] == 't');
- /* we save fmtbasetype so that dumpAggACL can use it later */
- agginfo->fmtbasetype = strdup(PQgetvalue(res, 0, i_fmtbasetype));
convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
- aggsig = format_aggregate_signature(agginfo, g_fout, true);
- aggsig_tag = format_aggregate_signature(agginfo, g_fout, false);
+ aggsig = format_aggregate_signature(agginfo, fout, true);
+ aggsig_tag = format_aggregate_signature(agginfo, fout, false);
if (!convertok)
{
write_msg(NULL, "WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n",
aggsig);
-
- appendPQExpBuffer(q, "-- WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n",
- aggsig);
- ArchiveEntry(fout, agginfo->oid, aggsig_tag,
- agginfo->aggnamespace->nspname, agginfo->usename,
- "WARNING", NULL,
- q->data, "" /* Del */ ,
- NULL, NULL, NULL);
return;
}
if (g_fout->remoteVersion >= 70300)
{
/* If using 7.3's regproc or regtype, data is already quoted */
- appendPQExpBuffer(details, " BASETYPE = %s,\n SFUNC = %s,\n STYPE = %s",
- agginfo->anybasetype ? "'any'" :
- agginfo->fmtbasetype,
+ appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
aggtransfn,
aggtranstype);
}
else if (g_fout->remoteVersion >= 70100)
{
/* format_type quotes, regproc does not */
- appendPQExpBuffer(details, " BASETYPE = %s,\n SFUNC = %s,\n STYPE = %s",
- agginfo->anybasetype ? "'any'" :
- agginfo->fmtbasetype,
+ appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
fmtId(aggtransfn),
aggtranstype);
}
else
{
/* need quotes all around */
- appendPQExpBuffer(details, " BASETYPE = %s,\n",
- agginfo->anybasetype ? "'any'" :
- fmtId(agginfo->fmtbasetype));
appendPQExpBuffer(details, " SFUNC = %s,\n",
fmtId(aggtransfn));
appendPQExpBuffer(details, " STYPE = %s",
if (!PQgetisnull(res, 0, i_agginitval))
{
appendPQExpBuffer(details, ",\n INITCOND = ");
- appendStringLiteral(details, agginitval, true);
+ appendStringLiteralAH(details, agginitval, fout);
}
if (strcmp(aggfinalfn, "-") != 0)
aggfinalfn);
}
+ aggsortop = convertOperatorReference(aggsortop);
+ if (aggsortop)
+ {
+ appendPQExpBuffer(details, ",\n SORTOP = %s",
+ aggsortop);
+ }
+
/*
- * DROP must be fully qualified in case same name appears in
- * pg_catalog
+ * DROP must be fully qualified in case same name appears in pg_catalog
*/
appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
- fmtId(agginfo->aggnamespace->nspname),
+ fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
aggsig);
appendPQExpBuffer(q, "CREATE AGGREGATE %s (\n%s\n);\n",
- fmtId(agginfo->aggname),
- details->data);
+ aggsig, details->data);
- ArchiveEntry(fout, agginfo->oid, aggsig_tag,
- agginfo->aggnamespace->nspname, agginfo->usename,
- "AGGREGATE", NULL,
- q->data, delq->data,
- NULL, NULL, NULL);
+ ArchiveEntry(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
+ aggsig_tag,
+ agginfo->aggfn.dobj.namespace->dobj.name,
+ NULL,
+ agginfo->aggfn.rolname,
+ false, "AGGREGATE", q->data, delq->data, NULL,
+ agginfo->aggfn.dobj.dependencies, agginfo->aggfn.dobj.nDeps,
+ NULL, NULL);
/* Dump Aggregate Comments */
-
resetPQExpBuffer(q);
appendPQExpBuffer(q, "AGGREGATE %s", aggsig);
- if (g_fout->remoteVersion >= 70300)
- dumpComment(fout, q->data,
- agginfo->aggnamespace->nspname, agginfo->usename,
- agginfo->oid, "pg_proc", 0, NULL);
- else
- dumpComment(fout, q->data,
- agginfo->aggnamespace->nspname, agginfo->usename,
- agginfo->oid, "pg_aggregate", 0, NULL);
+ dumpComment(fout, q->data,
+ agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
+ agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
+
+ /*
+ * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
+ * command look like a function's GRANT; in particular this affects the
+ * syntax for zero-argument aggregates.
+ */
+ free(aggsig);
+ free(aggsig_tag);
+
+ aggsig = format_function_signature(&agginfo->aggfn, true);
+ aggsig_tag = format_function_signature(&agginfo->aggfn, false);
+
+ dumpACL(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
+ "FUNCTION",
+ aggsig, aggsig_tag,
+ agginfo->aggfn.dobj.namespace->dobj.name,
+ agginfo->aggfn.rolname, agginfo->aggfn.proacl);
+
+ free(aggsig);
+ free(aggsig_tag);
PQclear(res);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
destroyPQExpBuffer(details);
- free(aggsig);
- free(aggsig_tag);
}
/*----------
* Write out grant/revoke information
*
- * 'type' must be TABLE, FUNCTION, LANGUAGE, or SCHEMA.
+ * 'objCatId' is the catalog ID of the underlying object.
+ * 'objDumpId' is the dump ID of the underlying object.
+ * 'type' must be TABLE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, or TABLESPACE.
* 'name' is the formatted name of the object. Must be quoted etc. already.
* 'tag' is the tag for the archive entry (typ. unquoted name of object).
* 'nspname' is the namespace the object is in (NULL if none).
* 'owner' is the owner, NULL if there is no owner (for languages).
* 'acls' is the string read out of the fooacl system catalog field;
* it will be parsed here.
- * 'objoid' is the OID of the object for purposes of ordering.
*----------
*/
static void
-dumpACL(Archive *fout, const char *type, const char *name,
+dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
+ const char *type, const char *name,
const char *tag, const char *nspname, const char *owner,
- const char *acls, const char *objoid)
+ const char *acls)
{
PQExpBuffer sql;
- /*
- * acl_lang is a flag only true if we are dumping language's ACL, so
- * we can set 'type' to a value that is suitable to build SQL requests
- * as for other types.
- */
- bool acl_lang = false;
-
- if (!strcmp(type, "ACL LANGUAGE"))
- {
- type = "LANGUAGE";
- acl_lang = true;
- }
+ /* Do nothing if ACL dump is not enabled */
+ if (dataOnly || aclsSkip)
+ return;
sql = createPQExpBuffer();
}
if (sql->len > 0)
- ArchiveEntry(fout, objoid, tag, nspname,
+ ArchiveEntry(fout, nilCatalogId, createDumpId(),
+ tag, nspname,
+ NULL,
owner ? owner : "",
- acl_lang ? "ACL LANGUAGE" : "ACL",
- NULL, sql->data, "", NULL, NULL, NULL);
+ false, "ACL", sql->data, "", NULL,
+ &(objDumpId), 1,
+ NULL, NULL);
destroyPQExpBuffer(sql);
}
-
-static void
-dumpTableACL(Archive *fout, TableInfo *tbinfo)
-{
- char *namecopy = strdup(fmtId(tbinfo->relname));
- char *dumpoid;
-
- /*
- * Choose OID to use for sorting ACL into position. For a view, sort
- * by the view OID; for a serial sequence, sort by the owning table's
- * OID; otherwise use the table's own OID.
- */
- if (tbinfo->viewoid != NULL)
- dumpoid = tbinfo->viewoid;
- else if (tbinfo->owning_tab != NULL)
- dumpoid = tbinfo->owning_tab;
- else
- dumpoid = tbinfo->oid;
-
- dumpACL(fout, "TABLE", namecopy, tbinfo->relname,
- tbinfo->relnamespace->nspname, tbinfo->usename, tbinfo->relacl,
- dumpoid);
-
- free(namecopy);
-}
-
-
/*
- * dumpTables:
- * write out to fout the declarations (not data) of all user-defined tables
+ * dumpTable
+ * write out to fout the declarations (not data) of a user-defined table
*/
-void
-dumpTables(Archive *fout, TableInfo tblinfo[], int numTables,
- const bool aclsSkip, const bool schemaOnly, const bool dataOnly)
+static void
+dumpTable(Archive *fout, TableInfo *tbinfo)
{
- int i;
-
- /*
- * Dump non-serial sequences first, in case they are referenced in
- * table defn's
- */
- for (i = 0; i < numTables; i++)
- {
- TableInfo *tbinfo = &tblinfo[i];
-
- if (tbinfo->relkind != RELKIND_SEQUENCE)
- continue;
+ char *namecopy;
- if (tbinfo->dump && tbinfo->owning_tab == NULL)
- {
- dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly);
- if (!dataOnly && !aclsSkip)
- dumpTableACL(fout, tbinfo);
- }
- }
-
- if (!dataOnly)
- {
- for (i = 0; i < numTables; i++)
- {
- TableInfo *tbinfo = &tblinfo[i];
-
- if (tbinfo->relkind == RELKIND_SEQUENCE) /* already dumped */
- continue;
-
- if (tbinfo->dump)
- {
- dumpOneTable(fout, tbinfo, tblinfo);
- if (!aclsSkip)
- dumpTableACL(fout, tbinfo);
- }
- }
- }
-
- /*
- * Dump serial sequences last (we will not emit any CREATE commands,
- * but we do have to think about ACLs and setval operations).
- */
- for (i = 0; i < numTables; i++)
+ if (tbinfo->dobj.dump)
{
- TableInfo *tbinfo = &tblinfo[i];
-
- if (tbinfo->relkind != RELKIND_SEQUENCE)
- continue;
-
- if (tbinfo->dump && tbinfo->owning_tab != NULL)
- {
- dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly);
- if (!dataOnly && !aclsSkip)
- dumpTableACL(fout, tbinfo);
- }
+ if (tbinfo->relkind == RELKIND_SEQUENCE)
+ dumpSequence(fout, tbinfo);
+ else if (!dataOnly)
+ dumpTableSchema(fout, tbinfo);
+
+ /* Handle the ACL here */
+ namecopy = strdup(fmtId(tbinfo->dobj.name));
+ dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
+ (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE",
+ namecopy, tbinfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+ tbinfo->relacl);
+ free(namecopy);
}
}
/*
- * dumpOneTable
+ * dumpTableSchema
* write the declaration (not data) of one user-defined table or view
*/
static void
-dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
+dumpTableSchema(Archive *fout, TableInfo *tbinfo)
{
PQExpBuffer query = createPQExpBuffer();
PQExpBuffer q = createPQExpBuffer();
PQExpBuffer delq = createPQExpBuffer();
PGresult *res;
int numParents;
- int *parentIndexes;
+ TableInfo **parents;
int actual_atts; /* number of attrs in this CREATE statment */
char *reltypename;
char *storage;
- char *objoid;
- const char *((*commentDeps)[]);
int j,
k;
/* Make sure we are in proper schema */
- selectSourceSchema(tbinfo->relnamespace->nspname);
+ selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
/* Is it a table or a view? */
if (tbinfo->relkind == RELKIND_VIEW)
/* Fetch the view definition */
if (g_fout->remoteVersion >= 70300)
{
- /* Beginning in 7.3, viewname is not unique; use OID */
- appendPQExpBuffer(query, "SELECT pg_catalog.pg_get_viewdef(ev_class) as viewdef, "
- "oid as view_oid"
- " from pg_catalog.pg_rewrite where"
- " ev_class = '%s'::pg_catalog.oid and"
- " rulename = '_RETURN';",
- tbinfo->oid);
+ /* Beginning in 7.3, viewname is not unique; rely on OID */
+ appendPQExpBuffer(query,
+ "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) as viewdef",
+ tbinfo->dobj.catId.oid);
}
else
{
- appendPQExpBuffer(query, "SELECT definition as viewdef, "
- "(select oid from pg_rewrite where "
- " rulename=('_RET' || viewname)::name) as view_oid"
+ appendPQExpBuffer(query, "SELECT definition as viewdef "
" from pg_views where viewname = ");
- appendStringLiteral(query, tbinfo->relname, true);
+ appendStringLiteralAH(query, tbinfo->dobj.name, fout);
appendPQExpBuffer(query, ";");
}
res = PQexec(g_conn, query->data);
- if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain definition of view \"%s\" failed: %s",
- tbinfo->relname, PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
if (PQntuples(res) != 1)
{
if (PQntuples(res) < 1)
write_msg(NULL, "query to obtain definition of view \"%s\" returned no data\n",
- tbinfo->relname);
+ tbinfo->dobj.name);
else
write_msg(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n",
- tbinfo->relname);
- exit_nicely();
- }
-
- if (PQgetisnull(res, 0, 1))
- {
- write_msg(NULL, "query to obtain definition of view \"%s\" returned null OID\n",
- tbinfo->relname);
+ tbinfo->dobj.name);
exit_nicely();
}
if (strlen(viewdef) == 0)
{
write_msg(NULL, "definition of view \"%s\" appears to be empty (length zero)\n",
- tbinfo->relname);
+ tbinfo->dobj.name);
exit_nicely();
}
- /* We use the OID of the view rule as the object OID */
- objoid = strdup(PQgetvalue(res, 0, 1));
- /* Save it for use by dumpACL, too */
- tbinfo->viewoid = objoid;
-
/*
* DROP must be fully qualified in case same name appears in
* pg_catalog
*/
appendPQExpBuffer(delq, "DROP VIEW %s.",
- fmtId(tbinfo->relnamespace->nspname));
+ fmtId(tbinfo->dobj.namespace->dobj.name));
appendPQExpBuffer(delq, "%s;\n",
- fmtId(tbinfo->relname));
+ fmtId(tbinfo->dobj.name));
appendPQExpBuffer(q, "CREATE VIEW %s AS\n %s\n",
- fmtId(tbinfo->relname), viewdef);
+ fmtId(tbinfo->dobj.name), viewdef);
PQclear(res);
-
- /*
- * Views can have default values -- however, they must be
- * specified in an ALTER TABLE command after the view has been
- * created, not in the view definition itself.
- */
- for (j = 0; j < tbinfo->numatts; j++)
- {
- if (tbinfo->adef_expr[j] != NULL && !tbinfo->inhAttrDef[j])
- {
- appendPQExpBuffer(q, "ALTER TABLE %s ",
- fmtId(tbinfo->relname));
- appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n",
- fmtId(tbinfo->attnames[j]),
- tbinfo->adef_expr[j]);
- }
- }
-
- commentDeps = malloc(sizeof(char *) * 2);
- (*commentDeps)[0] = strdup(objoid);
- (*commentDeps)[1] = NULL; /* end of list */
}
else
{
reltypename = "TABLE";
- objoid = tbinfo->oid;
- commentDeps = NULL;
numParents = tbinfo->numParents;
- parentIndexes = tbinfo->parentIndexes;
+ parents = tbinfo->parents;
/*
* DROP must be fully qualified in case same name appears in
* pg_catalog
*/
appendPQExpBuffer(delq, "DROP TABLE %s.",
- fmtId(tbinfo->relnamespace->nspname));
+ fmtId(tbinfo->dobj.namespace->dobj.name));
appendPQExpBuffer(delq, "%s;\n",
- fmtId(tbinfo->relname));
+ fmtId(tbinfo->dobj.name));
appendPQExpBuffer(q, "CREATE TABLE %s (",
- fmtId(tbinfo->relname));
+ fmtId(tbinfo->dobj.name));
actual_atts = 0;
for (j = 0; j < tbinfo->numatts; j++)
{
/* Attribute type */
if (g_fout->remoteVersion >= 70100)
{
- char *typname = tbinfo->atttypnames[j];
-
- if (tbinfo->attisserial[j])
- {
- if (strcmp(typname, "integer") == 0)
- typname = "serial";
- else if (strcmp(typname, "bigint") == 0)
- typname = "bigserial";
- }
- appendPQExpBuffer(q, "%s", typname);
+ appendPQExpBuffer(q, "%s",
+ tbinfo->atttypnames[j]);
}
else
{
tbinfo->atttypmod[j]));
}
- /* Default value --- suppress if inherited or serial */
- if (tbinfo->adef_expr[j] != NULL &&
+ /*
+ * Default value --- suppress if inherited or to be
+ * printed separately.
+ */
+ if (tbinfo->attrdefs[j] != NULL &&
!tbinfo->inhAttrDef[j] &&
- !tbinfo->attisserial[j])
+ !tbinfo->attrdefs[j]->separate)
appendPQExpBuffer(q, " DEFAULT %s",
- tbinfo->adef_expr[j]);
+ tbinfo->attrdefs[j]->adef_expr);
/*
* Not Null constraint --- suppress if inherited
- *
- * Note: we could suppress this for serial columns since
- * SERIAL implies NOT NULL. We choose not to for forward
- * compatibility, since there has been some talk of making
- * SERIAL not imply NOT NULL, in which case the explicit
- * specification would be needed.
*/
if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j])
appendPQExpBuffer(q, " NOT NULL");
}
/*
- * Add non-inherited CHECK constraints, if any. If a constraint
- * matches by name and condition with a constraint belonging to a
- * parent class (OR conditions match and both names start with
- * '$'), we assume it was inherited.
+ * Add non-inherited CHECK constraints, if any.
*/
- if (tbinfo->ncheck > 0)
+ for (j = 0; j < tbinfo->ncheck; j++)
{
- PGresult *res2;
- int i_conname,
- i_consrc;
- int ntups2;
-
- if (g_verbose)
- write_msg(NULL, "finding check constraints for table \"%s\"\n",
- tbinfo->relname);
-
- resetPQExpBuffer(query);
- if (g_fout->remoteVersion >= 70400)
- appendPQExpBuffer(query, "SELECT conname, "
- " pg_catalog.pg_get_constraintdef(c1.oid) AS consrc "
- " from pg_catalog.pg_constraint c1 "
- " where conrelid = '%s'::pg_catalog.oid "
- " and contype = 'c' "
- " and not exists "
- " (select 1 from "
- " pg_catalog.pg_constraint c2, "
- " pg_catalog.pg_inherits i "
- " where i.inhrelid = c1.conrelid "
- " and (c2.conname = c1.conname "
- " or (c2.conname[0] = '$' "
- " and c1.conname[0] = '$')"
- " )"
- " and pg_catalog.pg_get_constraintdef(c2.oid) "
- " = pg_catalog.pg_get_constraintdef(c1.oid) "
- " and c2.conrelid = i.inhparent) "
- " order by conname ",
- tbinfo->oid);
- else if (g_fout->remoteVersion >= 70300)
- appendPQExpBuffer(query, "SELECT conname, "
- " 'CHECK (' || consrc || ')' AS consrc"
- " from pg_catalog.pg_constraint c1"
- " where conrelid = '%s'::pg_catalog.oid "
- " and contype = 'c' "
- " and not exists "
- " (select 1 from "
- " pg_catalog.pg_constraint c2, "
- " pg_catalog.pg_inherits i "
- " where i.inhrelid = c1.conrelid "
- " and (c2.conname = c1.conname "
- " or (c2.conname[0] = '$' "
- " and c1.conname[0] = '$')"
- " )"
- " and c2.consrc = c1.consrc "
- " and c2.conrelid = i.inhparent) "
- " order by conname ",
- tbinfo->oid);
- else
- appendPQExpBuffer(query, "SELECT rcname as conname,"
- " 'CHECK (' || rcsrc || ')' as consrc"
- " from pg_relcheck c1"
- " where rcrelid = '%s'::oid "
- " and not exists "
- " (select 1 from pg_relcheck c2, "
- " pg_inherits i "
- " where i.inhrelid = c1.rcrelid "
- " and (c2.rcname = c1.rcname "
- " or (c2.rcname[0] = '$' "
- " and c1.rcname[0] = '$')"
- " )"
- " and c2.rcsrc = c1.rcsrc "
- " and c2.rcrelid = i.inhparent) "
- " order by rcname ",
- tbinfo->oid);
- res2 = PQexec(g_conn, query->data);
- if (!res2 ||
- PQresultStatus(res2) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain check constraints failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
- ntups2 = PQntuples(res2);
- if (ntups2 > tbinfo->ncheck)
- {
- write_msg(NULL, "expected %d check constraints on table \"%s\" but found %d\n",
- tbinfo->ncheck, tbinfo->relname, ntups2);
- write_msg(NULL, "(The system catalogs might be corrupted.)\n");
- exit_nicely();
- }
+ ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
- i_conname = PQfnumber(res2, "conname");
- i_consrc = PQfnumber(res2, "consrc");
+ if (constr->coninherited || constr->separate)
+ continue;
- for (j = 0; j < ntups2; j++)
- {
- const char *name = PQgetvalue(res2, j, i_conname);
- const char *expr = PQgetvalue(res2, j, i_consrc);
+ if (actual_atts > 0)
+ appendPQExpBuffer(q, ",\n ");
- if (actual_atts + j > 0)
- appendPQExpBuffer(q, ",\n ");
+ appendPQExpBuffer(q, "CONSTRAINT %s ",
+ fmtId(constr->dobj.name));
+ appendPQExpBuffer(q, "%s", constr->condef);
- appendPQExpBuffer(q, "CONSTRAINT %s ",
- fmtId(name));
- appendPQExpBuffer(q, "%s", expr);
- }
- PQclear(res2);
+ actual_atts++;
}
- /*
- * Primary Key: In versions of PostgreSQL prior to 7.2, we needed
- * to include the primary key in the table definition. However,
- * this is not ideal because it creates an index on the table,
- * which makes COPY slower. As of release 7.2, we can add primary
- * keys to a table after it has been created, using ALTER TABLE;
- * see dumpIndexes() for more information. Therefore, we ignore
- * primary keys in this function.
- */
-
appendPQExpBuffer(q, "\n)");
if (numParents > 0)
appendPQExpBuffer(q, "\nINHERITS (");
for (k = 0; k < numParents; k++)
{
- TableInfo *parentRel = &g_tblinfo[parentIndexes[k]];
+ TableInfo *parentRel = parents[k];
if (k > 0)
appendPQExpBuffer(q, ", ");
- if (parentRel->relnamespace != tbinfo->relnamespace)
+ if (parentRel->dobj.namespace != tbinfo->dobj.namespace)
appendPQExpBuffer(q, "%s.",
- fmtId(parentRel->relnamespace->nspname));
+ fmtId(parentRel->dobj.namespace->dobj.name));
appendPQExpBuffer(q, "%s",
- fmtId(parentRel->relname));
+ fmtId(parentRel->dobj.name));
}
appendPQExpBuffer(q, ")");
}
- if (!tbinfo->hasoids)
- appendPQExpBuffer(q, " WITHOUT OIDS");
+ if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0)
+ appendPQExpBuffer(q, "\nWITH (%s)", tbinfo->reloptions);
appendPQExpBuffer(q, ";\n");
for (j = 0; j < tbinfo->numatts; j++)
{
/*
- * Dump per-column statistics information. We only issue an
- * ALTER TABLE statement if the attstattarget entry for this
- * column is non-negative (i.e. it's not the default value)
+ * Dump per-column statistics information. We only issue an ALTER
+ * TABLE statement if the attstattarget entry for this column is
+ * non-negative (i.e. it's not the default value)
*/
if (tbinfo->attstattarget[j] >= 0 &&
!tbinfo->attisdropped[j])
{
appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
- fmtId(tbinfo->relname));
+ fmtId(tbinfo->dobj.name));
appendPQExpBuffer(q, "ALTER COLUMN %s ",
fmtId(tbinfo->attnames[j]));
appendPQExpBuffer(q, "SET STATISTICS %d;\n",
/*
* Dump per-column storage information. The statement is only
- * dumped if the storage has been changed from the type's
- * default.
+ * dumped if the storage has been changed from the type's default.
*/
if (!tbinfo->attisdropped[j] && tbinfo->attstorage[j] != tbinfo->typstorage[j])
{
}
/*
- * Only dump the statement if it's a storage type we
- * recognize
+ * Only dump the statement if it's a storage type we recognize
*/
if (storage != NULL)
{
appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
- fmtId(tbinfo->relname));
+ fmtId(tbinfo->dobj.name));
appendPQExpBuffer(q, "ALTER COLUMN %s ",
fmtId(tbinfo->attnames[j]));
appendPQExpBuffer(q, "SET STORAGE %s;\n",
}
}
- ArchiveEntry(fout, objoid, tbinfo->relname,
- tbinfo->relnamespace->nspname, tbinfo->usename,
- reltypename, NULL, q->data, delq->data,
- NULL, NULL, NULL);
+ ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
+ tbinfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name,
+ (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace,
+ tbinfo->rolname,
+ (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false,
+ reltypename, q->data, delq->data, NULL,
+ tbinfo->dobj.dependencies, tbinfo->dobj.nDeps,
+ NULL, NULL);
/* Dump Table Comments */
- dumpTableComment(fout, tbinfo, reltypename, commentDeps);
+ dumpTableComment(fout, tbinfo, reltypename);
+
+ /* Dump comments on inlined table constraints */
+ for (j = 0; j < tbinfo->ncheck; j++)
+ {
+ ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
+
+ if (constr->coninherited || constr->separate)
+ continue;
- /* commentDeps now belongs to the archive entry ... don't free it! */
+ dumpTableConstraintComment(fout, constr);
+ }
destroyPQExpBuffer(query);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
}
+/*
+ * dumpAttrDef --- dump an attribute's default-value declaration
+ */
+static void
+dumpAttrDef(Archive *fout, AttrDefInfo *adinfo)
+{
+ TableInfo *tbinfo = adinfo->adtable;
+ int adnum = adinfo->adnum;
+ PQExpBuffer q;
+ PQExpBuffer delq;
+
+ /* Only print it if "separate" mode is selected */
+ if (!tbinfo->dobj.dump || !adinfo->separate || dataOnly)
+ return;
+
+ /* Don't print inherited defaults, either */
+ if (tbinfo->inhAttrDef[adnum - 1])
+ return;
+
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
+
+ appendPQExpBuffer(q, "ALTER TABLE %s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n",
+ fmtId(tbinfo->attnames[adnum - 1]),
+ adinfo->adef_expr);
+
+ /*
+ * DROP must be fully qualified in case same name appears in pg_catalog
+ */
+ appendPQExpBuffer(delq, "ALTER TABLE %s.",
+ fmtId(tbinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, "%s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(delq, "ALTER COLUMN %s DROP DEFAULT;\n",
+ fmtId(tbinfo->attnames[adnum - 1]));
+
+ ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
+ tbinfo->attnames[adnum - 1],
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname,
+ false, "DEFAULT", q->data, delq->data, NULL,
+ adinfo->dobj.dependencies, adinfo->dobj.nDeps,
+ NULL, NULL);
+
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+}
+
/*
* getAttrName: extract the correct name for an attribute
*
return "tableoid";
}
write_msg(NULL, "invalid column number %d for table \"%s\"\n",
- attrnum, tblInfo->relname);
+ attrnum, tblInfo->dobj.name);
exit_nicely();
return NULL; /* keep compiler quiet */
}
/*
- * dumpIndexes:
- * write out to fout all the user-defined indexes for dumpable tables
+ * dumpIndex
+ * write out to fout a user-defined index
*/
-void
-dumpIndexes(Archive *fout, TableInfo *tblinfo, int numTables)
+static void
+dumpIndex(Archive *fout, IndxInfo *indxinfo)
{
- int i,
- j;
- PQExpBuffer query = createPQExpBuffer();
- PQExpBuffer q = createPQExpBuffer();
- PQExpBuffer delq = createPQExpBuffer();
- PGresult *res;
- int ntups;
- int i_indexreloid;
- int i_indexrelname;
- int i_indexdef;
- int i_contype;
- int i_conoid;
- int i_indkey;
- int i_indisclustered;
- int i_indnkeys;
-
- for (i = 0; i < numTables; i++)
- {
- TableInfo *tbinfo = &tblinfo[i];
-
- /* Only plain tables have indexes */
- if (tbinfo->relkind != RELKIND_RELATION || !tbinfo->hasindex)
- continue;
+ TableInfo *tbinfo = indxinfo->indextable;
+ PQExpBuffer q;
+ PQExpBuffer delq;
- if (!tbinfo->dump)
- continue;
+ if (dataOnly)
+ return;
- /* Make sure we are in proper schema so indexdef is right */
- selectSourceSchema(tbinfo->relnamespace->nspname);
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
- /*
- * The point of the messy-looking outer join is to find a
- * constraint that is related by an internal dependency link to
- * the index. If we find one, we emit an ADD CONSTRAINT command
- * instead of a CREATE INDEX command. We assume an index won't
- * have more than one internal dependency.
- */
- resetPQExpBuffer(query);
- if (g_fout->remoteVersion >= 70300)
- appendPQExpBuffer(query,
- "SELECT i.indexrelid as indexreloid, "
- "coalesce(c.conname, t.relname) as indexrelname, "
- "pg_catalog.pg_get_indexdef(i.indexrelid) as indexdef, "
- "i.indkey, i.indisclustered, "
- "t.relnatts as indnkeys, "
- "coalesce(c.contype, '0') as contype, "
- "coalesce(c.oid, '0') as conoid "
- "FROM pg_catalog.pg_index i "
- "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
- "LEFT JOIN pg_catalog.pg_depend d "
- "ON (d.classid = t.tableoid "
- "AND d.objid = t.oid "
- "AND d.deptype = 'i') "
- "LEFT JOIN pg_catalog.pg_constraint c "
- "ON (d.refclassid = c.tableoid "
- "AND d.refobjid = c.oid) "
- "WHERE i.indrelid = '%s'::pg_catalog.oid "
- "ORDER BY indexrelname",
- tbinfo->oid);
- else
- appendPQExpBuffer(query,
- "SELECT i.indexrelid as indexreloid, "
- "t.relname as indexrelname, "
- "pg_get_indexdef(i.indexrelid) as indexdef, "
- "i.indkey, false as indisclustered, "
- "t.relnatts as indnkeys, "
- "CASE WHEN i.indisprimary THEN 'p'::char "
- "ELSE '0'::char END as contype, "
- "0::oid as conoid "
- "FROM pg_index i, pg_class t "
- "WHERE t.oid = i.indexrelid "
- "AND i.indrelid = '%s'::oid "
- "ORDER BY indexrelname",
- tbinfo->oid);
+ /*
+ * If there's an associated constraint, don't dump the index per se, but
+ * do dump any comment for it. (This is safe because dependency ordering
+ * will have ensured the constraint is emitted first.)
+ */
+ if (indxinfo->indexconstraint == 0)
+ {
+ /* Plain secondary index */
+ appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
- res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
+ /* If the index is clustered, we need to record that. */
+ if (indxinfo->indisclustered)
{
- write_msg(NULL, "query to obtain list of indexes failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
+ appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, " ON %s;\n",
+ fmtId(indxinfo->dobj.name));
}
- ntups = PQntuples(res);
+ /*
+ * DROP must be fully qualified in case same name appears in
+ * pg_catalog
+ */
+ appendPQExpBuffer(delq, "DROP INDEX %s.",
+ fmtId(tbinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, "%s;\n",
+ fmtId(indxinfo->dobj.name));
- i_indexreloid = PQfnumber(res, "indexreloid");
- i_indexrelname = PQfnumber(res, "indexrelname");
- i_indexdef = PQfnumber(res, "indexdef");
- i_contype = PQfnumber(res, "contype");
- i_conoid = PQfnumber(res, "conoid");
- i_indkey = PQfnumber(res, "indkey");
- i_indisclustered = PQfnumber(res, "indisclustered");
- i_indnkeys = PQfnumber(res, "indnkeys");
+ ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
+ indxinfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name,
+ indxinfo->tablespace,
+ tbinfo->rolname, false,
+ "INDEX", q->data, delq->data, NULL,
+ indxinfo->dobj.dependencies, indxinfo->dobj.nDeps,
+ NULL, NULL);
+ }
- for (j = 0; j < ntups; j++)
- {
- const char *indexreloid = PQgetvalue(res, j, i_indexreloid);
- const char *indexrelname = PQgetvalue(res, j, i_indexrelname);
- const char *indexdef = PQgetvalue(res, j, i_indexdef);
- char contype = *(PQgetvalue(res, j, i_contype));
- const char *conoid = PQgetvalue(res, j, i_conoid);
- bool indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
+ /* Dump Index Comments */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "INDEX %s",
+ fmtId(indxinfo->dobj.name));
+ dumpComment(fout, q->data,
+ tbinfo->dobj.namespace->dobj.name,
+ tbinfo->rolname,
+ indxinfo->dobj.catId, 0, indxinfo->dobj.dumpId);
- resetPQExpBuffer(q);
- resetPQExpBuffer(delq);
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+}
- if (contype == 'p' || contype == 'u')
- {
- /*
- * If we found a constraint matching the index, emit ADD
- * CONSTRAINT not CREATE INDEX.
- *
- * In a pre-7.3 database, we take this path iff the index was
- * marked indisprimary.
- */
- int indnkeys = atoi(PQgetvalue(res, j, i_indnkeys));
- char **indkeys = (char **) malloc(indnkeys * sizeof(char *));
- int k;
+/*
+ * dumpConstraint
+ * write out to fout a user-defined constraint
+ */
+static void
+dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
+{
+ TableInfo *tbinfo = coninfo->contable;
+ PQExpBuffer q;
+ PQExpBuffer delq;
- parseNumericArray(PQgetvalue(res, j, i_indkey),
- indkeys, indnkeys);
+ /* Skip if not to be dumped */
+ if (!coninfo->dobj.dump || dataOnly)
+ return;
- appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
- fmtId(tbinfo->relname));
- appendPQExpBuffer(q, " ADD CONSTRAINT %s %s (",
- fmtId(indexrelname),
- contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
+ q = createPQExpBuffer();
+ delq = createPQExpBuffer();
- for (k = 0; k < indnkeys; k++)
- {
- int indkey = atoi(indkeys[k]);
- const char *attname;
+ if (coninfo->contype == 'p' || coninfo->contype == 'u')
+ {
+ /* Index-related constraint */
+ IndxInfo *indxinfo;
+ int k;
- if (indkey == InvalidAttrNumber)
- break;
- attname = getAttrName(indkey, tbinfo);
+ indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
- appendPQExpBuffer(q, "%s%s",
- (k == 0) ? "" : ", ",
- fmtId(attname));
- }
+ if (indxinfo == NULL)
+ {
+ write_msg(NULL, "missing index for constraint \"%s\"\n",
+ coninfo->dobj.name);
+ exit_nicely();
+ }
- appendPQExpBuffer(q, ");\n");
+ appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, " ADD CONSTRAINT %s %s (",
+ fmtId(coninfo->dobj.name),
+ coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
- /* If the index is clustered, we need to record that. */
- if (indisclustered)
- {
- appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
- fmtId(tbinfo->relname));
- appendPQExpBuffer(q, " ON %s;\n",
- fmtId(indexrelname));
- }
+ for (k = 0; k < indxinfo->indnkeys; k++)
+ {
+ int indkey = (int) indxinfo->indkeys[k];
+ const char *attname;
- /*
- * DROP must be fully qualified in case same name appears
- * in pg_catalog
- */
- appendPQExpBuffer(delq, "ALTER TABLE ONLY %s.",
- fmtId(tbinfo->relnamespace->nspname));
- appendPQExpBuffer(delq, "%s ",
- fmtId(tbinfo->relname));
- appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
- fmtId(indexrelname));
-
- ArchiveEntry(fout, indexreloid,
- indexrelname,
- tbinfo->relnamespace->nspname,
- tbinfo->usename,
- "CONSTRAINT", NULL,
- q->data, delq->data,
- NULL, NULL, NULL);
-
- for (k = 0; k < indnkeys; k++)
- free(indkeys[k]);
- free(indkeys);
-
- /* Dump Constraint Comments */
- resetPQExpBuffer(q);
- appendPQExpBuffer(q, "CONSTRAINT %s ",
- fmtId(indexrelname));
- appendPQExpBuffer(q, "ON %s",
- fmtId(tbinfo->relname));
- dumpComment(fout, q->data,
- tbinfo->relnamespace->nspname,
- tbinfo->usename,
- conoid, "pg_constraint", 0, NULL);
- }
- else
- {
- /* Plain secondary index */
- appendPQExpBuffer(q, "%s;\n", indexdef);
+ if (indkey == InvalidAttrNumber)
+ break;
+ attname = getAttrName(indkey, tbinfo);
- /* If the index is clustered, we need to record that. */
- if (indisclustered)
- {
- appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
- fmtId(tbinfo->relname));
- appendPQExpBuffer(q, " ON %s;\n",
- fmtId(indexrelname));
- }
+ appendPQExpBuffer(q, "%s%s",
+ (k == 0) ? "" : ", ",
+ fmtId(attname));
+ }
- /*
- * DROP must be fully qualified in case same name appears
- * in pg_catalog
- */
- appendPQExpBuffer(delq, "DROP INDEX %s.",
- fmtId(tbinfo->relnamespace->nspname));
- appendPQExpBuffer(delq, "%s;\n",
- fmtId(indexrelname));
-
- ArchiveEntry(fout, indexreloid,
- indexrelname,
- tbinfo->relnamespace->nspname,
- tbinfo->usename,
- "INDEX", NULL,
- q->data, delq->data,
- NULL, NULL, NULL);
- }
+ appendPQExpBuffer(q, ")");
- /* Dump Index Comments */
- resetPQExpBuffer(q);
- appendPQExpBuffer(q, "INDEX %s",
- fmtId(indexrelname));
- dumpComment(fout, q->data,
- tbinfo->relnamespace->nspname,
- tbinfo->usename,
- indexreloid, "pg_class", 0, NULL);
+ if (indxinfo->options && strlen(indxinfo->options) > 0)
+ appendPQExpBuffer(q, " WITH (%s)", indxinfo->options);
+
+ appendPQExpBuffer(q, ";\n");
+
+ /* If the index is clustered, we need to record that. */
+ if (indxinfo->indisclustered)
+ {
+ appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, " ON %s;\n",
+ fmtId(indxinfo->dobj.name));
}
- PQclear(res);
+ /*
+ * DROP must be fully qualified in case same name appears in
+ * pg_catalog
+ */
+ appendPQExpBuffer(delq, "ALTER TABLE ONLY %s.",
+ fmtId(tbinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, "%s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
+ fmtId(coninfo->dobj.name));
+
+ ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+ coninfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name,
+ indxinfo->tablespace,
+ tbinfo->rolname, false,
+ "CONSTRAINT", q->data, delq->data, NULL,
+ coninfo->dobj.dependencies, coninfo->dobj.nDeps,
+ NULL, NULL);
+ }
+ else if (coninfo->contype == 'f')
+ {
+ /*
+ * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
+ * current table data is not processed
+ */
+ appendPQExpBuffer(q, "ALTER TABLE ONLY %s\n",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
+ fmtId(coninfo->dobj.name),
+ coninfo->condef);
+
+ /*
+ * DROP must be fully qualified in case same name appears in
+ * pg_catalog
+ */
+ appendPQExpBuffer(delq, "ALTER TABLE ONLY %s.",
+ fmtId(tbinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, "%s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
+ fmtId(coninfo->dobj.name));
+
+ ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+ coninfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname, false,
+ "FK CONSTRAINT", q->data, delq->data, NULL,
+ coninfo->dobj.dependencies, coninfo->dobj.nDeps,
+ NULL, NULL);
+ }
+ else if (coninfo->contype == 'c' && tbinfo)
+ {
+ /* CHECK constraint on a table */
+
+ /* Ignore if not to be dumped separately */
+ if (coninfo->separate)
+ {
+ /* not ONLY since we want it to propagate to children */
+ appendPQExpBuffer(q, "ALTER TABLE %s\n",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
+ fmtId(coninfo->dobj.name),
+ coninfo->condef);
+
+ /*
+ * DROP must be fully qualified in case same name appears in
+ * pg_catalog
+ */
+ appendPQExpBuffer(delq, "ALTER TABLE %s.",
+ fmtId(tbinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, "%s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
+ fmtId(coninfo->dobj.name));
+
+ ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+ coninfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname, false,
+ "CHECK CONSTRAINT", q->data, delq->data, NULL,
+ coninfo->dobj.dependencies, coninfo->dobj.nDeps,
+ NULL, NULL);
+ }
}
+ else if (coninfo->contype == 'c' && tbinfo == NULL)
+ {
+ /* CHECK constraint on a domain */
+ TypeInfo *tinfo = coninfo->condomain;
+
+ /* Ignore if not to be dumped separately */
+ if (coninfo->separate)
+ {
+ appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
+ fmtId(tinfo->dobj.name));
+ appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
+ fmtId(coninfo->dobj.name),
+ coninfo->condef);
+
+ /*
+ * DROP must be fully qualified in case same name appears in
+ * pg_catalog
+ */
+ appendPQExpBuffer(delq, "ALTER DOMAIN %s.",
+ fmtId(tinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, "%s ",
+ fmtId(tinfo->dobj.name));
+ appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
+ fmtId(coninfo->dobj.name));
+
+ ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+ coninfo->dobj.name,
+ tinfo->dobj.namespace->dobj.name,
+ NULL,
+ tinfo->rolname, false,
+ "CHECK CONSTRAINT", q->data, delq->data, NULL,
+ coninfo->dobj.dependencies, coninfo->dobj.nDeps,
+ NULL, NULL);
+ }
+ }
+ else
+ {
+ write_msg(NULL, "unrecognized constraint type: %c\n", coninfo->contype);
+ exit_nicely();
+ }
+
+ /* Dump Constraint Comments --- only works for table constraints */
+ if (tbinfo && coninfo->separate)
+ dumpTableConstraintComment(fout, coninfo);
- destroyPQExpBuffer(query);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
}
/*
- * setMaxOid -
- * find the maximum oid and generate a COPY statement to set it
-*/
-
+ * dumpTableConstraintComment --- dump a constraint's comment if any
+ *
+ * This is split out because we need the function in two different places
+ * depending on whether the constraint is dumped as part of CREATE TABLE
+ * or as a separate ALTER command.
+ */
static void
-setMaxOid(Archive *fout)
+dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo)
{
- PGresult *res;
- Oid max_oid;
- char sql[1024];
+ TableInfo *tbinfo = coninfo->contable;
+ PQExpBuffer q = createPQExpBuffer();
- res = PQexec(g_conn, "CREATE TEMPORARY TABLE pgdump_oid (dummy integer)");
- if (!res ||
- PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- write_msg(NULL, "could not create pgdump_oid table: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
- PQclear(res);
- res = PQexec(g_conn, "INSERT INTO pgdump_oid VALUES (0)");
- if (!res ||
- PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- write_msg(NULL, "could not insert into pgdump_oid table: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
- max_oid = PQoidValue(res);
- if (max_oid == 0)
- {
- write_msg(NULL, "inserted invalid OID\n");
- exit_nicely();
- }
- PQclear(res);
- res = PQexec(g_conn, "DROP TABLE pgdump_oid;");
- if (!res ||
- PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- write_msg(NULL, "could not drop pgdump_oid table: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
- PQclear(res);
- if (g_verbose)
- write_msg(NULL, "maximum system OID is %u\n", max_oid);
- snprintf(sql, sizeof(sql),
- "CREATE TEMPORARY TABLE pgdump_oid (dummy integer);\n"
- "COPY pgdump_oid WITH OIDS FROM stdin;\n"
- "%u\t0\n"
- "\\.\n"
- "DROP TABLE pgdump_oid;\n",
- max_oid);
-
- ArchiveEntry(fout, "0", "Max OID", NULL, "",
- "<Init>", NULL,
- sql, "",
- NULL, NULL, NULL);
+ appendPQExpBuffer(q, "CONSTRAINT %s ",
+ fmtId(coninfo->dobj.name));
+ appendPQExpBuffer(q, "ON %s",
+ fmtId(tbinfo->dobj.name));
+ dumpComment(fout, q->data,
+ tbinfo->dobj.namespace->dobj.name,
+ tbinfo->rolname,
+ coninfo->dobj.catId, 0,
+ coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
+
+ destroyPQExpBuffer(q);
}
/*
* findLastBuiltInOid -
* find the last built in oid
- * we do this by retrieving datlastsysoid from the pg_database entry for this database,
+ *
+ * For 7.1 and 7.2, we do this by retrieving datlastsysoid from the
+ * pg_database entry for the current database
*/
-
static Oid
findLastBuiltinOid_V71(const char *dbname)
{
resetPQExpBuffer(query);
appendPQExpBuffer(query, "SELECT datlastsysoid from pg_database where datname = ");
- appendStringLiteral(query, dbname, true);
+ appendStringLiteralAH(query, dbname, g_fout);
res = PQexec(g_conn, query->data);
- if (res == NULL ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "error in finding the last system OID: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
ntups = PQntuples(res);
if (ntups < 1)
{
/*
* findLastBuiltInOid -
* find the last built in oid
- * we do this by looking up the oid of 'template1' in pg_database,
- * this is probably not foolproof but comes close
-*/
-
+ *
+ * For 7.0, we do this by assuming that the last thing that initdb does is to
+ * create the pg_indexes view. This sucks in general, but seeing that 7.0.x
+ * initdb won't be changing anymore, it'll do.
+ */
static Oid
findLastBuiltinOid_V70(void)
{
int last_oid;
res = PQexec(g_conn,
- "SELECT oid from pg_database where datname = 'template1'");
- if (res == NULL ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "error in finding the template1 database: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
+ "SELECT oid FROM pg_class WHERE relname = 'pg_indexes'");
+ check_sql_result(res, g_conn,
+ "SELECT oid FROM pg_class WHERE relname = 'pg_indexes'",
+ PGRES_TUPLES_OK);
ntups = PQntuples(res);
if (ntups < 1)
{
- write_msg(NULL, "could not find entry for database template1 in table pg_database\n");
+ write_msg(NULL, "could not find entry for pg_indexes in pg_class\n");
exit_nicely();
}
if (ntups > 1)
{
- write_msg(NULL, "found more than one entry for database template1 in table pg_database\n");
+ write_msg(NULL, "found more than one entry for pg_indexes in pg_class\n");
exit_nicely();
}
last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
}
static void
-dumpOneSequence(Archive *fout, TableInfo *tbinfo,
- const bool schemaOnly, const bool dataOnly)
+dumpSequence(Archive *fout, TableInfo *tbinfo)
{
PGresult *res;
char *last,
PQExpBuffer delqry = createPQExpBuffer();
/* Make sure we are in proper schema */
- selectSourceSchema(tbinfo->relnamespace->nspname);
+ selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE);
snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE);
appendPQExpBuffer(query,
"SELECT sequence_name, last_value, increment_by, "
- "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
- " WHEN increment_by < 0 AND max_value = -1 THEN NULL "
+ "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
+ " WHEN increment_by < 0 AND max_value = -1 THEN NULL "
" ELSE max_value "
"END AS max_value, "
- "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
- " WHEN increment_by < 0 AND min_value = %s THEN NULL "
+ "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
+ " WHEN increment_by < 0 AND min_value = %s THEN NULL "
" ELSE min_value "
"END AS min_value, "
"cache_value, is_cycled, is_called from %s",
bufx, bufm,
- fmtId(tbinfo->relname));
+ fmtId(tbinfo->dobj.name));
res = PQexec(g_conn, query->data);
- if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to get data of sequence \"%s\" failed: %s", tbinfo->relname, PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
if (PQntuples(res) != 1)
{
write_msg(NULL, "query to get data of sequence \"%s\" returned %d rows (expected 1)\n",
- tbinfo->relname, PQntuples(res));
+ tbinfo->dobj.name, PQntuples(res));
exit_nicely();
}
/* Disable this check: it fails if sequence has been renamed */
#ifdef NOT_USED
- if (strcmp(PQgetvalue(res, 0, 0), tbinfo->relname) != 0)
+ if (strcmp(PQgetvalue(res, 0, 0), tbinfo->dobj.name) != 0)
{
write_msg(NULL, "query to get data of sequence \"%s\" returned name \"%s\"\n",
- tbinfo->relname, PQgetvalue(res, 0, 0));
+ tbinfo->dobj.name, PQgetvalue(res, 0, 0));
exit_nicely();
}
#endif
/*
* The logic we use for restoring sequences is as follows:
*
- * Add a basic CREATE SEQUENCE statement (use last_val for start if
- * called is false, else use min_val for start_val). Skip this if the
- * sequence came from a SERIAL column.
+ * Add a CREATE SEQUENCE statement as part of a "schema" dump
+ * (use last_val for start if called is false, else use min_val for
+ * start_val). Also, if the sequence is owned by a column, add an
+ * ALTER SEQUENCE SET OWNED command for it.
*
- * Add a 'SETVAL(seq, last_val, iscalled)' at restore-time iff we load
- * data. We do this for serial sequences too.
+ * Add a 'SETVAL(seq, last_val, iscalled)' as part of a "data" dump.
*/
-
- if (!dataOnly && tbinfo->owning_tab == NULL)
+ if (!dataOnly)
{
resetPQExpBuffer(delqry);
* pg_catalog
*/
appendPQExpBuffer(delqry, "DROP SEQUENCE %s.",
- fmtId(tbinfo->relnamespace->nspname));
+ fmtId(tbinfo->dobj.namespace->dobj.name));
appendPQExpBuffer(delqry, "%s;\n",
- fmtId(tbinfo->relname));
+ fmtId(tbinfo->dobj.name));
resetPQExpBuffer(query);
appendPQExpBuffer(query,
"CREATE SEQUENCE %s\n",
- fmtId(tbinfo->relname));
+ fmtId(tbinfo->dobj.name));
if (!called)
appendPQExpBuffer(query, " START WITH %s\n", last);
appendPQExpBuffer(query, " NO MINVALUE\n");
appendPQExpBuffer(query,
- " CACHE %s%s;\n",
+ " CACHE %s%s",
cache, (cycled ? "\n CYCLE" : ""));
- ArchiveEntry(fout, tbinfo->oid, tbinfo->relname,
- tbinfo->relnamespace->nspname, tbinfo->usename,
- "SEQUENCE", NULL,
- query->data, delqry->data,
- NULL, NULL, NULL);
+ appendPQExpBuffer(query, ";\n");
+
+ ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
+ tbinfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname,
+ false, "SEQUENCE", query->data, delqry->data, NULL,
+ tbinfo->dobj.dependencies, tbinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /*
+ * If the sequence is owned by a table column, emit the ALTER for it
+ * as a separate TOC entry immediately following the sequence's own
+ * entry. It's OK to do this rather than using full sorting logic,
+ * because the dependency that tells us it's owned will have forced
+ * the table to be created first. We can't just include the ALTER
+ * in the TOC entry because it will fail if we haven't reassigned
+ * the sequence owner to match the table's owner.
+ *
+ * We need not schema-qualify the table reference because both
+ * sequence and table must be in the same schema.
+ */
+ if (OidIsValid(tbinfo->owning_tab))
+ {
+ TableInfo *owning_tab = findTableByOid(tbinfo->owning_tab);
+
+ if (owning_tab)
+ {
+ resetPQExpBuffer(query);
+ appendPQExpBuffer(query, "ALTER SEQUENCE %s",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(query, " OWNED BY %s",
+ fmtId(owning_tab->dobj.name));
+ appendPQExpBuffer(query, ".%s;\n",
+ fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
+
+ ArchiveEntry(fout, nilCatalogId, createDumpId(),
+ tbinfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname,
+ false, "SEQUENCE OWNED BY", query->data, "", NULL,
+ &(tbinfo->dobj.dumpId), 1,
+ NULL, NULL);
+ }
+ }
+
+ /* Dump Sequence Comments */
+ resetPQExpBuffer(query);
+ appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
+ dumpComment(fout, query->data,
+ tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+ tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
}
if (!schemaOnly)
{
resetPQExpBuffer(query);
appendPQExpBuffer(query, "SELECT pg_catalog.setval(");
- appendStringLiteral(query, fmtId(tbinfo->relname), true);
+ appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
appendPQExpBuffer(query, ", %s, %s);\n",
last, (called ? "true" : "false"));
- ArchiveEntry(fout, tbinfo->oid, tbinfo->relname,
- tbinfo->relnamespace->nspname, tbinfo->usename,
- "SEQUENCE SET", NULL,
- query->data, "" /* Del */ ,
- NULL, NULL, NULL);
- }
-
- if (!dataOnly)
- {
- /* Dump Sequence Comments */
-
- resetPQExpBuffer(query);
- appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->relname));
- dumpComment(fout, query->data,
- tbinfo->relnamespace->nspname, tbinfo->usename,
- tbinfo->oid, "pg_class", 0, NULL);
+ ArchiveEntry(fout, nilCatalogId, createDumpId(),
+ tbinfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname,
+ false, "SEQUENCE SET", query->data, "", NULL,
+ &(tbinfo->dobj.dumpId), 1,
+ NULL, NULL);
}
PQclear(res);
destroyPQExpBuffer(delqry);
}
-/*
- * dumpConstraints
- *
- * Dump out constraints after all table creation statements in
- * an alter table format. Currently handles foreign keys only.
- * (Unique and primary key constraints are handled with indexes,
- * while check constraints are merged into the table definition.)
- *
- * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that
- * the current table data is not processed
- */
static void
-dumpConstraints(Archive *fout, TableInfo *tblinfo, int numTables)
+dumpTrigger(Archive *fout, TriggerInfo *tginfo)
{
- int i,
- j;
+ TableInfo *tbinfo = tginfo->tgtable;
PQExpBuffer query;
PQExpBuffer delqry;
- PGresult *res;
- int i_condef,
- i_conoid,
- i_conname;
- int ntups;
+ const char *p;
+ int findx;
- /* pg_constraint was created in 7.3, so nothing to do if older */
- if (g_fout->remoteVersion < 70300)
+ if (dataOnly)
return;
query = createPQExpBuffer();
delqry = createPQExpBuffer();
- for (i = 0; i < numTables; i++)
- {
- TableInfo *tbinfo = &tblinfo[i];
-
- if (tbinfo->ntrig == 0 || !tbinfo->dump)
- continue;
-
- if (g_verbose)
- write_msg(NULL, "dumping foreign key constraints for table \"%s\"\n",
- tbinfo->relname);
-
- /*
- * select table schema to ensure regproc name is qualified if
- * needed
- */
- selectSourceSchema(tbinfo->relnamespace->nspname);
-
- resetPQExpBuffer(query);
- appendPQExpBuffer(query,
- "SELECT oid, conname, "
- "pg_catalog.pg_get_constraintdef(oid) as condef "
- "FROM pg_catalog.pg_constraint "
- "WHERE conrelid = '%s'::pg_catalog.oid "
- "AND contype = 'f'",
- tbinfo->oid);
- res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain list of foreign key definitions failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
- }
- ntups = PQntuples(res);
-
- i_conoid = PQfnumber(res, "oid");
- i_conname = PQfnumber(res, "conname");
- i_condef = PQfnumber(res, "condef");
-
- for (j = 0; j < ntups; j++)
- {
- const char *conOid = PQgetvalue(res, j, i_conoid);
- const char *conName = PQgetvalue(res, j, i_conname);
- const char *conDef = PQgetvalue(res, j, i_condef);
-
- resetPQExpBuffer(query);
- appendPQExpBuffer(query, "ALTER TABLE ONLY %s\n",
- fmtId(tbinfo->relname));
- appendPQExpBuffer(query, " ADD CONSTRAINT %s %s;\n",
- fmtId(conName),
- conDef);
-
- /*
- * DROP must be fully qualified in case same name appears in
- * pg_catalog
- */
- resetPQExpBuffer(delqry);
- appendPQExpBuffer(delqry, "ALTER TABLE ONLY %s.",
- fmtId(tbinfo->relnamespace->nspname));
- appendPQExpBuffer(delqry, "%s ",
- fmtId(tbinfo->relname));
- appendPQExpBuffer(delqry, "DROP CONSTRAINT %s;\n",
- fmtId(conName));
-
- ArchiveEntry(fout, conOid,
- conName,
- tbinfo->relnamespace->nspname,
- tbinfo->usename,
- "FK CONSTRAINT", NULL,
- query->data, delqry->data,
- NULL, NULL, NULL);
-
- resetPQExpBuffer(query);
- appendPQExpBuffer(query, "CONSTRAINT %s ",
- fmtId(conName));
- appendPQExpBuffer(query, "ON %s",
- fmtId(tbinfo->relname));
-
- dumpComment(fout, query->data,
- tbinfo->relnamespace->nspname, tbinfo->usename,
- conOid, "pg_constraint", 0, NULL);
- }
+ /*
+ * DROP must be fully qualified in case same name appears in pg_catalog
+ */
+ appendPQExpBuffer(delqry, "DROP TRIGGER %s ",
+ fmtId(tginfo->dobj.name));
+ appendPQExpBuffer(delqry, "ON %s.",
+ fmtId(tbinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delqry, "%s;\n",
+ fmtId(tbinfo->dobj.name));
- PQclear(res);
+ if (tginfo->tgisconstraint)
+ {
+ appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER ");
+ appendPQExpBufferStr(query, fmtId(tginfo->tgconstrname));
}
+ else
+ {
+ appendPQExpBuffer(query, "CREATE TRIGGER ");
+ appendPQExpBufferStr(query, fmtId(tginfo->dobj.name));
+ }
+ appendPQExpBuffer(query, "\n ");
- destroyPQExpBuffer(query);
- destroyPQExpBuffer(delqry);
-}
-
-static void
-dumpTriggers(Archive *fout, TableInfo *tblinfo, int numTables)
-{
- int i,
- j;
- PQExpBuffer query = createPQExpBuffer();
- PQExpBuffer delqry = createPQExpBuffer();
- PGresult *res;
- int i_tgoid,
- i_tgname,
- i_tgfname,
- i_tgtype,
- i_tgnargs,
- i_tgargs,
- i_tgisconstraint,
- i_tgconstrname,
- i_tgdeferrable,
- i_tgconstrrelid,
- i_tgconstrrelname,
- i_tginitdeferred;
- int ntups;
+ /* Trigger type */
+ findx = 0;
+ if (TRIGGER_FOR_BEFORE(tginfo->tgtype))
+ appendPQExpBuffer(query, "BEFORE");
+ else
+ appendPQExpBuffer(query, "AFTER");
+ if (TRIGGER_FOR_INSERT(tginfo->tgtype))
+ {
+ appendPQExpBuffer(query, " INSERT");
+ findx++;
+ }
+ if (TRIGGER_FOR_DELETE(tginfo->tgtype))
+ {
+ if (findx > 0)
+ appendPQExpBuffer(query, " OR DELETE");
+ else
+ appendPQExpBuffer(query, " DELETE");
+ findx++;
+ }
+ if (TRIGGER_FOR_UPDATE(tginfo->tgtype))
+ {
+ if (findx > 0)
+ appendPQExpBuffer(query, " OR UPDATE");
+ else
+ appendPQExpBuffer(query, " UPDATE");
+ }
+ appendPQExpBuffer(query, " ON %s\n",
+ fmtId(tbinfo->dobj.name));
- for (i = 0; i < numTables; i++)
+ if (tginfo->tgisconstraint)
{
- TableInfo *tbinfo = &tblinfo[i];
+ if (OidIsValid(tginfo->tgconstrrelid))
+ {
+ /* If we are using regclass, name is already quoted */
+ if (g_fout->remoteVersion >= 70300)
+ appendPQExpBuffer(query, " FROM %s\n ",
+ tginfo->tgconstrrelname);
+ else
+ appendPQExpBuffer(query, " FROM %s\n ",
+ fmtId(tginfo->tgconstrrelname));
+ }
+ if (!tginfo->tgdeferrable)
+ appendPQExpBuffer(query, "NOT ");
+ appendPQExpBuffer(query, "DEFERRABLE INITIALLY ");
+ if (tginfo->tginitdeferred)
+ appendPQExpBuffer(query, "DEFERRED\n");
+ else
+ appendPQExpBuffer(query, "IMMEDIATE\n");
+ }
- if (tbinfo->ntrig == 0 || !tbinfo->dump)
- continue;
+ if (TRIGGER_FOR_ROW(tginfo->tgtype))
+ appendPQExpBuffer(query, " FOR EACH ROW\n ");
+ else
+ appendPQExpBuffer(query, " FOR EACH STATEMENT\n ");
- if (g_verbose)
- write_msg(NULL, "dumping triggers for table \"%s\"\n",
- tbinfo->relname);
+ /* In 7.3, result of regproc is already quoted */
+ if (g_fout->remoteVersion >= 70300)
+ appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
+ tginfo->tgfname);
+ else
+ appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
+ fmtId(tginfo->tgfname));
- /*
- * select table schema to ensure regproc name is qualified if
- * needed
- */
- selectSourceSchema(tbinfo->relnamespace->nspname);
+ p = tginfo->tgargs;
+ for (findx = 0; findx < tginfo->tgnargs; findx++)
+ {
+ const char *s = p;
- resetPQExpBuffer(query);
- if (g_fout->remoteVersion >= 70300)
- {
- /*
- * We ignore triggers that are tied to a foreign-key
- * constraint
- */
- appendPQExpBuffer(query,
- "SELECT tgname, "
- "tgfoid::pg_catalog.regproc as tgfname, "
- "tgtype, tgnargs, tgargs, "
- "tgisconstraint, tgconstrname, tgdeferrable, "
- "tgconstrrelid, tginitdeferred, oid, "
- "tgconstrrelid::pg_catalog.regclass as tgconstrrelname "
- "from pg_catalog.pg_trigger t "
- "where tgrelid = '%s'::pg_catalog.oid "
- "and (not tgisconstraint "
- " OR NOT EXISTS"
- " (SELECT 1 FROM pg_catalog.pg_depend d "
- " JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
- " WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))",
- tbinfo->oid);
- }
- else
- {
- appendPQExpBuffer(query,
- "SELECT tgname, tgfoid::regproc as tgfname, "
- "tgtype, tgnargs, tgargs, "
- "tgisconstraint, tgconstrname, tgdeferrable, "
- "tgconstrrelid, tginitdeferred, oid, "
- "(select relname from pg_class where oid = tgconstrrelid) "
- " as tgconstrrelname "
- "from pg_trigger "
- "where tgrelid = '%s'::oid",
- tbinfo->oid);
- }
- res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
+ /* Set 'p' to end of arg string. marked by '\000' */
+ for (;;)
{
- write_msg(NULL, "query to obtain list of triggers failed: %s", PQerrorMessage(g_conn));
- exit_nicely();
+ p = strchr(p, '\\');
+ if (p == NULL)
+ {
+ write_msg(NULL, "invalid argument string (%s) for trigger \"%s\" on table \"%s\"\n",
+ tginfo->tgargs,
+ tginfo->dobj.name,
+ tbinfo->dobj.name);
+ exit_nicely();
+ }
+ p++;
+ if (*p == '\\') /* is it '\\'? */
+ {
+ p++;
+ continue;
+ }
+ if (p[0] == '0' && p[1] == '0' && p[2] == '0') /* is it '\000'? */
+ break;
}
- ntups = PQntuples(res);
+ p--;
- /*
- * We may have less triggers than recorded due to constraint
- * triggers which are dumped by dumpConstraints
- */
- if (ntups > tbinfo->ntrig)
+ appendPQExpBufferChar(query, '\'');
+ while (s < p)
{
- write_msg(NULL, "expected %d triggers on table \"%s\" but found %d\n",
- tbinfo->ntrig, tbinfo->relname, ntups);
- exit_nicely();
+ if (*s == '\'')
+ appendPQExpBufferChar(query, '\'');
+ /*
+ * bytea unconditionally doubles backslashes, so we suppress
+ * the doubling for standard_conforming_strings.
+ */
+ if (fout->std_strings && *s == '\\' && s[1] == '\\')
+ s++;
+ appendPQExpBufferChar(query, *s++);
}
- i_tgname = PQfnumber(res, "tgname");
- i_tgfname = PQfnumber(res, "tgfname");
- i_tgtype = PQfnumber(res, "tgtype");
- i_tgnargs = PQfnumber(res, "tgnargs");
- i_tgargs = PQfnumber(res, "tgargs");
- i_tgoid = PQfnumber(res, "oid");
- i_tgisconstraint = PQfnumber(res, "tgisconstraint");
- i_tgconstrname = PQfnumber(res, "tgconstrname");
- i_tgdeferrable = PQfnumber(res, "tgdeferrable");
- i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
- i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
- i_tginitdeferred = PQfnumber(res, "tginitdeferred");
-
- for (j = 0; j < ntups; j++)
- {
- const char *tgoid = PQgetvalue(res, j, i_tgoid);
- char *tgname = PQgetvalue(res, j, i_tgname);
- const char *tgfname = PQgetvalue(res, j, i_tgfname);
- int2 tgtype = atoi(PQgetvalue(res, j, i_tgtype));
- int tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
- const char *tgargs = PQgetvalue(res, j, i_tgargs);
- int tgisconstraint;
- int tgdeferrable;
- int tginitdeferred;
- char *tgconstrrelid;
- const char *p;
- int findx;
-
- if (strcmp(PQgetvalue(res, j, i_tgisconstraint), "f") == 0)
- tgisconstraint = 0;
- else
- tgisconstraint = 1;
+ appendPQExpBufferChar(query, '\'');
+ appendPQExpBuffer(query,
+ (findx < tginfo->tgnargs - 1) ? ", " : "");
+ p = p + 4;
+ }
+ appendPQExpBuffer(query, ");\n");
- if (strcmp(PQgetvalue(res, j, i_tgdeferrable), "f") == 0)
- tgdeferrable = 0;
- else
- tgdeferrable = 1;
+ if (!tginfo->tgenabled)
+ {
+ appendPQExpBuffer(query, "\nALTER TABLE %s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(query, "DISABLE TRIGGER %s;\n",
+ fmtId(tginfo->dobj.name));
+ }
- if (strcmp(PQgetvalue(res, j, i_tginitdeferred), "f") == 0)
- tginitdeferred = 0;
- else
- tginitdeferred = 1;
+ ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
+ tginfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname, false,
+ "TRIGGER", query->data, delqry->data, NULL,
+ tginfo->dobj.dependencies, tginfo->dobj.nDeps,
+ NULL, NULL);
- resetPQExpBuffer(delqry);
+ resetPQExpBuffer(query);
+ appendPQExpBuffer(query, "TRIGGER %s ",
+ fmtId(tginfo->dobj.name));
+ appendPQExpBuffer(query, "ON %s",
+ fmtId(tbinfo->dobj.name));
- /*
- * DROP must be fully qualified in case same name appears in
- * pg_catalog
- */
- appendPQExpBuffer(delqry, "DROP TRIGGER %s ",
- fmtId(tgname));
- appendPQExpBuffer(delqry, "ON %s.",
- fmtId(tbinfo->relnamespace->nspname));
- appendPQExpBuffer(delqry, "%s;\n",
- fmtId(tbinfo->relname));
+ dumpComment(fout, query->data,
+ tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+ tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
- resetPQExpBuffer(query);
- if (tgisconstraint)
- {
- appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER ");
- appendPQExpBuffer(query, fmtId(PQgetvalue(res, j, i_tgconstrname)));
- }
- else
- {
- appendPQExpBuffer(query, "CREATE TRIGGER ");
- appendPQExpBuffer(query, fmtId(tgname));
- }
- appendPQExpBuffer(query, "\n ");
- /* Trigger type */
- findx = 0;
- if (TRIGGER_FOR_BEFORE(tgtype))
- appendPQExpBuffer(query, "BEFORE");
- else
- appendPQExpBuffer(query, "AFTER");
- if (TRIGGER_FOR_INSERT(tgtype))
- {
- appendPQExpBuffer(query, " INSERT");
- findx++;
- }
- if (TRIGGER_FOR_DELETE(tgtype))
- {
- if (findx > 0)
- appendPQExpBuffer(query, " OR DELETE");
- else
- appendPQExpBuffer(query, " DELETE");
- findx++;
- }
- if (TRIGGER_FOR_UPDATE(tgtype))
- {
- if (findx > 0)
- appendPQExpBuffer(query, " OR UPDATE");
- else
- appendPQExpBuffer(query, " UPDATE");
- }
- appendPQExpBuffer(query, " ON %s\n",
- fmtId(tbinfo->relname));
+ destroyPQExpBuffer(query);
+ destroyPQExpBuffer(delqry);
+}
- if (tgisconstraint)
- {
- tgconstrrelid = PQgetvalue(res, j, i_tgconstrrelid);
+/*
+ * dumpRule
+ * Dump a rule
+ */
+static void
+dumpRule(Archive *fout, RuleInfo *rinfo)
+{
+ TableInfo *tbinfo = rinfo->ruletable;
+ PQExpBuffer query;
+ PQExpBuffer cmd;
+ PQExpBuffer delcmd;
+ PGresult *res;
- if (strcmp(tgconstrrelid, "0") != 0)
- {
+ /* Skip if not to be dumped */
+ if (!rinfo->dobj.dump || dataOnly)
+ return;
- if (PQgetisnull(res, j, i_tgconstrrelname))
- {
- write_msg(NULL, "query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %s)\n",
- tgname, tbinfo->relname, tgconstrrelid);
- exit_nicely();
- }
+ /*
+ * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
+ * we do not want to dump it as a separate object.
+ */
+ if (!rinfo->separate)
+ return;
- /* If we are using regclass, name is already quoted */
- if (g_fout->remoteVersion >= 70300)
- appendPQExpBuffer(query, " FROM %s\n ",
- PQgetvalue(res, j, i_tgconstrrelname));
- else
- appendPQExpBuffer(query, " FROM %s\n ",
- fmtId(PQgetvalue(res, j, i_tgconstrrelname)));
- }
- if (!tgdeferrable)
- appendPQExpBuffer(query, "NOT ");
- appendPQExpBuffer(query, "DEFERRABLE INITIALLY ");
- if (tginitdeferred)
- appendPQExpBuffer(query, "DEFERRED\n");
- else
- appendPQExpBuffer(query, "IMMEDIATE\n");
+ /*
+ * Make sure we are in proper schema.
+ */
+ selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
- }
+ query = createPQExpBuffer();
+ cmd = createPQExpBuffer();
+ delcmd = createPQExpBuffer();
- if (TRIGGER_FOR_ROW(tgtype))
- appendPQExpBuffer(query, " FOR EACH ROW\n ");
- else
- appendPQExpBuffer(query, " FOR EACH STATEMENT\n ");
+ if (g_fout->remoteVersion >= 70300)
+ {
+ appendPQExpBuffer(query,
+ "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid) AS definition",
+ rinfo->dobj.catId.oid);
+ }
+ else
+ {
+ /* Rule name was unique before 7.3 ... */
+ appendPQExpBuffer(query,
+ "SELECT pg_get_ruledef('%s') AS definition",
+ rinfo->dobj.name);
+ }
- /* In 7.3, result of regproc is already quoted */
- if (g_fout->remoteVersion >= 70300)
- appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
- tgfname);
- else
- appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
- fmtId(tgfname));
- for (findx = 0; findx < tgnargs; findx++)
- {
- const char *s;
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
- for (p = tgargs;;)
- {
- p = strchr(p, '\\');
- if (p == NULL)
- {
- write_msg(NULL, "invalid argument string (%s) for trigger \"%s\" on table \"%s\"\n",
- PQgetvalue(res, j, i_tgargs),
- tgname,
- tbinfo->relname);
- exit_nicely();
- }
- p++;
- if (*p == '\\')
- {
- p++;
- continue;
- }
- if (p[0] == '0' && p[1] == '0' && p[2] == '0')
- break;
- }
- p--;
- appendPQExpBufferChar(query, '\'');
- for (s = tgargs; s < p;)
- {
- if (*s == '\'')
- appendPQExpBufferChar(query, '\\');
- appendPQExpBufferChar(query, *s++);
- }
- appendPQExpBufferChar(query, '\'');
- appendPQExpBuffer(query, (findx < tgnargs - 1) ? ", " : "");
- tgargs = p + 4;
- }
- appendPQExpBuffer(query, ");\n");
+ if (PQntuples(res) != 1)
+ {
+ write_msg(NULL, "query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
+ rinfo->dobj.name, tbinfo->dobj.name);
+ exit_nicely();
+ }
- ArchiveEntry(fout, tgoid,
- tgname,
- tbinfo->relnamespace->nspname,
- tbinfo->usename,
- "TRIGGER", NULL,
- query->data, delqry->data,
- NULL, NULL, NULL);
+ printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
- resetPQExpBuffer(query);
- appendPQExpBuffer(query, "TRIGGER %s ",
- fmtId(tgname));
- appendPQExpBuffer(query, "ON %s",
- fmtId(tbinfo->relname));
-
- dumpComment(fout, query->data,
- tbinfo->relnamespace->nspname, tbinfo->usename,
- tgoid, "pg_trigger", 0, NULL);
- }
+ /*
+ * DROP must be fully qualified in case same name appears in pg_catalog
+ */
+ appendPQExpBuffer(delcmd, "DROP RULE %s ",
+ fmtId(rinfo->dobj.name));
+ appendPQExpBuffer(delcmd, "ON %s.",
+ fmtId(tbinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delcmd, "%s;\n",
+ fmtId(tbinfo->dobj.name));
+
+ ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
+ rinfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname, false,
+ "RULE", cmd->data, delcmd->data, NULL,
+ rinfo->dobj.dependencies, rinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump rule comments */
+ resetPQExpBuffer(query);
+ appendPQExpBuffer(query, "RULE %s",
+ fmtId(rinfo->dobj.name));
+ appendPQExpBuffer(query, " ON %s",
+ fmtId(tbinfo->dobj.name));
+ dumpComment(fout, query->data,
+ tbinfo->dobj.namespace->dobj.name,
+ tbinfo->rolname,
+ rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
- PQclear(res);
- }
+ PQclear(res);
destroyPQExpBuffer(query);
- destroyPQExpBuffer(delqry);
+ destroyPQExpBuffer(cmd);
+ destroyPQExpBuffer(delcmd);
}
-
+/*
+ * getDependencies --- obtain available dependency data
+ */
static void
-dumpRules(Archive *fout, TableInfo *tblinfo, int numTables)
+getDependencies(void)
{
+ PQExpBuffer query;
PGresult *res;
- int nrules;
- int i,
- t;
- PQExpBuffer query = createPQExpBuffer();
- PQExpBuffer cmd = createPQExpBuffer();
- int i_definition;
- int i_oid;
- int i_rulename;
+ int ntups,
+ i;
+ int i_classid,
+ i_objid,
+ i_refclassid,
+ i_refobjid,
+ i_deptype;
+ DumpableObject *dobj,
+ *refdobj;
+
+ /* No dependency info available before 7.3 */
+ if (g_fout->remoteVersion < 70300)
+ return;
if (g_verbose)
- write_msg(NULL, "dumping out rules\n");
+ write_msg(NULL, "reading dependency data\n");
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ query = createPQExpBuffer();
+
+ appendPQExpBuffer(query, "SELECT "
+ "classid, objid, refclassid, refobjid, deptype "
+ "FROM pg_depend "
+ "WHERE deptype != 'p' "
+ "ORDER BY 1,2");
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_classid = PQfnumber(res, "classid");
+ i_objid = PQfnumber(res, "objid");
+ i_refclassid = PQfnumber(res, "refclassid");
+ i_refobjid = PQfnumber(res, "refobjid");
+ i_deptype = PQfnumber(res, "deptype");
/*
- * For each table we dump
+ * Since we ordered the SELECT by referencing ID, we can expect that
+ * multiple entries for the same object will appear together; this saves
+ * on searches.
*/
- for (t = 0; t < numTables; t++)
+ dobj = NULL;
+
+ for (i = 0; i < ntups; i++)
{
- TableInfo *tbinfo = &tblinfo[t];
+ CatalogId objId;
+ CatalogId refobjId;
+ char deptype;
- if (!tbinfo->hasrules || !tbinfo->dump)
- continue;
+ objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
+ objId.oid = atooid(PQgetvalue(res, i, i_objid));
+ refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
+ refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
+ deptype = *(PQgetvalue(res, i, i_deptype));
- /* Make sure we are in proper schema */
- selectSourceSchema(tbinfo->relnamespace->nspname);
+ if (dobj == NULL ||
+ dobj->catId.tableoid != objId.tableoid ||
+ dobj->catId.oid != objId.oid)
+ dobj = findObjectByCatalogId(objId);
/*
- * Get all rules defined for this table, except view select rules
+ * Failure to find objects mentioned in pg_depend is not unexpected,
+ * since for example we don't collect info about TOAST tables.
*/
- resetPQExpBuffer(query);
-
- if (g_fout->remoteVersion >= 70300)
- {
- appendPQExpBuffer(query,
- "SELECT pg_catalog.pg_get_ruledef(oid) AS definition,"
- " oid, rulename "
- "FROM pg_catalog.pg_rewrite "
- "WHERE ev_class = '%s'::pg_catalog.oid "
- "AND rulename != '_RETURN' "
- "ORDER BY oid",
- tbinfo->oid);
- }
- else
+ if (dobj == NULL)
{
- /*
- * We include pg_rules in the cross since it filters out all
- * view rules (pjw 15-Sep-2000).
- */
- appendPQExpBuffer(query, "SELECT definition,"
- " pg_rewrite.oid, pg_rewrite.rulename "
- "FROM pg_rewrite, pg_class, pg_rules "
- "WHERE pg_class.relname = ");
- appendStringLiteral(query, tbinfo->relname, true);
- appendPQExpBuffer(query,
- " AND pg_rewrite.ev_class = pg_class.oid "
- " AND pg_rules.tablename = pg_class.relname "
- " AND pg_rules.rulename = pg_rewrite.rulename "
- "ORDER BY pg_rewrite.oid");
+#ifdef NOT_USED
+ fprintf(stderr, "no referencing object %u %u\n",
+ objId.tableoid, objId.oid);
+#endif
+ continue;
}
- res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
+ refdobj = findObjectByCatalogId(refobjId);
+
+ if (refdobj == NULL)
{
- write_msg(NULL, "query to get rules associated with table \"%s\" failed: %s",
- tbinfo->relname, PQerrorMessage(g_conn));
- exit_nicely();
+#ifdef NOT_USED
+ fprintf(stderr, "no referenced object %u %u\n",
+ refobjId.tableoid, refobjId.oid);
+#endif
+ continue;
}
- nrules = PQntuples(res);
- i_definition = PQfnumber(res, "definition");
- i_oid = PQfnumber(res, "oid");
- i_rulename = PQfnumber(res, "rulename");
-
/*
- * Dump them out
+ * Ordinarily, table rowtypes have implicit dependencies on their
+ * tables. However, for a composite type the implicit dependency goes
+ * the other way in pg_depend; which is the right thing for DROP but
+ * it doesn't produce the dependency ordering we need. So in that one
+ * case, we reverse the direction of the dependency.
*/
-
- for (i = 0; i < nrules; i++)
- {
- printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, i, i_definition));
- ArchiveEntry(fout, PQgetvalue(res, i, i_oid),
- PQgetvalue(res, i, i_rulename),
- tbinfo->relnamespace->nspname,
- tbinfo->usename,
- "RULE", NULL,
- cmd->data,
- "", /* Del */
- NULL, NULL, NULL);
-
- /* Dump rule comments */
-
- resetPQExpBuffer(query);
- appendPQExpBuffer(query, "RULE %s", fmtId(PQgetvalue(res, i, i_rulename)));
- appendPQExpBuffer(query, " ON %s", fmtId(tbinfo->relname));
- dumpComment(fout, query->data,
- tbinfo->relnamespace->nspname,
- tbinfo->usename,
- PQgetvalue(res, i, i_oid), "pg_rewrite", 0, NULL);
-
- }
-
- PQclear(res);
+ if (deptype == 'i' &&
+ dobj->objType == DO_TABLE &&
+ refdobj->objType == DO_TYPE)
+ addObjectDependency(refdobj, dobj->dumpId);
+ else
+ /* normal case */
+ addObjectDependency(dobj, refdobj->dumpId);
}
+ PQclear(res);
+
destroyPQExpBuffer(query);
- destroyPQExpBuffer(cmd);
}
+
/*
* selectSourceSchema - make the specified schema the active search path
* in the source database.
{
static char *curSchemaName = NULL;
PQExpBuffer query;
- PGresult *res;
/* Not relevant if fetching from pre-7.3 DB */
if (g_fout->remoteVersion < 70300)
fmtId(schemaName));
if (strcmp(schemaName, "pg_catalog") != 0)
appendPQExpBuffer(query, ", pg_catalog");
- res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- write_msg(NULL, "command to set search_path failed: %s",
- PQerrorMessage(g_conn));
- exit_nicely();
- }
- PQclear(res);
- destroyPQExpBuffer(query);
+ do_sql_command(g_conn, query->data);
+
+ destroyPQExpBuffer(query);
if (curSchemaName)
free(curSchemaName);
curSchemaName = strdup(schemaName);
* schema; this is why we don't try to cache the names.
*/
static char *
-getFormattedTypeName(const char *oid, OidOptions opts)
+getFormattedTypeName(Oid oid, OidOptions opts)
{
char *result;
PQExpBuffer query;
PGresult *res;
int ntups;
- if (atooid(oid) == 0)
+ if (oid == 0)
{
if ((opts & zeroAsOpaque) != 0)
return strdup(g_opaque_type);
query = createPQExpBuffer();
if (g_fout->remoteVersion >= 70300)
{
- appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%s'::pg_catalog.oid, NULL)",
+ appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
oid);
}
else if (g_fout->remoteVersion >= 70100)
{
- appendPQExpBuffer(query, "SELECT format_type('%s'::oid, NULL)",
+ appendPQExpBuffer(query, "SELECT format_type('%u'::oid, NULL)",
oid);
}
else
{
appendPQExpBuffer(query, "SELECT typname "
"FROM pg_type "
- "WHERE oid = '%s'::oid",
+ "WHERE oid = '%u'::oid",
oid);
}
res = PQexec(g_conn, query->data);
- if (!res ||
- PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- write_msg(NULL, "query to obtain name of data type %s failed: %s",
- oid, PQerrorMessage(g_conn));
- exit_nicely();
- }
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
/* Expecting a single result only */
ntups = PQntuples(res);
myFormatType(const char *typname, int32 typmod)
{
char *result;
+ bool isarray = false;
PQExpBuffer buf = createPQExpBuffer();
+ /* Handle array types */
+ if (typname[0] == '_')
+ {
+ isarray = true;
+ typname++;
+ }
+
/* Show lengths on bpchar and varchar */
if (!strcmp(typname, "bpchar"))
{
}
/*
- * char is an internal single-byte data type; Let's make sure we force
- * it through with quotes. - thomas 1998-12-13
+ * char is an internal single-byte data type; Let's make sure we force it
+ * through with quotes. - thomas 1998-12-13
*/
else if (strcmp(typname, "char") == 0)
appendPQExpBuffer(buf, "\"char\"");
else
appendPQExpBuffer(buf, "%s", fmtId(typname));
+ /* Append array qualifier for array types */
+ if (isarray)
+ appendPQExpBuffer(buf, "[]");
+
result = strdup(buf->data);
destroyPQExpBuffer(buf);
appendPQExpBuffer(q, ")");
return q->data;
}
+
+/*
+ * Convenience subroutine to execute a SQL command and check for
+ * COMMAND_OK status.
+ */
+static void
+do_sql_command(PGconn *conn, const char *query)
+{
+ PGresult *res;
+
+ res = PQexec(conn, query);
+ check_sql_result(res, conn, query, PGRES_COMMAND_OK);
+ PQclear(res);
+}
+
+/*
+ * Convenience subroutine to verify a SQL command succeeded,
+ * and exit with a useful error message if not.
+ */
+static void
+check_sql_result(PGresult *res, PGconn *conn, const char *query,
+ ExecStatusType expected)
+{
+ const char *err;
+
+ if (res && PQresultStatus(res) == expected)
+ return; /* A-OK */
+
+ write_msg(NULL, "SQL command failed\n");
+ if (res)
+ err = PQresultErrorMessage(res);
+ else
+ err = PQerrorMessage(conn);
+ write_msg(NULL, "Error message from server: %s", err);
+ write_msg(NULL, "The command was: %s\n", query);
+ exit_nicely();
+}