diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index 1167128716..d2ab719fe3 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -1,5 +1,5 @@ @@ -407,10 +407,11 @@ PostgreSQL documentation - The scripts or archives created by pg_dump - need to have superuser access in certain cases, such as when - disabling triggers or setting ownership of schema elements. - This option specifies the user name to use for those cases. + Specify the superuser user name to use when disabling triggers. + This is only relevant if @@ -481,6 +482,36 @@ PostgreSQL documentation + + + + + + This option is only relevant when creating a data-only dump. + It instructs pg_dump to include commands + to temporarily disable triggers on the target tables while + the data is reloaded. Use this if you have referential + integrity checks or other triggers on the tables that you + do not want to invoke during data reload. + + + + Presently, the commands emitted for + + + This option is only meaningful for the plain-text format. For + the other formats, you may specify the option when you + call pg_restore. + + + + diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml index 3e7f2c6fcc..aa87e2e6f4 100644 --- a/doc/src/sgml/ref/pg_restore.sgml +++ b/doc/src/sgml/ref/pg_restore.sgml @@ -1,4 +1,4 @@ - + @@ -336,8 +336,8 @@ - Specify the superuser user name to use when disabling triggers and/or setting ownership of schema elements. - By default, pg_restore will use the current user name if it is a superuser. + Specify the superuser user name to use when disabling triggers. + This is only relevant if @@ -402,6 +402,29 @@ + + + + + + This option is only relevant when performing a data-only restore. + It instructs pg_restore to execute commands + to temporarily disable triggers on the target tables while + the data is reloaded. Use this if you have referential + integrity checks or other triggers on the tables that you + do not want to invoke during data reload. + + + + Presently, the commands emitted for + + + diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 4188746471..47692bdeac 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -3,30 +3,15 @@ * common.c * common routines between pg_dump and pg4_dump * + * Since pg4_dump is long-dead code, there is no longer any useful distinction + * between this file and pg_dump.c. + * * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.63 2002/04/24 02:44:19 momjian Exp $ - * - * Modifications - 6/12/96 - dave@bensoft.com - version 1.13.dhb.2 - * - * - Fixed dumpTable output to output lengths for char and varchar types! - * - Added single. quote to twin single quote expansion for 'insert' string - * mode. - * - * Modifications 14-Sep-2000 - pjw@rhyme.com.au - * - Added enum for findTypeByOid to specify how to handle OID and which - * string to return - formatted type, or base type. If the base type - * is returned then fmtId is called on the string. - * - * Modifications 4-Apr-2001 - pjw@rhyme.com.au - * - Changed flagInhAttrs to check all parent tables for overridden settings - * and set flags accordingly. - * - * BEWARE: Since fmtId uses a static buffer, using 'useBaseTypeName' on more - * than one call in a line will cause problems. + * $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.64 2002/05/10 22:36:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -44,64 +29,368 @@ #include "strdup.h" #endif -static char **findParentsByOid(TableInfo *tbinfo, int numTables, - InhInfo *inhinfo, int numInherits, - const char *oid, - int *numParents, - int (**parentIndexes)[]); -static int findTableByOid(TableInfo *tbinfo, int numTables, const char *oid); +static void findParentsByOid(TableInfo *tblinfo, int numTables, + InhInfo *inhinfo, int numInherits, + const char *oid, + int *numParentsPtr, int **parentIndexes); +static void flagInhTables(TableInfo *tbinfo, int numTables, + InhInfo *inhinfo, int numInherits); static void flagInhAttrs(TableInfo *tbinfo, int numTables, InhInfo *inhinfo, int numInherits); static int strInArray(const char *pattern, char **arr, int arr_size); + /* - * findTypeByOid - * given an oid of a type, return its typename + * dumpSchema: + * we have a valid connection, we are now going to dump the schema + * into the file + */ + +TableInfo * +dumpSchema(Archive *fout, + int *numTablesPtr, + const bool aclsSkip, + const bool schemaOnly, + const bool dataOnly) +{ + int numNamespaces; + int numTypes; + int numFuncs; + int numTables; + int numInherits; + int numAggregates; + int numOperators; + NamespaceInfo *nsinfo; + TypeInfo *tinfo; + FuncInfo *finfo; + AggInfo *agginfo; + TableInfo *tblinfo; + InhInfo *inhinfo; + OprInfo *oprinfo; + + if (g_verbose) + write_msg(NULL, "reading namespaces\n"); + nsinfo = getNamespaces(&numNamespaces); + + if (g_verbose) + write_msg(NULL, "reading user-defined types\n"); + tinfo = getTypes(&numTypes); + + if (g_verbose) + write_msg(NULL, "reading user-defined functions\n"); + finfo = getFuncs(&numFuncs); + + if (g_verbose) + write_msg(NULL, "reading user-defined aggregate functions\n"); + agginfo = getAggregates(&numAggregates); + + if (g_verbose) + write_msg(NULL, "reading user-defined operators\n"); + oprinfo = getOperators(&numOperators); + + if (g_verbose) + write_msg(NULL, "reading user-defined tables\n"); + tblinfo = getTables(&numTables); + + if (g_verbose) + write_msg(NULL, "reading table inheritance information\n"); + inhinfo = getInherits(&numInherits); + + /* Link tables to parents, mark parents of target tables interesting */ + if (g_verbose) + write_msg(NULL, "finding inheritance relationships\n"); + flagInhTables(tblinfo, numTables, inhinfo, numInherits); + + if (g_verbose) + write_msg(NULL, "reading column info for interesting tables\n"); + getTableAttrs(tblinfo, numTables); + + if (g_verbose) + write_msg(NULL, "flagging inherited columns in subtables\n"); + flagInhAttrs(tblinfo, numTables, inhinfo, numInherits); + + if (!dataOnly) + { + if (g_verbose) + write_msg(NULL, "dumping out database comment\n"); + dumpDBComment(fout); + } + + if (!dataOnly) + { + if (g_verbose) + write_msg(NULL, "dumping out user-defined namespaces\n"); + dumpNamespaces(fout, nsinfo, numNamespaces); + } + + if (!dataOnly) + { + if (g_verbose) + write_msg(NULL, "dumping out user-defined types\n"); + dumpTypes(fout, finfo, numFuncs, tinfo, numTypes); + } + + if (g_verbose) + write_msg(NULL, "dumping out tables\n"); + dumpTables(fout, tblinfo, numTables, + aclsSkip, schemaOnly, dataOnly); + + if (!dataOnly) + { + if (g_verbose) + write_msg(NULL, "dumping out indexes\n"); + dumpIndexes(fout, tblinfo, numTables); + } + + if (!dataOnly) + { + if (g_verbose) + write_msg(NULL, "dumping out user-defined procedural languages\n"); + dumpProcLangs(fout, finfo, numFuncs); + } + + if (!dataOnly) + { + if (g_verbose) + write_msg(NULL, "dumping out user-defined functions\n"); + dumpFuncs(fout, finfo, numFuncs); + } + + if (!dataOnly) + { + if (g_verbose) + write_msg(NULL, "dumping out user-defined aggregate functions\n"); + dumpAggs(fout, agginfo, numAggregates); + } + + if (!dataOnly) + { + if (g_verbose) + write_msg(NULL, "dumping out user-defined operators\n"); + dumpOprs(fout, oprinfo, numOperators); + } + + *numTablesPtr = numTables; + return tblinfo; +} + +/* flagInhTables - + * Fill in parentIndexes fields of every target table, and mark + * parents of target tables as interesting * - * Can return various special cases for oid 0. + * Note that only direct ancestors of targets are marked interesting. + * This is sufficient; we don't much care whether they inherited their + * attributes or not. + * + * modifies tblinfo + */ +static void +flagInhTables(TableInfo *tblinfo, int numTables, + InhInfo *inhinfo, int numInherits) +{ + int i, + j; + int numParents; + int *parentIndexes; + + for (i = 0; i < numTables; i++) + { + /* Sequences and views never have parents */ + if (tblinfo[i].relkind == RELKIND_SEQUENCE || + tblinfo[i].relkind == RELKIND_VIEW) + continue; + + /* Don't bother computing anything for non-target tables, either */ + if (!tblinfo[i].dump) + continue; + + /* Find all the immediate parent tables */ + findParentsByOid(tblinfo, numTables, + inhinfo, numInherits, + tblinfo[i].oid, + &tblinfo[i].numParents, + &tblinfo[i].parentIndexes); + numParents = tblinfo[i].numParents; + parentIndexes = tblinfo[i].parentIndexes; + + /* Mark the parents as interesting for getTableAttrs */ + for (j = 0; j < numParents; j++) + { + int parentInd = parentIndexes[j]; + + tblinfo[parentInd].interesting = true; + } + } +} + +/* flagInhAttrs - + * for each dumpable table in tblinfo, flag its inherited attributes + * so when we dump the table out, we don't dump out the inherited attributes + * + * modifies tblinfo + */ +static void +flagInhAttrs(TableInfo *tblinfo, int numTables, + InhInfo *inhinfo, int numInherits) +{ + int i, + j, + k; + int parentInd; + int inhAttrInd; + int numParents; + int *parentIndexes; + bool foundAttr; /* Attr was found in a parent */ + bool foundNotNull; /* Attr was NOT NULL in a parent */ + bool defaultsMatch; /* All non-empty defaults match */ + bool defaultsFound; /* Found a default in a parent */ + char *attrDef; + char *inhDef; + + for (i = 0; i < numTables; i++) + { + /* Sequences and views never have parents */ + if (tblinfo[i].relkind == RELKIND_SEQUENCE || + tblinfo[i].relkind == RELKIND_VIEW) + continue; + + /* Don't bother computing anything for non-target tables, either */ + if (!tblinfo[i].dump) + continue; + + numParents = tblinfo[i].numParents; + parentIndexes = tblinfo[i].parentIndexes; + + if (numParents == 0) + continue; /* nothing to see here, move along */ + + /* + * For each attr, check the parent info: if no parent has an attr + * with the same name, then it's not inherited. If there *is* an + * attr with the same name, then only dump it if: + * + * - it is NOT NULL and zero parents are NOT NULL OR - it has a + * default value AND the default value does not match all parent + * default values, or no parents specify a default. + * + * See discussion on -hackers around 2-Apr-2001. + */ + for (j = 0; j < tblinfo[i].numatts; j++) + { + foundAttr = false; + foundNotNull = false; + defaultsMatch = true; + defaultsFound = false; + + attrDef = tblinfo[i].adef_expr[j]; + + for (k = 0; k < numParents; k++) + { + parentInd = parentIndexes[k]; + inhAttrInd = strInArray(tblinfo[i].attnames[j], + tblinfo[parentInd].attnames, + tblinfo[parentInd].numatts); + + if (inhAttrInd != -1) + { + foundAttr = true; + foundNotNull |= tblinfo[parentInd].notnull[inhAttrInd]; + if (attrDef != NULL) /* If we have a default, + * check parent */ + { + inhDef = tblinfo[parentInd].adef_expr[inhAttrInd]; + + if (inhDef != NULL) + { + defaultsFound = true; + defaultsMatch &= (strcmp(attrDef, inhDef) == 0); + } + } + } + } + + /* + * Based on the scan of the parents, decide if we can rely on + * the inherited attr + */ + if (foundAttr) /* Attr was inherited */ + { + /* Set inherited flag by default */ + tblinfo[i].inhAttrs[j] = true; + tblinfo[i].inhAttrDef[j] = true; + tblinfo[i].inhNotNull[j] = true; + + /* + * Clear it if attr had a default, but parents did not, or + * mismatch + */ + if ((attrDef != NULL) && (!defaultsFound || !defaultsMatch)) + { + tblinfo[i].inhAttrs[j] = false; + tblinfo[i].inhAttrDef[j] = false; + } + + /* + * Clear it if NOT NULL and none of the parents were NOT + * NULL + */ + if (tblinfo[i].notnull[j] && !foundNotNull) + { + tblinfo[i].inhAttrs[j] = false; + tblinfo[i].inhNotNull[j] = false; + } + } + } + } +} + + +/* + * findTableByOid + * finds the index (in tblinfo) of the table with the given oid + * returns -1 if not found * * NOTE: should hash this, but just do linear search for now */ - -char * -findTypeByOid(TypeInfo *tinfo, int numTypes, const char *oid, OidOptions opts) +int +findTableByOid(TableInfo *tblinfo, int numTables, const char *oid) { int i; - if (strcmp(oid, "0") == 0) + for (i = 0; i < numTables; i++) { - if ((opts & zeroAsOpaque) != 0) - return g_opaque_type; - else if ((opts & zeroAsAny) != 0) - return "'any'"; - else if ((opts & zeroAsStar) != 0) - return "*"; - else if ((opts & zeroAsNone) != 0) - return "NONE"; + if (strcmp(tblinfo[i].oid, oid) == 0) + return i; } + return -1; +} - for (i = 0; i < numTypes; i++) + +/* + * findFuncByOid + * finds the index (in finfo) of the function with the given OID + * returns -1 if not found + * + * NOTE: should hash this, but just do linear search for now + */ +int +findFuncByOid(FuncInfo *finfo, int numFuncs, const char *oid) +{ + int i; + + for (i = 0; i < numFuncs; i++) { - if (strcmp(tinfo[i].oid, oid) == 0) - { - if ((opts & useBaseTypeName) != 0) - return (char *) fmtId(tinfo[i].typname, false); - else - return tinfo[i].typedefn; - } + if (strcmp(finfo[i].oid, oid) == 0) + return i; } - - /* no suitable type name was found */ - return (NULL); + return -1; } /* * findOprByOid * given the oid of an operator, return the name of the operator * - * * NOTE: should hash this, but just do linear search for now - * */ char * findOprByOid(OprInfo *oprinfo, int numOprs, const char *oid) @@ -124,23 +413,22 @@ findOprByOid(OprInfo *oprinfo, int numOprs, const char *oid) /* * findParentsByOid - * given the oid of a class, return the names of its parent classes - * and assign the number of parents, and parent indexes to the last arguments. + * given the oid of a class, find its parent classes in tblinfo[] * - * - * returns NULL if none + * Returns the number of parents and their array indexes into the + * last two arguments. */ -static char ** +static void findParentsByOid(TableInfo *tblinfo, int numTables, - InhInfo *inhinfo, int numInherits, const char *oid, - int *numParentsPtr, int (**parentIndexes)[]) + InhInfo *inhinfo, int numInherits, + const char *oid, + int *numParentsPtr, int **parentIndexes) { int i, j; int parentInd, selfInd; - char **result; int numParents; numParents = 0; @@ -154,8 +442,7 @@ findParentsByOid(TableInfo *tblinfo, int numTables, if (numParents > 0) { - result = (char **) malloc(sizeof(char *) * numParents); - (*parentIndexes) = malloc(sizeof(int) * numParents); + *parentIndexes = (int *) malloc(sizeof(int) * numParents); j = 0; for (i = 0; i < numInherits; i++) { @@ -178,17 +465,12 @@ findParentsByOid(TableInfo *tblinfo, int numTables, exit_nicely(); } - (**parentIndexes)[j] = parentInd; - result[j++] = tblinfo[parentInd].relname; + (*parentIndexes)[j++] = parentInd; } } - return result; } else - { - (*parentIndexes) = NULL; - return NULL; - } + *parentIndexes = NULL; } /* @@ -247,7 +529,6 @@ parseNumericArray(const char *str, char **array, int arraysize) * takes in a string and a string array and the number of elements in the * string array. * returns the index if the string is somewhere in the array, -1 otherwise - * */ static int @@ -262,333 +543,3 @@ strInArray(const char *pattern, char **arr, int arr_size) } return -1; } - -/* - * dumpSchema: - * we have a valid connection, we are now going to dump the schema - * into the file - * - */ - -TableInfo * -dumpSchema(Archive *fout, - int *numTablesPtr, - const char *tablename, - const bool aclsSkip, - const bool oids, - const bool schemaOnly, - const bool dataOnly) -{ - int numTypes; - int numFuncs; - int numTables; - int numInherits; - int numAggregates; - int numOperators; - int numIndexes; - TypeInfo *tinfo = NULL; - FuncInfo *finfo = NULL; - AggInfo *agginfo = NULL; - TableInfo *tblinfo = NULL; - InhInfo *inhinfo = NULL; - OprInfo *oprinfo = NULL; - IndInfo *indinfo = NULL; - - if (g_verbose) - write_msg(NULL, "reading user-defined types\n"); - tinfo = getTypes(&numTypes); - - if (g_verbose) - write_msg(NULL, "reading user-defined functions\n"); - finfo = getFuncs(&numFuncs); - - if (g_verbose) - write_msg(NULL, "reading user-defined aggregate functions\n"); - agginfo = getAggregates(&numAggregates); - - if (g_verbose) - write_msg(NULL, "reading user-defined operators\n"); - oprinfo = getOperators(&numOperators); - - if (g_verbose) - write_msg(NULL, "reading user-defined tables\n"); - tblinfo = getTables(&numTables, finfo, numFuncs, tablename); - - if (g_verbose) - write_msg(NULL, "reading index information\n"); - indinfo = getIndexes(&numIndexes); - - if (g_verbose) - write_msg(NULL, "reading table inheritance information\n"); - inhinfo = getInherits(&numInherits); - - if (g_verbose) - write_msg(NULL, "finding the column names and types for each table\n"); - getTableAttrs(tblinfo, numTables); - - if (g_verbose) - write_msg(NULL, "flagging inherited columns in subtables\n"); - flagInhAttrs(tblinfo, numTables, inhinfo, numInherits); - - if (!tablename && !dataOnly) - { - if (g_verbose) - write_msg(NULL, "dumping out database comment\n"); - dumpDBComment(fout); - } - - if (!tablename && fout) - { - if (g_verbose) - write_msg(NULL, "dumping out user-defined types\n"); - dumpTypes(fout, finfo, numFuncs, tinfo, numTypes); - } - - if (g_verbose) - write_msg(NULL, "dumping out tables\n"); - - dumpTables(fout, tblinfo, numTables, tablename, - aclsSkip, schemaOnly, dataOnly); - - if (fout && !dataOnly) - { - if (g_verbose) - write_msg(NULL, "dumping out indexes\n"); - dumpIndexes(fout, indinfo, numIndexes, tblinfo, numTables, tablename); - } - - if (!tablename && !dataOnly) - { - if (g_verbose) - write_msg(NULL, "dumping out user-defined procedural languages\n"); - dumpProcLangs(fout, finfo, numFuncs, tinfo, numTypes); - } - - if (!tablename && !dataOnly) - { - if (g_verbose) - write_msg(NULL, "dumping out user-defined functions\n"); - dumpFuncs(fout, finfo, numFuncs, tinfo, numTypes); - } - - if (!tablename && !dataOnly) - { - if (g_verbose) - write_msg(NULL, "dumping out user-defined aggregate functions\n"); - dumpAggs(fout, agginfo, numAggregates, tinfo, numTypes); - } - - if (!tablename && !dataOnly) - { - if (g_verbose) - write_msg(NULL, "dumping out user-defined operators\n"); - dumpOprs(fout, oprinfo, numOperators, tinfo, numTypes); - } - - *numTablesPtr = numTables; - clearAggInfo(agginfo, numAggregates); - clearOprInfo(oprinfo, numOperators); - clearTypeInfo(tinfo, numTypes); - clearFuncInfo(finfo, numFuncs); - clearInhInfo(inhinfo, numInherits); - clearIndInfo(indinfo, numIndexes); - return tblinfo; -} - -/* flagInhAttrs - - * for each table in tblinfo, flag its inherited attributes - * so when we dump the table out, we don't dump out the inherited attributes - * - * initializes the parentRels field of each table - * - * modifies tblinfo - * - */ -static void -flagInhAttrs(TableInfo *tblinfo, int numTables, - InhInfo *inhinfo, int numInherits) -{ - int i, - j, - k; - int parentInd; - int inhAttrInd; - int (*parentIndexes)[]; - bool foundAttr; /* Attr was found in a parent */ - bool foundNotNull; /* Attr was NOT NULL in a parent */ - bool defaultsMatch; /* All non-empty defaults match */ - bool defaultsFound; /* Found a default in a parent */ - char *attrDef; - char *inhDef; - - /* - * we go backwards because the tables in tblinfo are in OID order, - * meaning the subtables are after the parent tables we flag inherited - * attributes from child tables first - */ - for (i = numTables - 1; i >= 0; i--) - { - /* Sequences can never have parents, and attr info is undefined */ - if (tblinfo[i].relkind == RELKIND_SEQUENCE) - continue; - - /* Get all the parents and their indexes. */ - tblinfo[i].parentRels = findParentsByOid(tblinfo, numTables, - inhinfo, numInherits, - tblinfo[i].oid, - &tblinfo[i].numParents, - &parentIndexes); - - /* - * For each attr, check the parent info: if no parent has an attr - * with the same name, then it's not inherited. If there *is* an - * attr with the same name, then only dump it if: - * - * - it is NOT NULL and zero parents are NOT NULL OR - it has a - * default value AND the default value does not match all parent - * default values, or no parents specify a default. - * - * See discussion on -hackers around 2-Apr-2001. - */ - for (j = 0; j < tblinfo[i].numatts; j++) - { - foundAttr = false; - foundNotNull = false; - defaultsMatch = true; - defaultsFound = false; - - attrDef = tblinfo[i].adef_expr[j]; - - for (k = 0; k < tblinfo[i].numParents; k++) - { - parentInd = (*parentIndexes)[k]; - - if (parentInd < 0) - { - /* shouldn't happen unless findParentsByOid is broken */ - write_msg(NULL, "failed sanity check, table \"%s\" not found by flagInhAttrs\n", - tblinfo[i].parentRels[k]); - exit_nicely(); - }; - - inhAttrInd = strInArray(tblinfo[i].attnames[j], - tblinfo[parentInd].attnames, - tblinfo[parentInd].numatts); - - if (inhAttrInd != -1) - { - foundAttr = true; - foundNotNull |= tblinfo[parentInd].notnull[inhAttrInd]; - if (attrDef != NULL) /* It we have a default, - * check parent */ - { - inhDef = tblinfo[parentInd].adef_expr[inhAttrInd]; - - if (inhDef != NULL) - { - defaultsFound = true; - defaultsMatch &= (strcmp(attrDef, inhDef) == 0); - }; - }; - }; - }; - - /* - * Based on the scan of the parents, decide if we can rely on - * the inherited attr - */ - if (foundAttr) /* Attr was inherited */ - { - /* Set inherited flag by default */ - tblinfo[i].inhAttrs[j] = 1; - tblinfo[i].inhAttrDef[j] = 1; - tblinfo[i].inhNotNull[j] = 1; - - /* - * Clear it if attr had a default, but parents did not, or - * mismatch - */ - if ((attrDef != NULL) && (!defaultsFound || !defaultsMatch)) - { - tblinfo[i].inhAttrs[j] = 0; - tblinfo[i].inhAttrDef[j] = 0; - } - - /* - * Clear it if NOT NULL and none of the parents were NOT - * NULL - */ - if (tblinfo[i].notnull[j] && !foundNotNull) - { - tblinfo[i].inhAttrs[j] = 0; - tblinfo[i].inhNotNull[j] = 0; - } - } - } - } -} - - -/* - * findTableByName - * finds the index (in tblinfo) of the table with the given relname - * returns -1 if not found - * - * NOTE: should hash this, but just do linear search for now - */ - -int -findTableByName(TableInfo *tblinfo, int numTables, const char *relname) -{ - int i; - - for (i = 0; i < numTables; i++) - { - if (strcmp(tblinfo[i].relname, relname) == 0) - return i; - } - return -1; -} - -/* - * findTableByOid - * finds the index (in tblinfo) of the table with the given oid - * returns -1 if not found - * - * NOTE: should hash this, but just do linear search for now - */ - -static int -findTableByOid(TableInfo *tblinfo, int numTables, const char *oid) -{ - int i; - - for (i = 0; i < numTables; i++) - { - if (strcmp(tblinfo[i].oid, oid) == 0) - return i; - } - return -1; -} - - -/* - * findFuncByName - * finds the index (in finfo) of the function with the given name - * returns -1 if not found - * - * NOTE: should hash this, but just do linear search for now - */ - -int -findFuncByName(FuncInfo *finfo, int numFuncs, const char *name) -{ - int i; - - for (i = 0; i < numFuncs; i++) - { - if (strcmp(finfo[i].proname, name) == 0) - return i; - } - return -1; -} diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index 32d4c1e88e..dca162cd76 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -15,27 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup.h,v 1.18 2002/02/11 00:18:20 tgl Exp $ - * - * Modifications - 28-Jun-2000 - pjw@rhyme.com.au - * - * Initial version. - * - * - * Modifications - 28-Jul-2000 - pjw@rhyme.com.au (1.45) - * - * Added --create, --no-owner, --superuser, --no-reconnect (pg_dump & pg_restore) - * Added code to dump 'Create Schema' statement (pg_dump) - * Don't bother to disable/enable triggers if we don't have a superuser (pg_restore) - * Cleaned up code for reconnecting to database. - * Force a reconnect as superuser before enabling/disabling triggers. - * - * Modifications - 31-Jul-2000 - pjw@rhyme.com.au (1.46, 1.47) - * Added & Removed --throttle (pg_dump) - * Fixed minor bug in language dumping code: expbuffres were not being reset. - * Fixed version number initialization in _allocAH (pg_backup_archiver.c) - * Added second connection when restoring BLOBs to allow temp. table to survive - * (db reconnection causes temp tables to be lost). + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup.h,v 1.19 2002/05/10 22:36:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -87,6 +67,7 @@ typedef struct _restoreOptions * cirsumstances */ int use_setsessauth;/* use SET SESSSION AUTHORIZATION instead * of \connect */ + int disable_triggers;/* disable triggers during data-only restore */ char *superuser; /* Username to use as superuser */ int dataOnly; int dropSchema; @@ -152,10 +133,12 @@ PGconn *ConnectDatabase(Archive *AH, /* Called to add a TOC entry */ -extern void ArchiveEntry(Archive *AH, const char *oid, const char *name, - const char *desc, const char *((*deps)[]), const char *defn, - const char *dropStmt, const char *copyStmt, const char *owner, - DataDumperPtr dumpFn, void *dumpArg); +extern void ArchiveEntry(Archive *AHX, const char *oid, const char *name, + const char *namespace, const char *owner, + const char *desc, const char *((*deps)[]), + const char *defn, const char *dropStmt, + const char *copyStmt, + DataDumperPtr dumpFn, void *dumpArg); /* Called to write *data* to the archive */ extern int WriteData(Archive *AH, const void *data, int dLen); diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index 11c62a0298..272b98cece 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.45 2002/05/06 17:34:45 tgl Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.46 2002/05/10 22:36:26 tgl Exp $ * * Modifications - 28-Jun-2000 - pjw@rhyme.com.au * @@ -95,8 +95,10 @@ static ArchiveHandle *_allocAH(const char *FileSpec, const ArchiveFormat fmt, const int compression, ArchiveMode mode); static int _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData); +static void _doSetSessionAuth(ArchiveHandle *AH, const char *autharg); static void _reconnectAsOwner(ArchiveHandle *AH, const char *dbname, TocEntry *te); static void _reconnectAsUser(ArchiveHandle *AH, const char *dbname, const char *user); +static void _selectOutputSchema(ArchiveHandle *AH, const char *schemaName); static teReqs _tocEntryRequired(TocEntry *te, RestoreOptions *ropt); static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt); @@ -208,23 +210,13 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt) AHX->minRemoteVersion = 070100; AHX->maxRemoteVersion = 999999; - ConnectDatabase(AHX, ropt->dbname, ropt->pghost, ropt->pgport, ropt->username, + ConnectDatabase(AHX, ropt->dbname, + ropt->pghost, ropt->pgport, ropt->username, ropt->requirePassword, ropt->ignoreVersion); - - /* - * If no superuser was specified then see if the current user will - * do... - */ - if (!ropt->superuser) - { - if (UserIsSuperuser(AH, ConnectedUser(AH))) - ropt->superuser = strdup(ConnectedUser(AH)); - } - } /* - * Work out if we have an implied data-only retore. This can happen if + * Work out if we have an implied data-only restore. This can happen if * the dump was data only or if the user has used a toc list to * exclude all of the schema data. All we do is look for schema * entries - if none are found then we set the dataOnly flag. @@ -253,12 +245,6 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt) } } - if (!ropt->superuser) - write_msg(modulename, "WARNING:\n" - " Data restoration may fail because existing triggers cannot be disabled\n" - " (no superuser user name specified). This is only a problem when\n" - " restoring into a database with already existing triggers.\n"); - /* * Setup the output file if necessary. */ @@ -280,8 +266,9 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt) { /* We want the schema */ ahlog(AH, 1, "dropping %s %s\n", te->desc, te->name); - /* Reconnect if necessary */ + /* Select owner and schema as necessary */ _reconnectAsOwner(AH, NULL, te); + _selectOutputSchema(AH, te->namespace); /* Drop it */ ahprintf(AH, "%s", te->dropStmt); } @@ -376,6 +363,7 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt) * have reconnected) */ _reconnectAsOwner(AH, NULL, te); + _selectOutputSchema(AH, te->namespace); ahlog(AH, 1, "restoring data for table %s\n", te->name); @@ -433,7 +421,7 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt) if ((reqs & REQ_DATA) != 0) /* We loaded the data */ { ahlog(AH, 1, "fixing up large object cross-reference for %s\n", te->name); - FixupBlobRefs(AH, te->name); + FixupBlobRefs(AH, te); } } else @@ -501,30 +489,31 @@ _canRestoreBlobs(ArchiveHandle *AH) static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt) { - char *oldUser = NULL; + char *oldUser; + char *oldSchema; - /* Can't do much if we're connected & don't have a superuser */ - /* Also, don't bother with triggers unless a data-only retore. */ - if (!ropt->dataOnly || (_restoringToDB(AH) && !ropt->superuser)) + /* This hack is only needed in a data-only restore */ + if (!ropt->dataOnly || !ropt->disable_triggers) return; + oldUser = strdup(AH->currUser); + oldSchema = strdup(AH->currSchema); + /* - * Reconnect as superuser if possible, since they are the only ones - * who can update pg_class... + * Become superuser if possible, since they are the only ones + * who can update pg_class. If -S was not given, but we are allowed + * to use SET SESSION AUTHORIZATION, assume the initial user identity + * is a superuser. Otherwise we just have to bull ahead anyway. */ if (ropt->superuser) { - if (!_restoringToDB(AH) || !ConnectedUserIsSuperuser(AH)) - { - /* - * If we're not allowing changes for ownership, then remember - * the user so we can change it back here. Otherwise, let - * _reconnectAsOwner do what it has to do. - */ - if (ropt->noOwner) - oldUser = strdup(ConnectedUser(AH)); - _reconnectAsUser(AH, NULL, ropt->superuser); - } + _reconnectAsUser(AH, NULL, ropt->superuser); + /* be careful to preserve schema setting */ + _selectOutputSchema(AH, oldSchema); + } + else if (AH->ropt->use_setsessauth) + { + _doSetSessionAuth(AH, "DEFAULT"); } ahlog(AH, 1, "disabling triggers\n"); @@ -538,52 +527,59 @@ _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *rop /* * Just update the AFFECTED table, if known. */ - if (te && te->name && strlen(te->name) > 0) - ahprintf(AH, "UPDATE \"pg_class\" SET \"reltriggers\" = 0 WHERE \"relname\" = '%s';\n\n", - te->name); + ahprintf(AH, "UPDATE pg_class SET reltriggers = 0 " + "WHERE oid = '%s'::regclass;\n\n", + fmtId(te->name, false)); else - ahprintf(AH, "UPDATE \"pg_class\" SET \"reltriggers\" = 0 WHERE \"relname\" !~ '^pg_';\n\n"); + ahprintf(AH, "UPDATE pg_class SET reltriggers = 0 FROM pg_namespace " + "WHERE relnamespace = pg_namespace.oid AND nspname !~ '^pg_';\n\n"); /* - * Restore the user connection from the start of this procedure if - * _reconnectAsOwner is disabled. + * Restore original user and schema state. */ - if (ropt->noOwner && oldUser) + if (ropt->superuser) { _reconnectAsUser(AH, NULL, oldUser); - free(oldUser); + /* be careful to preserve schema setting */ + _selectOutputSchema(AH, oldSchema); } + else if (AH->ropt->use_setsessauth) + { + _doSetSessionAuth(AH, fmtId(oldUser, false)); + } + free(oldUser); + free(oldSchema); } static void _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt) { - char *oldUser = NULL; + char *oldUser; + char *oldSchema; - /* Can't do much if we're connected & don't have a superuser */ - /* Also, don't bother with triggers unless a data-only retore. */ - if (!ropt->dataOnly || (_restoringToDB(AH) && !ropt->superuser)) + /* This hack is only needed in a data-only restore */ + if (!ropt->dataOnly || !ropt->disable_triggers) return; + oldUser = strdup(AH->currUser); + oldSchema = strdup(AH->currSchema); + /* - * Reconnect as superuser if possible, since they are the only ones - * who can update pg_class... + * Become superuser if possible, since they are the only ones + * who can update pg_class. If -S was not given, but we are allowed + * to use SET SESSION AUTHORIZATION, assume the initial user identity + * is a superuser. Otherwise we just have to bull ahead anyway. */ if (ropt->superuser) { - if (!_restoringToDB(AH) || !ConnectedUserIsSuperuser(AH)) - { - /* - * If we're not allowing changes for ownership, then remember - * the user so we can change it back here. Otherwise, let - * _reconnectAsOwner do what it has to do - */ - if (ropt->noOwner) - oldUser = strdup(ConnectedUser(AH)); - - _reconnectAsUser(AH, NULL, ropt->superuser); - } + _reconnectAsUser(AH, NULL, ropt->superuser); + /* be careful to preserve schema setting */ + _selectOutputSchema(AH, oldSchema); + } + else if (AH->ropt->use_setsessauth) + { + _doSetSessionAuth(AH, "DEFAULT"); } ahlog(AH, 1, "enabling triggers\n"); @@ -593,29 +589,36 @@ _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt * 'SET' command when one is available. */ ahprintf(AH, "-- Enable triggers\n"); - if (te && te->name && strlen(te->name) > 0) - { - ahprintf(AH, "UPDATE pg_class SET reltriggers = " - "(SELECT count(*) FROM pg_trigger where pg_class.oid = tgrelid) " - "WHERE relname = '%s';\n\n", - te->name); - } - else - { - ahprintf(AH, "UPDATE \"pg_class\" SET \"reltriggers\" = " - "(SELECT count(*) FROM pg_trigger where pg_class.oid = tgrelid) " - "WHERE \"relname\" !~ '^pg_';\n\n"); - } /* - * Restore the user connection from the start of this procedure if - * _reconnectAsOwner is disabled. + * Just update the AFFECTED table, if known. */ - if (ropt->noOwner && oldUser) + if (te && te->name && strlen(te->name) > 0) + ahprintf(AH, "UPDATE pg_class SET reltriggers = " + "(SELECT count(*) FROM pg_trigger where pg_class.oid = tgrelid) " + "WHERE oid = '%s'::regclass;\n\n", + fmtId(te->name, false)); + else + ahprintf(AH, "UPDATE pg_class SET reltriggers = " + "(SELECT count(*) FROM pg_trigger where pg_class.oid = tgrelid) " + "FROM pg_namespace " + "WHERE relnamespace = pg_namespace.oid AND nspname !~ '^pg_';\n\n"); + + /* + * Restore original user and schema state. + */ + if (ropt->superuser) { _reconnectAsUser(AH, NULL, oldUser); - free(oldUser); + /* be careful to preserve schema setting */ + _selectOutputSchema(AH, oldSchema); } + else if (AH->ropt->use_setsessauth) + { + _doSetSessionAuth(AH, fmtId(oldUser, false)); + } + free(oldUser); + free(oldSchema); } /* @@ -642,8 +645,10 @@ WriteData(Archive *AHX, const void *data, int dLen) /* Public */ void ArchiveEntry(Archive *AHX, const char *oid, const char *name, - const char *desc, const char *((*deps)[]), const char *defn, - const char *dropStmt, const char *copyStmt, const char *owner, + const char *namespace, const char *owner, + const char *desc, const char *((*deps)[]), + const char *defn, const char *dropStmt, + const char *copyStmt, DataDumperPtr dumpFn, void *dumpArg) { ArchiveHandle *AH = (ArchiveHandle *) AHX; @@ -664,21 +669,21 @@ ArchiveEntry(Archive *AHX, const char *oid, const char *name, newToc->id = AH->lastID; newToc->name = strdup(name); + newToc->namespace = namespace ? strdup(namespace) : NULL; + newToc->owner = strdup(owner); newToc->desc = strdup(desc); - - newToc->oid = strdup(oid); - newToc->depOid = deps; - _fixupOidInfo(newToc); - - newToc->defn = strdup(defn); newToc->dropStmt = strdup(dropStmt); newToc->copyStmt = copyStmt ? strdup(copyStmt) : NULL; - newToc->owner = strdup(owner); + + newToc->oid = strdup(oid); + newToc->depOid = deps; /* NB: not copied */ + _fixupOidInfo(newToc); + newToc->printed = 0; newToc->formatData = NULL; - newToc->dataDumper = dumpFn, - newToc->dataDumperArg = dumpArg; + newToc->dataDumper = dumpFn; + newToc->dataDumperArg = dumpArg; newToc->hadDumper = dumpFn ? 1 : 0; @@ -1667,6 +1672,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt, AH->currUser = strdup(""); /* So it's valid, but we can free() it * later if necessary */ + AH->currSchema = strdup(""); /* ditto */ AH->toc = (TocEntry *) calloc(1, sizeof(TocEntry)); if (!AH->toc) @@ -1789,6 +1795,7 @@ WriteToc(ArchiveHandle *AH) WriteStr(AH, te->defn); WriteStr(AH, te->dropStmt); WriteStr(AH, te->copyStmt); + WriteStr(AH, te->namespace); WriteStr(AH, te->owner); /* Dump list of dependencies */ @@ -1840,6 +1847,9 @@ ReadToc(ArchiveHandle *AH) if (AH->version >= K_VERS_1_3) te->copyStmt = ReadStr(AH); + if (AH->version >= K_VERS_1_6) + te->namespace = ReadStr(AH); + te->owner = ReadStr(AH); /* Read TOC entry dependencies */ @@ -1975,6 +1985,33 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt) return res; } +/* + * Issue a SET SESSION AUTHORIZATION command. Caller is responsible + * for updating state if appropriate. Note that caller must also quote + * the argument if it's a username (it might be DEFAULT, too). + */ +static void +_doSetSessionAuth(ArchiveHandle *AH, const char *autharg) +{ + if (RestoringToDB(AH)) + { + PQExpBuffer qry = createPQExpBuffer(); + PGresult *res; + + appendPQExpBuffer(qry, "SET SESSION AUTHORIZATION %s;", autharg); + res = PQexec(AH->connection, qry->data); + + if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) + die_horribly(AH, modulename, "could not set session user to %s: %s", + autharg, PQerrorMessage(AH->connection)); + + PQclear(res); + destroyPQExpBuffer(qry); + } + else + ahprintf(AH, "SET SESSION AUTHORIZATION %s;\n\n", autharg); +} + /* * Issue the commands to connect to the database as the specified user @@ -1999,25 +2036,7 @@ _reconnectAsUser(ArchiveHandle *AH, const char *dbname, const char *user) */ if (!dbname && AH->ropt->use_setsessauth) { - if (RestoringToDB(AH)) - { - PQExpBuffer qry = createPQExpBuffer(); - PGresult *res; - - appendPQExpBuffer(qry, "SET SESSION AUTHORIZATION %s;", - fmtId(user, false)); - res = PQexec(AH->connection, qry->data); - - if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) - die_horribly(AH, modulename, "could not set session user to %s: %s", - user, PQerrorMessage(AH->connection)); - - PQclear(res); - destroyPQExpBuffer(qry); - } - else - ahprintf(AH, "SET SESSION AUTHORIZATION %s;\n\n", - fmtId(user, false)); + _doSetSessionAuth(AH, fmtId(user, false)); } else if (AH->ropt && AH->ropt->noReconnect) { @@ -2038,6 +2057,11 @@ _reconnectAsUser(ArchiveHandle *AH, const char *dbname, const char *user) ahprintf(AH, qry->data); destroyPQExpBuffer(qry); + + /* don't assume we still know the output schema */ + if (AH->currSchema) + free(AH->currSchema); + AH->currSchema = strdup(""); } /* @@ -2066,6 +2090,43 @@ _reconnectAsOwner(ArchiveHandle *AH, const char *dbname, TocEntry *te) } +/* + * Issue the commands to select the specified schema as the current schema + * in the target database. + */ +static void +_selectOutputSchema(ArchiveHandle *AH, const char *schemaName) +{ + if (!schemaName || *schemaName == '\0' || + strcmp(AH->currSchema, schemaName) == 0) + return; /* no need to do anything */ + + if (RestoringToDB(AH)) + { + PQExpBuffer qry = createPQExpBuffer(); + PGresult *res; + + appendPQExpBuffer(qry, "SET search_path = %s;", + fmtId(schemaName, false)); + res = PQexec(AH->connection, qry->data); + + if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) + die_horribly(AH, modulename, "could not set search_path to %s: %s", + schemaName, PQerrorMessage(AH->connection)); + + PQclear(res); + destroyPQExpBuffer(qry); + } + else + ahprintf(AH, "SET search_path = %s;\n\n", + fmtId(schemaName, false)); + + if (AH->currSchema) + free(AH->currSchema); + AH->currSchema = strdup(schemaName); +} + + /* * fmtId * @@ -2139,16 +2200,19 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat { char *pfx; - /* Reconnect if necessary */ + /* Select owner and schema as necessary */ _reconnectAsOwner(AH, NULL, te); + _selectOutputSchema(AH, te->namespace); if (isData) pfx = "Data for "; else pfx = ""; - ahprintf(AH, "--\n-- %sTOC Entry ID %d (OID %s)\n--\n-- Name: %s Type: %s Owner: %s\n", - pfx, te->id, te->oid, te->name, te->desc, te->owner); + ahprintf(AH, "--\n-- %sTOC Entry ID %d (OID %s)\n--\n-- Name: %s Type: %s Schema: %s Owner: %s\n", + pfx, te->id, te->oid, te->name, te->desc, + te->namespace ? te->namespace : "-", + te->owner); if (AH->PrintExtraTocPtr !=NULL) (*AH->PrintExtraTocPtr) (AH, te); ahprintf(AH, "--\n\n"); diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h index 0fbce03f52..95ab2133ca 100644 --- a/src/bin/pg_dump/pg_backup_archiver.h +++ b/src/bin/pg_dump/pg_backup_archiver.h @@ -17,17 +17,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.42 2002/04/24 02:21:04 momjian Exp $ - * - * Modifications - 28-Jun-2000 - pjw@rhyme.com.au - * - Initial version. - * - * Modifications - 15-Sep-2000 - pjw@rhyme.com.au - * - Added braceDepth to sqlparseInfo to handle braces in rule definitions. - * - * Modifications - 31-Mar-2001 - pjw@rhyme.com.au (1.50) - * - Make dependencies work on ArchiveEntry calls so that UDTs will - * dump in correct order. + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.43 2002/05/10 22:36:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -68,8 +58,8 @@ typedef z_stream *z_streamp; #include "libpq-fe.h" #define K_VERS_MAJOR 1 -#define K_VERS_MINOR 5 -#define K_VERS_REV 7 +#define K_VERS_MINOR 6 +#define K_VERS_REV 0 /* Data block types */ #define BLK_DATA 1 @@ -82,7 +72,8 @@ typedef z_stream *z_streamp; #define K_VERS_1_3 (( (1 * 256 + 3) * 256 + 0) * 256 + 0) /* BLOBs */ #define K_VERS_1_4 (( (1 * 256 + 4) * 256 + 0) * 256 + 0) /* Date & name in header */ #define K_VERS_1_5 (( (1 * 256 + 5) * 256 + 0) * 256 + 0) /* Handle dependencies */ -#define K_VERS_MAX (( (1 * 256 + 5) * 256 + 255) * 256 + 0) +#define K_VERS_1_6 (( (1 * 256 + 6) * 256 + 0) * 256 + 0) /* Schema field in TOCs */ +#define K_VERS_MAX (( (1 * 256 + 6) * 256 + 255) * 256 + 0) /* No of BLOBs to restore in 1 TX */ #define BLOB_BATCH_SIZE 100 @@ -235,6 +226,7 @@ typedef struct _archiveHandle int tocCount; /* Number of TOC entries */ struct _tocEntry *currToc; /* Used when dumping data */ char *currUser; /* Restore: current username in script */ + char *currSchema; /* Restore: current schema in script */ int compression; /* Compression requested on open */ ArchiveMode mode; /* File mode - r or w */ void *formatData; /* Header data specific to file format */ @@ -254,11 +246,12 @@ typedef struct _tocEntry int hadDumper; /* Archiver was passed a dumper routine * (used in restore) */ char *name; + char *namespace; /* null or empty string if not in a schema */ + char *owner; char *desc; char *defn; char *dropStmt; char *copyStmt; - char *owner; char *oid; /* Oid of source of entry */ Oid oidVal; /* Value of above */ const char *((*depOid)[]); @@ -314,9 +307,6 @@ extern OutputContext SetOutput(ArchiveHandle *AH, char *filename, int compressio extern void ResetOutput(ArchiveHandle *AH, OutputContext savedContext); extern int RestoringToDB(ArchiveHandle *AH); extern int ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *newUser); -extern int UserIsSuperuser(ArchiveHandle *AH, char *user); -extern char *ConnectedUser(ArchiveHandle *AH); -extern int ConnectedUserIsSuperuser(ArchiveHandle *AH); int ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle *AH); int ahprintf(ArchiveHandle *AH, const char *fmt,...) __attribute__((format(printf, 2, 3))); diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c index be638d226f..eb7bcafbc3 100644 --- a/src/bin/pg_dump/pg_backup_db.c +++ b/src/bin/pg_dump/pg_backup_db.c @@ -5,25 +5,7 @@ * Implements the basic DB functions used by the archiver. * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.31 2002/01/18 19:17:05 momjian Exp $ - * - * NOTES - * - * Modifications - 04-Jan-2001 - pjw@rhyme.com.au - * - * - Check results of PQ routines more carefully. - * - * Modifications - 19-Mar-2001 - pjw@rhyme.com.au - * - * - Avoid forcing table name to lower case in FixupBlobXrefs! - * - * - * Modifications - 18-Jan-2002 - pjw@rhyme.com.au - * - * - Split ExecuteSqlCommandBuf into 3 routines for (slightly) improved - * clarity. Modify loop to cater for COPY commands buried in the SQL - * command buffer (prev version assumed COPY command was executed - * in prior call). This was to fix the buf in the 'set max oid' code. + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.32 2002/05/10 22:36:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -219,57 +201,6 @@ _check_database_version(ArchiveHandle *AH, bool ignoreVersion) } } -/* - * Check if a given user is a superuser. - */ -int -UserIsSuperuser(ArchiveHandle *AH, char *user) -{ - PQExpBuffer qry = createPQExpBuffer(); - PGresult *res; - int i_usesuper; - int ntups; - int isSuper; - - /* Get the superuser setting */ - appendPQExpBuffer(qry, "select usesuper from pg_user where usename = '%s'", user); - res = PQexec(AH->connection, qry->data); - - if (!res) - die_horribly(AH, modulename, "null result checking superuser status of %s\n", user); - - if (PQresultStatus(res) != PGRES_TUPLES_OK) - die_horribly(AH, modulename, "could not check superuser status of %s: %s", - user, PQerrorMessage(AH->connection)); - - ntups = PQntuples(res); - - if (ntups == 0) - isSuper = 0; - else - { - i_usesuper = PQfnumber(res, "usesuper"); - isSuper = (strcmp(PQgetvalue(res, 0, i_usesuper), "t") == 0); - } - PQclear(res); - - destroyPQExpBuffer(qry); - - return isSuper; -} - -int -ConnectedUserIsSuperuser(ArchiveHandle *AH) -{ - return UserIsSuperuser(AH, PQuser(AH->connection)); -} - -char * -ConnectedUser(ArchiveHandle *AH) -{ - return PQuser(AH->connection); -} - /* * Reconnect to the server. If dbname is not NULL, use that database, * else the one associated with the archive handle. If username is @@ -310,6 +241,11 @@ ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *username) AH->username = strdup(newusername); /* XXX Why don't we update AH->dbname? */ + /* don't assume we still know the output schema */ + if (AH->currSchema) + free(AH->currSchema); + AH->currSchema = strdup(""); + return 1; } @@ -481,13 +417,6 @@ ConnectDatabase(Archive *AHX, PQsetNoticeProcessor(AH->connection, notice_processor, NULL); - /* - * AH->currUser = PQuser(AH->connection); - * - * Removed because it prevented an initial \connect when dumping to SQL - * in pg_dump. - */ - return AH->connection; } @@ -775,8 +704,9 @@ ExecuteSqlCommandBuf(ArchiveHandle *AH, void *qryv, int bufLen) } void -FixupBlobRefs(ArchiveHandle *AH, char *tablename) +FixupBlobRefs(ArchiveHandle *AH, TocEntry *te) { + PQExpBuffer tblName; PQExpBuffer tblQry; PGresult *res, *uRes; @@ -784,44 +714,55 @@ FixupBlobRefs(ArchiveHandle *AH, char *tablename) n; char *attr; - if (strcmp(tablename, BLOB_XREF_TABLE) == 0) + if (strcmp(te->name, BLOB_XREF_TABLE) == 0) return; + tblName = createPQExpBuffer(); tblQry = createPQExpBuffer(); - appendPQExpBuffer(tblQry, "SELECT a.attname FROM pg_class c, pg_attribute a, pg_type t " - " WHERE a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid " - " AND t.typname in ('oid', 'lo') AND c.relname = '%s';", tablename); + if (te->namespace && strlen(te->namespace) > 0) + appendPQExpBuffer(tblName, "%s.", + fmtId(te->namespace, false)); + appendPQExpBuffer(tblName, "%s", + fmtId(te->name, false)); + + appendPQExpBuffer(tblQry, + "SELECT a.attname FROM " + "pg_catalog.pg_attribute a, pg_catalog.pg_type t " + "WHERE a.attnum > 0 AND a.attrelid = '%s'::regclass " + "AND a.atttypid = t.oid AND t.typname in ('oid', 'lo')", + tblName->data); res = PQexec(AH->blobConnection, tblQry->data); if (!res) die_horribly(AH, modulename, "could not find oid columns of table \"%s\": %s", - tablename, PQerrorMessage(AH->connection)); + te->name, PQerrorMessage(AH->connection)); if ((n = PQntuples(res)) == 0) { /* nothing to do */ - ahlog(AH, 1, "no OID type columns in table %s\n", tablename); + ahlog(AH, 1, "no OID type columns in table %s\n", te->name); } for (i = 0; i < n; i++) { attr = PQgetvalue(res, i, 0); - ahlog(AH, 1, "fixing large object cross-references for %s.%s\n", tablename, attr); + ahlog(AH, 1, "fixing large object cross-references for %s.%s\n", + te->name, attr); resetPQExpBuffer(tblQry); - /* - * We should use coalesce here (rather than 'exists'), but it - * seems to be broken in 7.0.2 (weird optimizer strategy) - */ - appendPQExpBuffer(tblQry, "UPDATE \"%s\" SET \"%s\" = ", tablename, attr); - appendPQExpBuffer(tblQry, " (SELECT x.newOid FROM \"%s\" x WHERE x.oldOid = \"%s\".\"%s\")", - BLOB_XREF_TABLE, tablename, attr); - appendPQExpBuffer(tblQry, " where exists" - "(select * from %s x where x.oldOid = \"%s\".\"%s\");", - BLOB_XREF_TABLE, tablename, attr); + /* Can't use fmtId twice in one call... */ + appendPQExpBuffer(tblQry, + "UPDATE %s SET %s = %s.newOid", + tblName->data, fmtId(attr, false), + BLOB_XREF_TABLE); + appendPQExpBuffer(tblQry, + " FROM %s WHERE %s.oldOid = %s.%s", + BLOB_XREF_TABLE, + BLOB_XREF_TABLE, + tblName->data, fmtId(attr, false)); ahlog(AH, 10, "SQL: %s\n", tblQry->data); @@ -829,17 +770,18 @@ FixupBlobRefs(ArchiveHandle *AH, char *tablename) if (!uRes) die_horribly(AH, modulename, "could not update column \"%s\" of table \"%s\": %s", - attr, tablename, PQerrorMessage(AH->blobConnection)); + attr, te->name, PQerrorMessage(AH->blobConnection)); if (PQresultStatus(uRes) != PGRES_COMMAND_OK) die_horribly(AH, modulename, "error while updating column \"%s\" of table \"%s\": %s", - attr, tablename, PQerrorMessage(AH->blobConnection)); + attr, te->name, PQerrorMessage(AH->blobConnection)); PQclear(uRes); } PQclear(res); + destroyPQExpBuffer(tblName); destroyPQExpBuffer(tblQry); } diff --git a/src/bin/pg_dump/pg_backup_db.h b/src/bin/pg_dump/pg_backup_db.h index 463526922d..d4baa55538 100644 --- a/src/bin/pg_dump/pg_backup_db.h +++ b/src/bin/pg_dump/pg_backup_db.h @@ -2,12 +2,12 @@ * Definitions for pg_backup_db.c * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_db.h,v 1.5 2001/06/27 21:21:37 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_db.h,v 1.6 2002/05/10 22:36:26 tgl Exp $ */ -#define BLOB_XREF_TABLE "dump_blob_xref" /* MUST be lower case */ +#define BLOB_XREF_TABLE "pg_dump_blob_xref" /* MUST be lower case */ -extern void FixupBlobRefs(ArchiveHandle *AH, char *tablename); +extern void FixupBlobRefs(ArchiveHandle *AH, TocEntry *te); extern int ExecuteSqlCommand(ArchiveHandle *AH, PQExpBuffer qry, char *desc, bool use_blob); extern int ExecuteSqlCommandBuf(ArchiveHandle *AH, void *qry, int bufLen); diff --git a/src/bin/pg_dump/pg_backup_null.c b/src/bin/pg_dump/pg_backup_null.c index af8f52a7c2..beb6ed12b6 100644 --- a/src/bin/pg_dump/pg_backup_null.c +++ b/src/bin/pg_dump/pg_backup_null.c @@ -3,7 +3,7 @@ * pg_backup_null.c * * Implementation of an archive that is never saved; it is used by - * pg_dump to output output a plain text SQL script instead of save + * pg_dump to output a plain text SQL script instead of save * a real archive. * * See the headers to pg_restore for more details. @@ -17,7 +17,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_null.c,v 1.8 2002/04/24 02:21:04 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_null.c,v 1.9 2002/05/10 22:36:26 tgl Exp $ * * Modifications - 09-Jul-2000 - pjw@rhyme.com.au * diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c index efd8a03013..783b8a3136 100644 --- a/src/bin/pg_dump/pg_backup_tar.c +++ b/src/bin/pg_dump/pg_backup_tar.c @@ -16,7 +16,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_tar.c,v 1.21 2002/04/24 02:21:04 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_tar.c,v 1.22 2002/05/10 22:36:26 tgl Exp $ * * Modifications - 28-Jun-2000 - pjw@rhyme.com.au * @@ -835,7 +835,7 @@ _CloseArchive(ArchiveHandle *AH) ropt = NewRestoreOptions(); ropt->dropSchema = 1; ropt->compression = 0; - ropt->superuser = PQuser(AH->connection); + ropt->superuser = NULL; ropt->suppressDumpWarnings = true; savVerbose = AH->public.verbose; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 20c847ea63..c3f89cb85d 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.258 2002/05/06 18:33:45 tgl Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.259 2002/05/10 22:36:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -64,6 +64,7 @@ #include "pg_backup.h" #include "pg_backup_archiver.h" + typedef enum _formatLiteralOptions { CONV_ALL = 0, @@ -74,31 +75,55 @@ typedef enum _formatLiteralOptions /* only checks for 'opts == CONV_ALL' anyway. */ } formatLiteralOptions; -static void dumpComment(Archive *fout, const char *target, const char *oid, - const char *classname, int subid, - const char *((*deps)[])); +typedef struct _dumpContext +{ + TableInfo *tblinfo; + int tblidx; + bool oids; +} DumpContext; + +static void help(const char *progname); +static int parse_version(const char *versionString); +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 dumpSequence(Archive *fout, TableInfo tbinfo, const bool schemaOnly, const bool dataOnly); +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 dumpACL(Archive *fout, TableInfo *tbinfo); -static void dumpTriggers(Archive *fout, const char *tablename, - TableInfo *tblinfo, int numTables); -static void dumpRules(Archive *fout, const char *tablename, - TableInfo *tblinfo, int numTables); -static void formatStringLiteral(PQExpBuffer buf, const char *str, const formatLiteralOptions opts); -static void clearTableInfo(TableInfo *, int); -static void dumpOneFunc(Archive *fout, FuncInfo *finfo, int i, - TypeInfo *tinfo, int numTypes); +static void dumpTriggers(Archive *fout, TableInfo *tblinfo, int numTables); +static void dumpRules(Archive *fout, TableInfo *tblinfo, int numTables); +static void formatStringLiteral(PQExpBuffer buf, const char *str, + const formatLiteralOptions opts); +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 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 void AddAcl(char *aclbuf, const char *keyword); static char *GetPrivileges(Archive *AH, const char *s); static int dumpBlobs(Archive *AH, char *, void *); static int dumpDatabase(Archive *AH); -static PQExpBuffer getPKconstraint(TableInfo *tblInfo, IndInfo *indInfo); static const char *getAttrName(int attrnum, TableInfo *tblInfo); extern char *optarg; @@ -108,10 +133,10 @@ extern int optind, /* global decls */ bool g_verbose; /* User wants verbose narration of our * activities. */ -Oid g_last_builtin_oid; /* value of the last builtin oid */ Archive *g_fout; /* the script file */ PGconn *g_conn; /* the database connection */ +/* various user-settable parameters */ bool force_quotes; /* User wants to suppress double-quotes */ bool dumpData; /* dump data using proper insert strings */ bool attrNames; /* put attr names into insert strings */ @@ -119,19 +144,519 @@ bool schemaOnly; bool dataOnly; bool aclsSkip; +/* 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 */ + char g_opaque_type[10]; /* name for the opaque type */ /* placeholders for the delimiters for comments */ char g_comment_start[10]; char g_comment_end[10]; +/* these are to avoid passing around info for findNamespace() */ +static NamespaceInfo *g_namespaces; +static int g_numNamespaces; -typedef struct _dumpContext + +int +main(int argc, char **argv) { + int c; + const char *filename = NULL; + const char *format = "p"; + const char *dbname = NULL; + const char *pghost = NULL; + const char *pgport = NULL; + const char *username = NULL; + bool oids = false; TableInfo *tblinfo; - int tblidx; - bool oids; -} DumpContext; + int numTables; + bool force_password = false; + int compressLevel = -1; + bool ignore_version = false; + int plainText = 0; + int outputClean = 0; + int outputCreate = 0; + int outputBlobs = 0; + int outputNoOwner = 0; + int outputNoReconnect = 0; + static int use_setsessauth = 0; + static int disable_triggers = 0; + char *outputSuperuser = NULL; + + RestoreOptions *ropt; + +#ifdef HAVE_GETOPT_LONG + static struct option long_options[] = { + {"data-only", no_argument, NULL, 'a'}, + {"blobs", no_argument, NULL, 'b'}, + {"clean", no_argument, NULL, 'c'}, + {"create", no_argument, NULL, 'C'}, + {"file", required_argument, NULL, 'f'}, + {"format", required_argument, NULL, 'F'}, + {"inserts", no_argument, NULL, 'd'}, + {"attribute-inserts", no_argument, NULL, 'D'}, + {"column-inserts", no_argument, NULL, 'D'}, + {"host", required_argument, NULL, 'h'}, + {"ignore-version", no_argument, NULL, 'i'}, + {"no-reconnect", no_argument, NULL, 'R'}, + {"no-quotes", no_argument, NULL, 'n'}, + {"quotes", no_argument, NULL, 'N'}, + {"oids", no_argument, NULL, 'o'}, + {"no-owner", no_argument, NULL, 'O'}, + {"port", required_argument, NULL, 'p'}, + {"schema-only", no_argument, NULL, 's'}, + {"superuser", required_argument, NULL, 'S'}, + {"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'}, + {"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' + */ + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, + {"disable-triggers", no_argument, &disable_triggers, 1}, + + {NULL, 0, NULL, 0} + }; + int optindex; +#endif + +#ifdef ENABLE_NLS + setlocale(LC_ALL, ""); + bindtextdomain("pg_dump", LOCALEDIR); + textdomain("pg_dump"); +#endif + + g_verbose = false; + force_quotes = true; + + strcpy(g_comment_start, "-- "); + g_comment_end[0] = '\0'; + strcpy(g_opaque_type, "opaque"); + + dataOnly = schemaOnly = dumpData = attrNames = false; + + if (!strrchr(argv[0], '/')) + progname = argv[0]; + else + progname = strrchr(argv[0], '/') + 1; + + /* Set default options based on progname */ + if (strcmp(progname, "pg_backup") == 0) + { + format = "c"; + outputBlobs = true; + } + + if (argc > 1) + { + if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) + { + help(progname); + exit(0); + } + if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) + { + puts("pg_dump (PostgreSQL) " PG_VERSION); + exit(0); + } + } + +#ifdef HAVE_GETOPT_LONG + while ((c = getopt_long(argc, argv, "abcCdDf:F:h:inNoOp:RsS:t:uU:vWxX:zZ:V?", long_options, &optindex)) != -1) +#else + while ((c = getopt(argc, argv, "abcCdDf:F:h:inNoOp:RsS:t:uU:vWxX:zZ:V?-")) != -1) +#endif + + { + switch (c) + { + case 'a': /* Dump data only */ + dataOnly = true; + break; + + case 'b': /* Dump blobs */ + outputBlobs = true; + break; + + case 'c': /* clean (i.e., drop) schema prior to + * create */ + outputClean = 1; + break; + + case 'C': /* Create DB */ + + outputCreate = 1; + break; + + case 'd': /* dump data as proper insert strings */ + dumpData = true; + break; + + case 'D': /* dump data as proper insert strings with + * attr names */ + dumpData = true; + attrNames = true; + break; + + case 'f': + filename = optarg; + break; + + case 'F': + format = optarg; + break; + + case 'h': /* server host */ + pghost = optarg; + break; + + case 'i': /* ignore database version mismatch */ + ignore_version = true; + break; + + case 'n': /* Do not force double-quotes on + * identifiers */ + force_quotes = false; + break; + + case 'N': /* Force double-quotes on identifiers */ + force_quotes = true; + break; + + case 'o': /* Dump oids */ + oids = true; + break; + + + case 'O': /* Don't reconnect to match owner */ + outputNoOwner = 1; + break; + + case 'p': /* server port */ + pgport = optarg; + break; + + case 'R': /* No reconnect */ + outputNoReconnect = 1; + break; + + case 's': /* dump schema only */ + schemaOnly = true; + break; + + case 'S': /* Username for superuser in plain text + * output */ + outputSuperuser = strdup(optarg); + break; + + case 't': /* Dump data for this table only */ + { + int i; + + selectTablename = strdup(optarg); + + /* + * quoted string? Then strip quotes and preserve + * case... + */ + if (selectTablename[0] == '"') + { + char *endptr; + + endptr = selectTablename + strlen(selectTablename) - 1; + if (*endptr == '"') + *endptr = '\0'; + strcpy(selectTablename, &selectTablename[1]); + } + else + { + /* otherwise, convert table name to lowercase... */ + for (i = 0; selectTablename[i]; i++) + if (isupper((unsigned char) selectTablename[i])) + selectTablename[i] = tolower((unsigned char) selectTablename[i]); + + /* + * '*' is a special case meaning ALL tables, but + * only if unquoted + */ + if (strcmp(selectTablename, "*") == 0) + selectTablename[0] = '\0'; + } + } + break; + + case 'u': + force_password = true; + username = simple_prompt("User name: ", 100, true); + break; + + case 'U': + username = optarg; + break; + + case 'v': /* verbose */ + g_verbose = true; + break; + + case 'W': + force_password = true; + break; + + case 'x': /* skip ACL dump */ + aclsSkip = 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'. + */ + case 'X': + if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; + else if (strcmp(optarg, "disable-triggers") == 0) + disable_triggers = 1; + else + { + fprintf(stderr, + gettext("%s: invalid -X option -- %s\n"), + progname, optarg); + fprintf(stderr, gettext("Try '%s --help' for more information.\n"), progname); + exit(1); + } + break; + case 'Z': /* Compression Level */ + compressLevel = atoi(optarg); + break; + +#ifndef HAVE_GETOPT_LONG + case '-': + fprintf(stderr, + gettext("%s was compiled without support for long options.\n" + "Use --help for help on invocation options.\n"), + progname); + exit(1); + break; +#else + /* This covers the long options equivalent to -X xxx. */ + case 0: + break; +#endif + default: + fprintf(stderr, gettext("Try '%s --help' for more information.\n"), progname); + exit(1); + } + } + + if (optind < (argc - 1)) + { + fprintf(stderr, + gettext("%s: too many command line options (first is '%s')\n" + "Try '%s --help' for more information.\n"), + progname, argv[optind + 1], progname); + exit(1); + } + + /* Get the target database name */ + if (optind < argc) + dbname = argv[optind]; + else + dbname = getenv("PGDATABASE"); + if (!dbname) + { + write_msg(NULL, "no database name specified\n"); + exit(1); + } + + if (dataOnly && schemaOnly) + { + write_msg(NULL, "The options \"schema only\" (-s) and \"data only\" (-a) cannot be used together.\n"); + exit(1); + } + + if (outputBlobs && selectTablename != NULL && strlen(selectTablename) > 0) + { + write_msg(NULL, "Large object output is not supported for a single table.\n"); + write_msg(NULL, "Use all tables or a full dump instead.\n"); + exit(1); + } + + if (dumpData == 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]) + { + + case 'c': + case 'C': + g_fout = CreateArchive(filename, archCustom, compressLevel); + break; + + case 'f': + case 'F': + g_fout = CreateArchive(filename, archFiles, compressLevel); + break; + + case 'p': + case 'P': + plainText = 1; + g_fout = CreateArchive(filename, archNull, 0); + break; + + case 't': + case 'T': + g_fout = CreateArchive(filename, archTar, compressLevel); + break; + + default: + write_msg(NULL, "invalid output format '%s' specified\n", format); + exit(1); + } + + if (g_fout == NULL) + { + write_msg(NULL, "could not open output file %s for writing\n", filename); + exit(1); + } + + /* Let the archiver know how noisy to be */ + g_fout->verbose = g_verbose; + + /* + * Open the database using the Archiver, so it knows about it. Errors + * mean death. + */ + g_fout->minRemoteVersion = 70000; /* we can handle back to 7.0 */ + g_fout->maxRemoteVersion = parse_version(PG_VERSION); + g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport, username, force_password, ignore_version); + + /* + * Start serializable transaction to dump consistent data + */ + { + PGresult *res; + + 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); + 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); + } + + if (g_fout->remoteVersion < 70300) + { + if (g_fout->remoteVersion >= 70100) + g_last_builtin_oid = findLastBuiltinOid_V71(dbname); + else + g_last_builtin_oid = findLastBuiltinOid_V70(); + if (g_verbose) + 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); + + tblinfo = dumpSchema(g_fout, &numTables, aclsSkip, schemaOnly, dataOnly); + + if (!schemaOnly) + dumpClasses(tblinfo, numTables, g_fout, oids); + + if (outputBlobs) + ArchiveEntry(g_fout, "0", "BLOBS", NULL, "", + "BLOBS", NULL, "", "", NULL, dumpBlobs, NULL); + + if (!dataOnly) /* dump indexes and triggers at the end + * for performance */ + { + dumpTriggers(g_fout, tblinfo, numTables); + dumpRules(g_fout, tblinfo, numTables); + } + + /* Now sort the output nicely */ + SortTocByOID(g_fout); + MoveToStart(g_fout, "SCHEMA"); + MoveToStart(g_fout, "DATABASE"); + MoveToEnd(g_fout, "TABLE DATA"); + MoveToEnd(g_fout, "BLOBS"); + MoveToEnd(g_fout, "INDEX"); + MoveToEnd(g_fout, "CONSTRAINT"); + MoveToEnd(g_fout, "TRIGGER"); + MoveToEnd(g_fout, "RULE"); + MoveToEnd(g_fout, "SEQUENCE SET"); + + /* + * Moving all comments to end is annoying, but must do it for comments + * on stuff we just moved, and we don't seem to have quite enough + * dependency structure to get it really right... + */ + MoveToEnd(g_fout, "COMMENT"); + + if (plainText) + { + ropt = NewRestoreOptions(); + ropt->filename = (char *) filename; + ropt->dropSchema = outputClean; + ropt->aclsSkip = aclsSkip; + ropt->superuser = outputSuperuser; + ropt->create = outputCreate; + ropt->noOwner = outputNoOwner; + ropt->noReconnect = outputNoReconnect; + ropt->use_setsessauth = use_setsessauth; + ropt->disable_triggers = disable_triggers; + + if (compressLevel == -1) + ropt->compression = 0; + else + ropt->compression = compressLevel; + + ropt->suppressDumpWarnings = true; /* We've already shown + * them */ + + RestoreArchive(g_fout, ropt); + } + + CloseArchive(g_fout); + + PQfinish(g_conn); + exit(0); +} + static void help(const char *progname) @@ -173,6 +698,8 @@ help(const char *progname) " -X use-set-session-authorization, --use-set-session-authorization\n" " output SET SESSION AUTHORIZATION commands rather\n" " than \\connect commands\n" + " -X disable-triggers, --disable-triggers\n" + " disable triggers during data-only restore\n" " -Z, --compress {0-9} compression level for compressed formats\n" )); #else @@ -207,6 +734,7 @@ help(const char *progname) " -X use-set-session-authorization\n" " output SET SESSION AUTHORIZATION commands rather\n" " than \\connect commands\n" + " -X disable-triggers disable triggers during data-only restore\n" " -Z {0-9} compression level for compressed formats\n" )); #endif @@ -215,6 +743,27 @@ help(const char *progname) "Report bugs to .")); } +static int +parse_version(const char *versionString) +{ + int cnt; + int vmaj, + vmin, + vrev; + + cnt = sscanf(versionString, "%d.%d.%d", &vmaj, &vmin, &vrev); + + if (cnt < 2) + { + write_msg(NULL, "unable to parse version string \"%s\"\n", versionString); + exit(1); + } + + if (cnt == 2) + vrev = 0; + + return (100 * vmaj + vmin) * 100 + vrev; +} void exit_nicely(void) @@ -225,8 +774,44 @@ exit_nicely(void) exit(1); } +/* + * selectDumpableNamespace: policy-setting subroutine + * Mark a namespace as to be dumped or not + */ +static void +selectDumpableNamespace(NamespaceInfo *nsinfo) +{ + /* + * If a specific table is being dumped, do not dump any complete + * namespaces. Otherwise, dump all non-system namespaces. + */ + if (selectTablename != NULL) + nsinfo->dump = false; + else if (strncmp(nsinfo->nspname, "pg_", 3) == 0) + nsinfo->dump = false; + else + nsinfo->dump = true; +} -#define COPYBUFSIZ 8192 +/* + * selectDumpableTable: policy-setting subroutine + * Mark a table as to be dumped or not + */ +static void +selectDumpableTable(TableInfo *tbinfo) +{ + /* + * Always dump if dumping parent namespace; else, if a particular + * tablename has been specified, dump matching table name; else, + * do not dump. + */ + if (tbinfo->relnamespace->dump) + tbinfo->dump = true; + else if (selectTablename != NULL) + tbinfo->dump = (strcmp(tbinfo->relname, selectTablename) == 0); + else + tbinfo->dump = false; +} /* * Dump a table's contents for loading using the COPY command @@ -234,16 +819,18 @@ exit_nicely(void) * to be dumped. */ +#define COPYBUFSIZ 8192 + static int dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv) { const DumpContext *dctx = (DumpContext *) dctxv; - const char *classname = dctx->tblinfo[dctx->tblidx].relname; - const bool hasoids = dctx->tblinfo[dctx->tblidx].hasoids; + TableInfo *tbinfo = &dctx->tblinfo[dctx->tblidx]; + const char *classname = tbinfo->relname; + const bool hasoids = tbinfo->hasoids; const bool oids = dctx->oids; - + PQExpBuffer q = createPQExpBuffer(); PGresult *res; - char query[255]; int ret; bool copydone; char copybuf[COPYBUFSIZ]; @@ -251,140 +838,132 @@ dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv) if (g_verbose) write_msg(NULL, "dumping out the contents of table %s\n", classname); + /* + * 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. + */ + selectSourceSchema(tbinfo->relnamespace->nspname); + if (oids && hasoids) { - /* - * archprintf(fout, "COPY %s WITH OIDS FROM stdin;\n", - * fmtId(classname, force_quotes)); - * - * - Not used as of V1.3 (needs to be in ArchiveEntry call) - * - */ - - sprintf(query, "COPY %s WITH OIDS TO stdout;", - fmtId(classname, force_quotes)); + appendPQExpBuffer(q, "COPY %s WITH OIDS TO stdout;", + fmtQualifiedId(tbinfo->relnamespace->nspname, + classname)); } else { - /* - * archprintf(fout, "COPY %s FROM stdin;\n", fmtId(classname, - * force_quotes)); - * - * - Not used as of V1.3 (needs to be in ArchiveEntry call) - * - */ - - sprintf(query, "COPY %s TO stdout;", fmtId(classname, force_quotes)); + appendPQExpBuffer(q, "COPY %s TO stdout;", + fmtQualifiedId(tbinfo->relnamespace->nspname, + classname)); } - res = PQexec(g_conn, query); + 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", query); + write_msg(NULL, "The command was: %s\n", q->data); exit_nicely(); } - else + if (PQresultStatus(res) != PGRES_COPY_OUT) { - 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; + + while (!copydone) + { + ret = PQgetline(g_conn, copybuf, COPYBUFSIZ); + + if (copybuf[0] == '\\' && + copybuf[1] == '.' && + copybuf[2] == '\0') { - 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", query); - exit_nicely(); + copydone = true; /* don't print this... */ } else { - copydone = false; - - while (!copydone) + archputs(copybuf, fout); + switch (ret) { - ret = PQgetline(g_conn, copybuf, COPYBUFSIZ); - - if (copybuf[0] == '\\' && - copybuf[1] == '.' && - copybuf[2] == '\0') - { - copydone = true; /* don't print this... */ - } - else - { - archputs(copybuf, fout); - switch (ret) - { - case EOF: - copydone = true; - /* FALLTHROUGH */ - case 0: - archputc('\n', fout); - break; - case 1: - break; - } - } - - /* - * 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. - * - * 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: - * - * 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 - * - * 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. - * - * 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. - * - * select(0, NULL, NULL, NULL, &tvi); - * - * This will return after the interval specified in the - * structure tvi. Fianally, call gettimeofday again to - * save the 'last sleep time'. - */ + case EOF: + copydone = true; + /* FALLTHROUGH */ + case 0: + archputc('\n', fout); + break; + case 1: + break; } - archprintf(fout, "\\.\n"); - } - ret = PQendcopy(g_conn); - if (ret != 0) - { - write_msg(NULL, "SQL command to dump the contents of table \"%s\" failed: PQendcopy() failed.\n", classname); - write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn)); - write_msg(NULL, "The command was: %s\n", query); - PQclear(res); - exit_nicely(); } + + /* + * 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. + * + * 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: + * + * 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 + * + * 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. + * + * 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. + * + * select(0, NULL, NULL, NULL, &tvi); + * + * This will return after the interval specified in the + * structure tvi. Fianally, call gettimeofday again to + * save the 'last sleep time'. + */ + } + archprintf(fout, "\\.\n"); + + ret = PQendcopy(g_conn); + if (ret != 0) + { + write_msg(NULL, "SQL command to dump the contents of table \"%s\" failed: PQendcopy() failed.\n", classname); + write_msg(NULL, "Error message from server: %s", PQerrorMessage(g_conn)); + write_msg(NULL, "The command was: %s\n", q->data); + PQclear(res); + exit_nicely(); } + destroyPQExpBuffer(q); return 1; } @@ -392,17 +971,35 @@ static int dumpClasses_dumpData(Archive *fout, char *oid, void *dctxv) { const DumpContext *dctx = (DumpContext *) dctxv; - const char *classname = dctx->tblinfo[dctx->tblidx].relname; - - PGresult *res; + TableInfo *tbinfo = &dctx->tblinfo[dctx->tblidx]; + const char *classname = tbinfo->relname; PQExpBuffer q = createPQExpBuffer(); + PGresult *res; int tuple; int field; + /* + * 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. + */ + selectSourceSchema(tbinfo->relnamespace->nspname); + if (fout->remoteVersion >= 70100) - appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT * FROM ONLY %s", fmtId(classname, force_quotes)); + { + appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " + "SELECT * FROM ONLY %s", + fmtQualifiedId(tbinfo->relnamespace->nspname, + classname)); + } else - appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT * FROM %s", fmtId(classname, force_quotes)); + { + appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " + "SELECT * FROM %s", + fmtQualifiedId(tbinfo->relnamespace->nspname, + classname)); + } res = PQexec(g_conn, q->data); if (!res || @@ -550,7 +1147,7 @@ formatStringLiteral(PQExpBuffer buf, const char *str, const formatLiteralOptions */ static void dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout, - const char *onlytable, const bool oids, const bool force_quotes) + const bool oids) { int i; DataDumperPtr dumpFn; @@ -558,33 +1155,22 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout, char copyBuf[512]; char *copyStmt; - if (g_verbose) - { - if (onlytable == NULL || (strlen(onlytable) == 0)) - write_msg(NULL, "preparing to dump the contents of all %d tables/sequences\n", numTables); - else - write_msg(NULL, "preparing to dump the contents of only one table/sequence\n"); - } - for (i = 0; i < numTables; i++) { const char *classname = tblinfo[i].relname; /* Skip VIEW relations */ - if (tblinfo[i].viewdef != NULL) + if (tblinfo[i].relkind == RELKIND_VIEW) continue; if (tblinfo[i].relkind == RELKIND_SEQUENCE) /* already dumped */ continue; - if (!onlytable || (strcmp(classname, onlytable) == 0) || (strlen(onlytable) == 0)) + if (tblinfo[i].dump) { if (g_verbose) - write_msg(NULL, "preparing to dump the contents of table %s\n", classname); - -#if 0 - becomeUser(fout, tblinfo[i].usename); -#endif + write_msg(NULL, "preparing to dump the contents of table %s\n", + classname); dumpCtx = (DumpContext *) malloc(sizeof(DumpContext)); dumpCtx->tblinfo = (TableInfo *) tblinfo; @@ -608,533 +1194,17 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout, } ArchiveEntry(fout, tblinfo[i].oid, tblinfo[i].relname, - "TABLE DATA", NULL, "", "", - copyStmt, tblinfo[i].usename, + tblinfo[i].relnamespace->nspname, tblinfo[i].usename, + "TABLE DATA", NULL, "", "", copyStmt, dumpFn, dumpCtx); } } } - -static int -parse_version(const char *versionString) -{ - int cnt; - int vmaj, - vmin, - vrev; - - cnt = sscanf(versionString, "%d.%d.%d", &vmaj, &vmin, &vrev); - - if (cnt < 2) - { - write_msg(NULL, "unable to parse version string \"%s\"\n", versionString); - exit(1); - } - - if (cnt == 2) - vrev = 0; - - return (100 * vmaj + vmin) * 100 + vrev; -} - - - -int -main(int argc, char **argv) -{ - int c; - const char *filename = NULL; - const char *format = "p"; - const char *dbname = NULL; - const char *pghost = NULL; - const char *pgport = NULL; - const char *username = NULL; - char *tablename = NULL; - bool oids = false; - TableInfo *tblinfo; - int numTables; - bool force_password = false; - int compressLevel = -1; - bool ignore_version = false; - int plainText = 0; - int outputClean = 0; - int outputCreate = 0; - int outputBlobs = 0; - int outputNoOwner = 0; - int outputNoReconnect = 0; - static int use_setsessauth = 0; - char *outputSuperuser = NULL; - - RestoreOptions *ropt; - -#ifdef HAVE_GETOPT_LONG - static struct option long_options[] = { - {"data-only", no_argument, NULL, 'a'}, - {"blobs", no_argument, NULL, 'b'}, - {"clean", no_argument, NULL, 'c'}, - {"create", no_argument, NULL, 'C'}, - {"file", required_argument, NULL, 'f'}, - {"format", required_argument, NULL, 'F'}, - {"inserts", no_argument, NULL, 'd'}, - {"attribute-inserts", no_argument, NULL, 'D'}, - {"column-inserts", no_argument, NULL, 'D'}, - {"host", required_argument, NULL, 'h'}, - {"ignore-version", no_argument, NULL, 'i'}, - {"no-reconnect", no_argument, NULL, 'R'}, - {"no-quotes", no_argument, NULL, 'n'}, - {"quotes", no_argument, NULL, 'N'}, - {"oids", no_argument, NULL, 'o'}, - {"no-owner", no_argument, NULL, 'O'}, - {"port", required_argument, NULL, 'p'}, - {"schema-only", no_argument, NULL, 's'}, - {"superuser", required_argument, NULL, 'S'}, - {"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'}, - {"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' - */ - {"use-set-session-authorization", no_argument, &use_setsessauth, 1} - }; - int optindex; -#endif - -#ifdef ENABLE_NLS - setlocale(LC_ALL, ""); - bindtextdomain("pg_dump", LOCALEDIR); - textdomain("pg_dump"); -#endif - - g_verbose = false; - force_quotes = true; - - strcpy(g_comment_start, "-- "); - g_comment_end[0] = '\0'; - strcpy(g_opaque_type, "opaque"); - - dataOnly = schemaOnly = dumpData = attrNames = false; - - if (!strrchr(argv[0], '/')) - progname = argv[0]; - else - progname = strrchr(argv[0], '/') + 1; - - /* Set default options based on progname */ - if (strcmp(progname, "pg_backup") == 0) - { - format = "c"; - outputBlobs = true; - } - - if (argc > 1) - { - if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) - { - help(progname); - exit(0); - } - if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) - { - puts("pg_dump (PostgreSQL) " PG_VERSION); - exit(0); - } - } - -#ifdef HAVE_GETOPT_LONG - while ((c = getopt_long(argc, argv, "abcCdDf:F:h:inNoOp:RsS:t:uU:vWxX:zZ:V?", long_options, &optindex)) != -1) -#else - while ((c = getopt(argc, argv, "abcCdDf:F:h:inNoOp:RsS:t:uU:vWxX:zZ:V?-")) != -1) -#endif - - { - switch (c) - { - case 'a': /* Dump data only */ - dataOnly = true; - break; - - case 'b': /* Dump blobs */ - outputBlobs = true; - break; - - case 'c': /* clean (i.e., drop) schema prior to - * create */ - outputClean = 1; - break; - - case 'C': /* Create DB */ - - outputCreate = 1; - break; - - case 'd': /* dump data as proper insert strings */ - dumpData = true; - break; - - case 'D': /* dump data as proper insert strings with - * attr names */ - dumpData = true; - attrNames = true; - break; - - case 'f': - filename = optarg; - break; - - case 'F': - format = optarg; - break; - - case 'h': /* server host */ - pghost = optarg; - break; - - case 'i': /* ignore database version mismatch */ - ignore_version = true; - break; - - case 'n': /* Do not force double-quotes on - * identifiers */ - force_quotes = false; - break; - - case 'N': /* Force double-quotes on identifiers */ - force_quotes = true; - break; - - case 'o': /* Dump oids */ - oids = true; - break; - - - case 'O': /* Don't reconnect to match owner */ - outputNoOwner = 1; - break; - - case 'p': /* server port */ - pgport = optarg; - break; - - case 'R': /* No reconnect */ - outputNoReconnect = 1; - break; - - case 's': /* dump schema only */ - schemaOnly = true; - break; - - case 'S': /* Username for superuser in plain text - * output */ - outputSuperuser = strdup(optarg); - break; - - case 't': /* Dump data for this table only */ - { - int i; - - tablename = strdup(optarg); - - /* - * quoted string? Then strip quotes and preserve - * case... - */ - if (tablename[0] == '"') - { - strcpy(tablename, &tablename[1]); - if (*(tablename + strlen(tablename) - 1) == '"') - *(tablename + strlen(tablename) - 1) = '\0'; - } - /* otherwise, convert table name to lowercase... */ - else - { - for (i = 0; tablename[i]; i++) - if (isupper((unsigned char) tablename[i])) - tablename[i] = tolower((unsigned char) tablename[i]); - - /* - * '*' is a special case meaning ALL tables, but - * only if unquoted - */ - if (strcmp(tablename, "*") == 0) - tablename[0] = '\0'; - - } - } - break; - - case 'u': - force_password = true; - username = simple_prompt("User name: ", 100, true); - break; - - case 'U': - username = optarg; - break; - - case 'v': /* verbose */ - g_verbose = true; - break; - - case 'W': - force_password = true; - break; - - case 'x': /* skip ACL dump */ - aclsSkip = 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'. - */ - case 'X': - if (strcmp(optarg, "use-set-session-authorization") == 0) - use_setsessauth = 1; - else - { - fprintf(stderr, - gettext("%s: invalid -X option -- %s\n"), - progname, optarg); - fprintf(stderr, gettext("Try '%s --help' for more information.\n"), progname); - exit(1); - } - break; - case 'Z': /* Compression Level */ - compressLevel = atoi(optarg); - break; - -#ifndef HAVE_GETOPT_LONG - case '-': - fprintf(stderr, - gettext("%s was compiled without support for long options.\n" - "Use --help for help on invocation options.\n"), - progname); - exit(1); - break; -#else - /* This covers the long options equivalent to -X xxx. */ - case 0: - break; -#endif - default: - fprintf(stderr, gettext("Try '%s --help' for more information.\n"), progname); - exit(1); - } - } - - if (optind < (argc - 1)) - { - fprintf(stderr, - gettext("%s: too many command line options (first is '%s')\n" - "Try '%s --help' for more information.\n"), - progname, argv[optind + 1], progname); - exit(1); - } - - /* Get the target database name */ - if (optind < argc) - dbname = argv[optind]; - else - dbname = getenv("PGDATABASE"); - if (!dbname) - { - write_msg(NULL, "no database name specified\n"); - exit(1); - } - - if (dataOnly && schemaOnly) - { - write_msg(NULL, "The options \"schema only\" (-s) and \"data only\" (-a) cannot be used together.\n"); - exit(1); - } - - if (outputBlobs && tablename != NULL && strlen(tablename) > 0) - { - write_msg(NULL, "Large object output is not supported for a single table.\n"); - write_msg(NULL, "Use all tables or a full dump instead.\n"); - exit(1); - } - - if (dumpData == 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]) - { - - case 'c': - case 'C': - g_fout = CreateArchive(filename, archCustom, compressLevel); - break; - - case 'f': - case 'F': - g_fout = CreateArchive(filename, archFiles, compressLevel); - break; - - case 'p': - case 'P': - plainText = 1; - g_fout = CreateArchive(filename, archNull, 0); - break; - - case 't': - case 'T': - g_fout = CreateArchive(filename, archTar, compressLevel); - break; - - default: - write_msg(NULL, "invalid output format '%s' specified\n", format); - exit(1); - } - - if (g_fout == NULL) - { - write_msg(NULL, "could not open output file %s for writing\n", filename); - exit(1); - } - - /* Let the archiver know how noisy to be */ - g_fout->verbose = g_verbose; - - /* - * Open the database using the Archiver, so it knows about it. Errors - * mean death. - */ - g_fout->minRemoteVersion = 70000; /* we can handle back to 7.0 */ - g_fout->maxRemoteVersion = parse_version(PG_VERSION); - g_conn = ConnectDatabase(g_fout, dbname, pghost, pgport, username, force_password, ignore_version); - - /* - * Start serializable transaction to dump consistent data - */ - { - PGresult *res; - - 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); - 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); - } - - if (g_fout->remoteVersion >= 70100) - g_last_builtin_oid = findLastBuiltinOid_V71(dbname); - else - g_last_builtin_oid = findLastBuiltinOid_V70(); - - /* Dump the database definition */ - if (!dataOnly) - dumpDatabase(g_fout); - - if (oids == true) - setMaxOid(g_fout); - - if (g_verbose) - write_msg(NULL, "last built-in oid is %u\n", g_last_builtin_oid); - tblinfo = dumpSchema(g_fout, &numTables, tablename, aclsSkip, oids, schemaOnly, dataOnly); - - if (!schemaOnly) - dumpClasses(tblinfo, numTables, g_fout, tablename, oids, force_quotes); - - if (outputBlobs) - ArchiveEntry(g_fout, "0", "BLOBS", "BLOBS", NULL, "", "", "", "", dumpBlobs, 0); - - if (!dataOnly) /* dump indexes and triggers at the end - * for performance */ - { - dumpTriggers(g_fout, tablename, tblinfo, numTables); - dumpRules(g_fout, tablename, tblinfo, numTables); - } - - /* Now sort the output nicely */ - SortTocByOID(g_fout); - MoveToStart(g_fout, "DATABASE"); - MoveToEnd(g_fout, "TABLE DATA"); - MoveToEnd(g_fout, "BLOBS"); - MoveToEnd(g_fout, "INDEX"); - MoveToEnd(g_fout, "CONSTRAINT"); - MoveToEnd(g_fout, "TRIGGER"); - MoveToEnd(g_fout, "RULE"); - MoveToEnd(g_fout, "SEQUENCE SET"); - - /* - * Moving all comments to end is annoying, but must do it for comments - * on stuff we just moved, and we don't seem to have quite enough - * dependency structure to get it really right... - */ - MoveToEnd(g_fout, "COMMENT"); - - if (plainText) - { - ropt = NewRestoreOptions(); - ropt->filename = (char *) filename; - ropt->dropSchema = outputClean; - ropt->aclsSkip = aclsSkip; - ropt->superuser = outputSuperuser; - ropt->create = outputCreate; - ropt->noOwner = outputNoOwner; - ropt->noReconnect = outputNoReconnect; - ropt->use_setsessauth = use_setsessauth; - - if (outputSuperuser) - ropt->superuser = outputSuperuser; - else - ropt->superuser = PQuser(g_conn); - - if (compressLevel == -1) - ropt->compression = 0; - else - ropt->compression = compressLevel; - - ropt->suppressDumpWarnings = true; /* We've already shown - * them */ - - RestoreArchive(g_fout, ropt); - } - - CloseArchive(g_fout); - - clearTableInfo(tblinfo, numTables); - PQfinish(g_conn); - exit(0); -} - /* * dumpDatabase: * dump the database definition - * */ static int dumpDatabase(Archive *AH) @@ -1157,6 +1227,9 @@ dumpDatabase(Archive *AH) if (g_verbose) write_msg(NULL, "saving database definition\n"); + /* Make sure we are in proper schema */ + 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," " encoding, datpath from pg_database" @@ -1207,10 +1280,17 @@ dumpDatabase(Archive *AH) appendPQExpBuffer(delQry, "DROP DATABASE %s;\n", fmtId(datname, force_quotes)); - ArchiveEntry(AH, "0" /* OID */ , datname /* Name */ , "DATABASE", NULL, - creaQry->data /* Create */ , delQry->data /* Del */ , - "" /* Copy */ , dba /* Owner */ , - NULL /* Dumper */ , NULL /* Dumper Arg */ ); + ArchiveEntry(AH, "0", /* OID */ + datname, /* Name */ + NULL, /* Namespace */ + dba, /* Owner */ + "DATABASE", /* Desc */ + NULL, /* Deps */ + creaQry->data, /* Create */ + delQry->data, /* Del */ + NULL, /* Copy */ + NULL, /* Dumper */ + NULL); /* Dumper Arg */ PQclear(res); @@ -1246,6 +1326,9 @@ dumpBlobs(Archive *AH, char *junkOid, void *junkVal) 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 */ if (AH->remoteVersion >= 70100) appendPQExpBuffer(oidQry, "Declare blobOid Cursor for SELECT DISTINCT loid FROM pg_largeobject"); @@ -1318,13 +1401,153 @@ dumpBlobs(Archive *AH, char *junkOid, void *junkVal) return 1; } +/* + * getNamespaces: + * read all namespaces in the system catalogs and return them in the + * NamespaceInfo* structure + * + * numNamespaces is set to the number of namespaces read in + */ +NamespaceInfo * +getNamespaces(int *numNamespaces) +{ + PGresult *res; + int ntups; + int i; + PQExpBuffer query; + NamespaceInfo *nsinfo; + int i_oid; + int i_nspname; + int i_usename; + int i_nspacl; + + /* + * 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].nspacl = strdup(""); + + selectDumpableNamespace(&nsinfo[0]); + + nsinfo[1].oid = strdup("1"); + nsinfo[1].nspname = strdup("pg_catalog"); + nsinfo[1].usename = strdup(""); + nsinfo[1].nspacl = strdup(""); + + selectDumpableNamespace(&nsinfo[1]); + + g_namespaces = nsinfo; + g_numNamespaces = *numNamespaces = 2; + + return nsinfo; + } + + query = createPQExpBuffer(); + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + + /* + * 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"); + + res = PQexec(g_conn, query->data); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) + { + write_msg(NULL, "query to obtain list of namespaces failed: %s", PQerrorMessage(g_conn)); + exit_nicely(); + } + + ntups = PQntuples(res); + + nsinfo = (NamespaceInfo *) malloc(ntups * sizeof(NamespaceInfo)); + + i_oid = PQfnumber(res, "oid"); + i_nspname = PQfnumber(res, "nspname"); + i_usename = PQfnumber(res, "usename"); + 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].nspacl = strdup(PQgetvalue(res, i, i_nspacl)); + + /* Decide whether to dump this namespace */ + selectDumpableNamespace(&nsinfo[i]); + + if (strlen(nsinfo[i].usename) == 0) + write_msg(NULL, "WARNING: owner of namespace %s appears to be invalid\n", + nsinfo[i].nspname); + } + + PQclear(res); + destroyPQExpBuffer(query); + + g_namespaces = nsinfo; + g_numNamespaces = *numNamespaces = ntups; + + return nsinfo; +} + +/* + * findNamespace: + * given a namespace OID and an object OID, look up the info read by + * getNamespaces + * + * NB: for pre-7.3 source database, we use object OID to guess whether it's + * a system object or not. In 7.3 and later there is no guessing. + */ +static NamespaceInfo * +findNamespace(const char *nsoid, const char *objoid) +{ + int i; + + if (g_fout->remoteVersion >= 70300) + { + for (i = 0; i < g_numNamespaces; i++) + { + NamespaceInfo *nsinfo = &g_namespaces[i]; + + if (strcmp(nsoid, nsinfo->oid) == 0) + return nsinfo; + } + write_msg(NULL, "Failed to find namespace with OID %s.\n", nsoid); + exit_nicely(); + } + else + { + /* This code depends on the layout set up by getNamespaces. */ + if (atooid(objoid) > g_last_builtin_oid) + i = 0; /* user object */ + else + i = 1; /* system object */ + return &g_namespaces[i]; + } + + return NULL; /* keep compiler quiet */ +} + /* * getTypes: - * read all base types in the system catalogs and return them in the + * read all types in the system catalogs and return them in the * TypeInfo* structure * * numTypes is set to the number of types read in - * */ TypeInfo * getTypes(int *numTypes) @@ -1334,29 +1557,14 @@ getTypes(int *numTypes) int i; PQExpBuffer query = createPQExpBuffer(); TypeInfo *tinfo; - int i_oid; - int i_typowner; int i_typname; - int i_typlen; - int i_typprtlen; - int i_typinput; - int i_typoutput; - int i_typreceive; - int i_typsend; - int i_typelem; - int i_typdelim; - int i_typdefault; - int i_typrelid; - int i_typalign; - int i_typstorage; - int i_typbyval; - int i_typisdefined; + int i_typnamespace; int i_usename; - int i_typedefn; + int i_typelem; + int i_typrelid; int i_typtype; - - /* find all base types */ + int i_typisdefined; /* * we include even the built-in types because those may be used as @@ -1365,23 +1573,24 @@ getTypes(int *numTypes) * we filter out the built-in types when we dump out the types */ - if (g_fout->remoteVersion < 70100) + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + + if (g_fout->remoteVersion >= 70300) { - appendPQExpBuffer(query, "SELECT pg_type.oid, typowner, typname, typlen, typprtlen, " - "typinput, typoutput, typreceive, typsend, typelem, typdelim, " - "typdefault, typrelid, typalign, 'p'::char as typstorage, typbyval, typisdefined, " + appendPQExpBuffer(query, "SELECT pg_type.oid, typname, " + "typnamespace, " "(select usename from pg_user where typowner = usesysid) as usename, " - "typname as typedefn, typtype " - "from pg_type"); + "typelem, typrelid, typtype, typisdefined " + "FROM pg_type"); } else { - appendPQExpBuffer(query, "SELECT pg_type.oid, typowner, typname, typlen, typprtlen, " - "typinput, typoutput, typreceive, typsend, typelem, typdelim, " - "typdefault, typrelid, typalign, typstorage, typbyval, typisdefined, " + appendPQExpBuffer(query, "SELECT pg_type.oid, typname, " + "0::oid as typnamespace, " "(select usename from pg_user where typowner = usesysid) as usename, " - "format_type(pg_type.oid, NULL) as typedefn, typtype " - "from pg_type"); + "typelem, typrelid, typtype, typisdefined " + "FROM pg_type"); } res = PQexec(g_conn, query->data); @@ -1397,72 +1606,42 @@ getTypes(int *numTypes) tinfo = (TypeInfo *) malloc(ntups * sizeof(TypeInfo)); i_oid = PQfnumber(res, "oid"); - i_typowner = PQfnumber(res, "typowner"); i_typname = PQfnumber(res, "typname"); - i_typlen = PQfnumber(res, "typlen"); - i_typprtlen = PQfnumber(res, "typprtlen"); - i_typinput = PQfnumber(res, "typinput"); - i_typoutput = PQfnumber(res, "typoutput"); - i_typreceive = PQfnumber(res, "typreceive"); - i_typsend = PQfnumber(res, "typsend"); - i_typelem = PQfnumber(res, "typelem"); - i_typdelim = PQfnumber(res, "typdelim"); - i_typdefault = PQfnumber(res, "typdefault"); - i_typrelid = PQfnumber(res, "typrelid"); - i_typalign = PQfnumber(res, "typalign"); - i_typstorage = PQfnumber(res, "typstorage"); - i_typbyval = PQfnumber(res, "typbyval"); - i_typisdefined = PQfnumber(res, "typisdefined"); + i_typnamespace = PQfnumber(res, "typnamespace"); i_usename = PQfnumber(res, "usename"); - i_typedefn = PQfnumber(res, "typedefn"); + i_typelem = PQfnumber(res, "typelem"); + i_typrelid = PQfnumber(res, "typrelid"); i_typtype = PQfnumber(res, "typtype"); + i_typisdefined = PQfnumber(res, "typisdefined"); for (i = 0; i < ntups; i++) { tinfo[i].oid = strdup(PQgetvalue(res, i, i_oid)); - tinfo[i].typowner = strdup(PQgetvalue(res, i, i_typowner)); tinfo[i].typname = strdup(PQgetvalue(res, i, i_typname)); - tinfo[i].typlen = strdup(PQgetvalue(res, i, i_typlen)); - tinfo[i].typprtlen = strdup(PQgetvalue(res, i, i_typprtlen)); - tinfo[i].typinput = strdup(PQgetvalue(res, i, i_typinput)); - tinfo[i].typoutput = strdup(PQgetvalue(res, i, i_typoutput)); - tinfo[i].typreceive = strdup(PQgetvalue(res, i, i_typreceive)); - tinfo[i].typsend = strdup(PQgetvalue(res, i, i_typsend)); - tinfo[i].typelem = strdup(PQgetvalue(res, i, i_typelem)); - tinfo[i].typdelim = strdup(PQgetvalue(res, i, i_typdelim)); - if (PQgetisnull(res, i, i_typdefault)) - tinfo[i].typdefault = NULL; - else - tinfo[i].typdefault = strdup(PQgetvalue(res, i, i_typdefault)); - tinfo[i].typrelid = strdup(PQgetvalue(res, i, i_typrelid)); - tinfo[i].typalign = strdup(PQgetvalue(res, i, i_typalign)); - tinfo[i].typstorage = strdup(PQgetvalue(res, i, i_typstorage)); + tinfo[i].typnamespace = findNamespace(PQgetvalue(res, i, i_typnamespace), + tinfo[i].oid); tinfo[i].usename = strdup(PQgetvalue(res, i, i_usename)); - tinfo[i].typedefn = strdup(PQgetvalue(res, i, i_typedefn)); - tinfo[i].typtype = strdup(PQgetvalue(res, i, i_typtype)); + tinfo[i].typelem = strdup(PQgetvalue(res, i, i_typelem)); + tinfo[i].typrelid = strdup(PQgetvalue(res, i, i_typrelid)); + tinfo[i].typtype = *PQgetvalue(res, i, i_typtype); if (strlen(tinfo[i].usename) == 0) write_msg(NULL, "WARNING: owner of data type %s appears to be invalid\n", tinfo[i].typname); - if (strcmp(PQgetvalue(res, i, i_typbyval), "f") == 0) - tinfo[i].passedbyvalue = 0; - else - tinfo[i].passedbyvalue = 1; - /* * check for user-defined array types, omit system generated ones */ if ((strcmp(tinfo[i].typelem, "0") != 0) && tinfo[i].typname[0] != '_') - tinfo[i].isArray = 1; + tinfo[i].isArray = true; else - tinfo[i].isArray = 0; + tinfo[i].isArray = false; - if (strcmp(PQgetvalue(res, i, i_typisdefined), "f") == 0) - tinfo[i].isDefined = 0; + if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0) + tinfo[i].isDefined = true; else - tinfo[i].isDefined = 1; + tinfo[i].isDefined = false; } *numTypes = ntups; @@ -1488,34 +1667,37 @@ getOperators(int *numOprs) int ntups; int i; PQExpBuffer query = createPQExpBuffer(); - OprInfo *oprinfo; - int i_oid; int i_oprname; - int i_oprkind; - int i_oprcode; - int i_oprleft; - int i_oprright; - int i_oprcom; - int i_oprnegate; - int i_oprrest; - int i_oprjoin; - int i_oprcanhash; - int i_oprlsortop; - int i_oprrsortop; + int i_oprnamespace; int i_usename; + int i_oprcode; /* - * find all operators, including builtin operators, filter out - * system-defined operators at dump-out time + * find all operators, including builtin operators; + * we filter out system-defined operators at dump-out time. */ - appendPQExpBuffer(query, "SELECT pg_operator.oid, oprname, oprkind, oprcode, " - "oprleft, oprright, oprcom, oprnegate, oprrest, oprjoin, " - "oprcanhash, oprlsortop, oprrsortop, " - "(select usename from pg_user where oprowner = usesysid) as usename " - "from pg_operator"); + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + + if (g_fout->remoteVersion >= 70300) + { + appendPQExpBuffer(query, "SELECT pg_operator.oid, oprname, " + "oprnamespace, " + "(select usename from pg_user where oprowner = usesysid) as usename, " + "oprcode::oid " + "from pg_operator"); + } + else + { + appendPQExpBuffer(query, "SELECT pg_operator.oid, oprname, " + "0::oid as oprnamespace, " + "(select usename from pg_user where oprowner = usesysid) as usename, " + "oprcode::oid " + "from pg_operator"); + } res = PQexec(g_conn, query->data); if (!res || @@ -1532,40 +1714,22 @@ getOperators(int *numOprs) i_oid = PQfnumber(res, "oid"); i_oprname = PQfnumber(res, "oprname"); - i_oprkind = PQfnumber(res, "oprkind"); - i_oprcode = PQfnumber(res, "oprcode"); - i_oprleft = PQfnumber(res, "oprleft"); - i_oprright = PQfnumber(res, "oprright"); - i_oprcom = PQfnumber(res, "oprcom"); - i_oprnegate = PQfnumber(res, "oprnegate"); - i_oprrest = PQfnumber(res, "oprrest"); - i_oprjoin = PQfnumber(res, "oprjoin"); - i_oprcanhash = PQfnumber(res, "oprcanhash"); - i_oprlsortop = PQfnumber(res, "oprlsortop"); - i_oprrsortop = PQfnumber(res, "oprrsortop"); + i_oprnamespace = PQfnumber(res, "oprnamespace"); i_usename = PQfnumber(res, "usename"); + 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].oprkind = strdup(PQgetvalue(res, i, i_oprkind)); - oprinfo[i].oprcode = strdup(PQgetvalue(res, i, i_oprcode)); - oprinfo[i].oprleft = strdup(PQgetvalue(res, i, i_oprleft)); - oprinfo[i].oprright = strdup(PQgetvalue(res, i, i_oprright)); - oprinfo[i].oprcom = strdup(PQgetvalue(res, i, i_oprcom)); - oprinfo[i].oprnegate = strdup(PQgetvalue(res, i, i_oprnegate)); - oprinfo[i].oprrest = strdup(PQgetvalue(res, i, i_oprrest)); - oprinfo[i].oprjoin = strdup(PQgetvalue(res, i, i_oprjoin)); - oprinfo[i].oprcanhash = strdup(PQgetvalue(res, i, i_oprcanhash)); - oprinfo[i].oprlsortop = strdup(PQgetvalue(res, i, i_oprlsortop)); - oprinfo[i].oprrsortop = strdup(PQgetvalue(res, i, i_oprrsortop)); + 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)); if (strlen(oprinfo[i].usename) == 0) write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n", oprinfo[i].oprname); - } PQclear(res); @@ -1575,273 +1739,6 @@ getOperators(int *numOprs) return oprinfo; } -void -clearTypeInfo(TypeInfo *tp, int numTypes) -{ - int i; - - for (i = 0; i < numTypes; ++i) - { - if (tp[i].oid) - free(tp[i].oid); - if (tp[i].typowner) - free(tp[i].typowner); - if (tp[i].typname) - free(tp[i].typname); - if (tp[i].typlen) - free(tp[i].typlen); - if (tp[i].typprtlen) - free(tp[i].typprtlen); - if (tp[i].typinput) - free(tp[i].typinput); - if (tp[i].typoutput) - free(tp[i].typoutput); - if (tp[i].typreceive) - free(tp[i].typreceive); - if (tp[i].typsend) - free(tp[i].typsend); - if (tp[i].typelem) - free(tp[i].typelem); - if (tp[i].typdelim) - free(tp[i].typdelim); - if (tp[i].typdefault) - free(tp[i].typdefault); - if (tp[i].typrelid) - free(tp[i].typrelid); - if (tp[i].typalign) - free(tp[i].typalign); - if (tp[i].typstorage) - free(tp[i].typstorage); - if (tp[i].usename) - free(tp[i].usename); - if (tp[i].typedefn) - free(tp[i].typedefn); - } - free(tp); -} - -void -clearFuncInfo(FuncInfo *fun, int numFuncs) -{ - int i, - a; - - if (!fun) - return; - for (i = 0; i < numFuncs; ++i) - { - if (fun[i].oid) - free(fun[i].oid); - if (fun[i].proname) - free(fun[i].proname); - if (fun[i].usename) - free(fun[i].usename); - if (fun[i].argtypes) - { - for (a = 0; a < fun[i].nargs; ++a) - if (fun[i].argtypes[a]) - free(fun[i].argtypes[a]); - free(fun[i].argtypes); - } - if (fun[i].prorettype) - free(fun[i].prorettype); - if (fun[i].prosrc) - free(fun[i].prosrc); - if (fun[i].probin) - free(fun[i].probin); - } - free(fun); -} - -static void -clearTableInfo(TableInfo *tblinfo, int numTables) -{ - int i, - j; - - for (i = 0; i < numTables; ++i) - { - - if (tblinfo[i].oid) - free(tblinfo[i].oid); - if (tblinfo[i].relacl) - free(tblinfo[i].relacl); - if (tblinfo[i].usename) - free(tblinfo[i].usename); - - if (tblinfo[i].relname) - free(tblinfo[i].relname); - - if (tblinfo[i].relkind == RELKIND_SEQUENCE) - continue; - - /* Process Attributes */ - for (j = 0; j < tblinfo[i].numatts; j++) - { - if (tblinfo[i].attnames[j]) - free(tblinfo[i].attnames[j]); - if (tblinfo[i].typnames[j]) - free(tblinfo[i].typnames[j]); - } - - if (tblinfo[i].triggers) - { - for (j = 0; j < tblinfo[i].ntrig; j++) - { - if (tblinfo[i].triggers[j].tgsrc) - free(tblinfo[i].triggers[j].tgsrc); - if (tblinfo[i].triggers[j].oid) - free(tblinfo[i].triggers[j].oid); - if (tblinfo[i].triggers[j].tgname) - free(tblinfo[i].triggers[j].tgname); - if (tblinfo[i].triggers[j].tgdel) - free(tblinfo[i].triggers[j].tgdel); - } - free(tblinfo[i].triggers); - } - - if (tblinfo[i].atttypmod) - free((int *) tblinfo[i].atttypmod); - if (tblinfo[i].inhAttrs) - free((int *) tblinfo[i].inhAttrs); - if (tblinfo[i].inhAttrDef) - free((int *) tblinfo[i].inhAttrDef); - if (tblinfo[i].inhNotNull) - free((int *) tblinfo[i].inhNotNull); - if (tblinfo[i].attnames) - free(tblinfo[i].attnames); - if (tblinfo[i].atttypedefns) - free(tblinfo[i].atttypedefns); - if (tblinfo[i].typnames) - free(tblinfo[i].typnames); - if (tblinfo[i].notnull) - free(tblinfo[i].notnull); - if (tblinfo[i].primary_key_name) - free(tblinfo[i].primary_key_name); - } - free(tblinfo); -} - -void -clearInhInfo(InhInfo *inh, int numInherits) -{ - int i; - - if (!inh) - return; - for (i = 0; i < numInherits; ++i) - { - if (inh[i].inhrelid) - free(inh[i].inhrelid); - if (inh[i].inhparent) - free(inh[i].inhparent); - } - free(inh); -} - -void -clearOprInfo(OprInfo *opr, int numOprs) -{ - int i; - - if (!opr) - return; - for (i = 0; i < numOprs; ++i) - { - if (opr[i].oid) - free(opr[i].oid); - if (opr[i].oprname) - free(opr[i].oprname); - if (opr[i].oprkind) - free(opr[i].oprkind); - if (opr[i].oprcode) - free(opr[i].oprcode); - if (opr[i].oprleft) - free(opr[i].oprleft); - if (opr[i].oprright) - free(opr[i].oprright); - if (opr[i].oprcom) - free(opr[i].oprcom); - if (opr[i].oprnegate) - free(opr[i].oprnegate); - if (opr[i].oprrest) - free(opr[i].oprrest); - if (opr[i].oprjoin) - free(opr[i].oprjoin); - if (opr[i].oprcanhash) - free(opr[i].oprcanhash); - if (opr[i].oprlsortop) - free(opr[i].oprlsortop); - if (opr[i].oprrsortop) - free(opr[i].oprrsortop); - if (opr[i].usename) - free(opr[i].usename); - } - free(opr); -} - -void -clearIndInfo(IndInfo *ind, int numIndexes) -{ - int i, - a; - - if (!ind) - return; - for (i = 0; i < numIndexes; ++i) - { - if (ind[i].indexreloid) - free(ind[i].indexreloid); - if (ind[i].indreloid) - free(ind[i].indreloid); - if (ind[i].indexrelname) - free(ind[i].indexrelname); - if (ind[i].indrelname) - free(ind[i].indrelname); - if (ind[i].indexdef) - free(ind[i].indexdef); - if (ind[i].indisprimary) - free(ind[i].indisprimary); - if (ind[i].indkey) - { - for (a = 0; a < ind[i].indnkeys; ++a) - if (ind[i].indkey[a]) - free(ind[i].indkey[a]); - free(ind[i].indkey); - } - } - free(ind); -} - -void -clearAggInfo(AggInfo *agginfo, int numArgs) -{ - int i; - - if (!agginfo) - return; - for (i = 0; i < numArgs; ++i) - { - if (agginfo[i].oid) - free(agginfo[i].oid); - if (agginfo[i].aggname) - free(agginfo[i].aggname); - if (agginfo[i].aggtransfn) - free(agginfo[i].aggtransfn); - if (agginfo[i].aggfinalfn) - free(agginfo[i].aggfinalfn); - if (agginfo[i].aggtranstype) - free(agginfo[i].aggtranstype); - if (agginfo[i].aggbasetype) - free(agginfo[i].aggbasetype); - if (agginfo[i].agginitval) - free(agginfo[i].agginitval); - if (agginfo[i].usename) - free(agginfo[i].usename); - } - free(agginfo); -} - /* * getAggregates: * read all the user-defined aggregates in the system catalogs and @@ -1860,43 +1757,32 @@ getAggregates(int *numAggs) int i_oid; int i_aggname; - int i_aggtransfn; - int i_aggfinalfn; - int i_aggtranstype; - int i_aggbasetype; - int i_agginitval; + int i_aggnamespace; int i_usename; - int i_convertok; + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); /* find all user-defined aggregates */ - if (g_fout->remoteVersion < 70100) + if (g_fout->remoteVersion >= 70300) { - appendPQExpBuffer(query, "SELECT pg_aggregate.oid, aggname, aggtransfn1 as aggtransfn, " - "aggfinalfn, aggtranstype1 as aggtranstype, aggbasetype, " - "agginitval1 as agginitval, " - "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) as convertok, " - "(select usename from pg_user where aggowner = usesysid) as usename " - "from pg_aggregate"); - } - else if (g_fout->remoteVersion < 70300) - { - appendPQExpBuffer(query, "SELECT pg_aggregate.oid, aggname, aggtransfn, " - "aggfinalfn, aggtranstype, aggbasetype, " - "agginitval, " - "'t'::boolean as convertok, " - "(select usename from pg_user where aggowner = usesysid) as usename " - "from pg_aggregate"); + appendPQExpBuffer(query, "SELECT pg_proc.oid, proname as aggname, " + "pronamespace as aggnamespace, " + "(select usename from pg_user where proowner = usesysid) as usename " + "FROM pg_proc " + "WHERE proisagg " + "AND pronamespace != " + "(select oid from pg_namespace where nspname = 'pg_catalog')"); } else { - appendPQExpBuffer(query, "SELECT p.oid, proname as aggname, aggtransfn, " - "aggfinalfn, aggtranstype, proargtypes[0] as aggbasetype, " - "agginitval, " - "'t'::boolean as convertok, " - "(select usename from pg_user where proowner = usesysid) as usename " - "from pg_aggregate a, pg_proc p " - "where a.aggfnoid = p.oid"); + appendPQExpBuffer(query, "SELECT pg_aggregate.oid, aggname, " + "0::oid as aggnamespace, " + "(select usename from pg_user where aggowner = usesysid) as usename " + "from pg_aggregate " + "where oid > '%u'::oid", + g_last_builtin_oid); } res = PQexec(g_conn, query->data); @@ -1915,30 +1801,19 @@ getAggregates(int *numAggs) i_oid = PQfnumber(res, "oid"); i_aggname = PQfnumber(res, "aggname"); - i_aggtransfn = PQfnumber(res, "aggtransfn"); - i_aggfinalfn = PQfnumber(res, "aggfinalfn"); - i_aggtranstype = PQfnumber(res, "aggtranstype"); - i_aggbasetype = PQfnumber(res, "aggbasetype"); - i_agginitval = PQfnumber(res, "agginitval"); + i_aggnamespace = PQfnumber(res, "aggnamespace"); i_usename = PQfnumber(res, "usename"); - i_convertok = PQfnumber(res, "convertok"); 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].aggtransfn = strdup(PQgetvalue(res, i, i_aggtransfn)); - agginfo[i].aggfinalfn = strdup(PQgetvalue(res, i, i_aggfinalfn)); - agginfo[i].aggtranstype = strdup(PQgetvalue(res, i, i_aggtranstype)); - agginfo[i].aggbasetype = strdup(PQgetvalue(res, i, i_aggbasetype)); - agginfo[i].agginitval = strdup(PQgetvalue(res, i, i_agginitval)); + agginfo[i].aggnamespace = findNamespace(PQgetvalue(res, i, i_aggnamespace), + agginfo[i].oid); agginfo[i].usename = strdup(PQgetvalue(res, i, i_usename)); if (strlen(agginfo[i].usename) == 0) write_msg(NULL, "WARNING: owner of aggregate function \"%s\" appears to be invalid\n", agginfo[i].aggname); - - agginfo[i].convertok = (PQgetvalue(res, i, i_convertok)[0] == 't'); - } PQclear(res); @@ -1954,8 +1829,6 @@ getAggregates(int *numAggs) * return them in the FuncInfo* structure * * numFuncs is set to the number of functions read in - * - * */ FuncInfo * getFuncs(int *numFuncs) @@ -1968,55 +1841,39 @@ getFuncs(int *numFuncs) int i_oid; int i_proname; + int i_pronamespace; + int i_usename; int i_prolang; int i_pronargs; int i_proargtypes; int i_prorettype; - int i_proretset; - int i_prosrc; - int i_probin; - int i_provolatile; - int i_isimplicit; - int i_isstrict; - int i_usename; + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); /* find all user-defined funcs */ - if (g_fout->remoteVersion < 70100) + if (g_fout->remoteVersion >= 70300) { appendPQExpBuffer(query, - "SELECT pg_proc.oid, proname, prolang, pronargs, prorettype, " - "proretset, proargtypes, prosrc, probin, " - "(select usename from pg_user where proowner = usesysid) as usename, " - "case when proiscachable then 'i' else 'v' end as provolatile, " - "'f'::boolean as proimplicit, " - "'f'::boolean as proisstrict " - "from pg_proc " - "where pg_proc.oid > '%u'::oid", - g_last_builtin_oid); - } - else if (g_fout->remoteVersion < 70300) - { - appendPQExpBuffer(query, - "SELECT pg_proc.oid, proname, prolang, pronargs, prorettype, " - "proretset, proargtypes, prosrc, probin, " - "(select usename from pg_user where proowner = usesysid) as usename, " - "case when proiscachable then 'i' else 'v' end as provolatile, " - "'f'::boolean as proimplicit, " - "proisstrict " - "from pg_proc " - "where pg_proc.oid > '%u'::oid", - g_last_builtin_oid); + "SELECT pg_proc.oid, proname, prolang, " + "pronargs, proargtypes, prorettype, " + "pronamespace, " + "(select usename from pg_user where proowner = usesysid) as usename " + "FROM pg_proc " + "WHERE NOT proisagg " + "AND pronamespace != " + "(select oid from pg_namespace where nspname = 'pg_catalog')"); } else { appendPQExpBuffer(query, - "SELECT pg_proc.oid, proname, prolang, pronargs, prorettype, " - "proretset, proargtypes, prosrc, probin, " - "(select usename from pg_user where proowner = usesysid) as usename, " - "provolatile, proimplicit, proisstrict " - "from pg_proc " - "where pg_proc.oid > '%u'::oid and not proisagg", + "SELECT pg_proc.oid, proname, prolang, " + "pronargs, proargtypes, prorettype, " + "0::oid as pronamespace, " + "(select usename from pg_user where proowner = usesysid) as usename " + "FROM pg_proc " + "where pg_proc.oid > '%u'::oid", g_last_builtin_oid); } @@ -2039,44 +1896,38 @@ getFuncs(int *numFuncs) i_oid = PQfnumber(res, "oid"); i_proname = PQfnumber(res, "proname"); + i_pronamespace = PQfnumber(res, "pronamespace"); + i_usename = PQfnumber(res, "usename"); i_prolang = PQfnumber(res, "prolang"); i_pronargs = PQfnumber(res, "pronargs"); i_proargtypes = PQfnumber(res, "proargtypes"); i_prorettype = PQfnumber(res, "prorettype"); - i_proretset = PQfnumber(res, "proretset"); - i_prosrc = PQfnumber(res, "prosrc"); - i_probin = PQfnumber(res, "probin"); - i_provolatile = PQfnumber(res, "provolatile"); - i_isimplicit = PQfnumber(res, "proimplicit"); - i_isstrict = PQfnumber(res, "proisstrict"); - i_usename = PQfnumber(res, "usename"); 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].prosrc = strdup(PQgetvalue(res, i, i_prosrc)); - finfo[i].probin = strdup(PQgetvalue(res, i, i_probin)); - - finfo[i].prorettype = strdup(PQgetvalue(res, i, i_prorettype)); - finfo[i].retset = (strcmp(PQgetvalue(res, i, i_proretset), "t") == 0); - finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs)); - finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang)); + finfo[i].pronamespace = findNamespace(PQgetvalue(res, i, i_pronamespace), + finfo[i].oid); finfo[i].usename = strdup(PQgetvalue(res, i, i_usename)); - finfo[i].provolatile = (PQgetvalue(res, i, i_provolatile))[0]; - finfo[i].isimplicit = (strcmp(PQgetvalue(res, i, i_isimplicit), "t") == 0); - finfo[i].isstrict = (strcmp(PQgetvalue(res, i, i_isstrict), "t") == 0); + finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang)); + finfo[i].prorettype = strdup(PQgetvalue(res, i, i_prorettype)); + 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].dumped = false; if (strlen(finfo[i].usename) == 0) write_msg(NULL, "WARNING: owner of function \"%s\" appears to be invalid\n", finfo[i].proname); - - 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].dumped = 0; } PQclear(res); @@ -2094,7 +1945,7 @@ getFuncs(int *numFuncs) * numTables is set to the number of tables read in */ TableInfo * -getTables(int *numTables, FuncInfo *finfo, int numFuncs, const char *tablename) +getTables(int *numTables) { PGresult *res; int ntups; @@ -2106,32 +1957,57 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs, const char *tablename) int i_reloid; int i_relname; + int i_relnamespace; int i_relkind; int i_relacl; int i_usename; int i_relchecks; int i_reltriggers; int i_relhasindex; + int i_relhasrules; int i_relhasoids; + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + /* - * find all the user-defined tables (no indexes and no catalogs), - * ordering by oid is important so that we always process the parent - * tables before the child tables when traversing the tblinfo* + * Find all the tables (including views and sequences). * - * we ignore tables that are not type 'r' (ordinary relation) or 'S' + * 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). + * + * 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. */ - if (g_fout->remoteVersion >= 70200) + if (g_fout->remoteVersion >= 70300) { appendPQExpBuffer(query, - "SELECT pg_class.oid, relname, relacl, relkind, " + "SELECT pg_class.oid, relname, relacl, relkind, " + "relnamespace, " + "(select usename from pg_user where relowner = usesysid) as usename, " - "relchecks, reltriggers, relhasindex, relhasoids " + "relchecks, reltriggers, " + "relhasindex, relhasrules, relhasoids " "from pg_class " - "where relname !~ '^pg_' " - "and relkind in ('%c', '%c', '%c') " + "where relkind in ('%c', '%c', '%c') " + "order by oid", + RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW); + } + else if (g_fout->remoteVersion >= 70200) + { + appendPQExpBuffer(query, + "SELECT pg_class.oid, relname, relacl, relkind, " + "0::oid as relnamespace, " + "(select usename from pg_user where relowner = usesysid) as usename, " + "relchecks, reltriggers, " + "relhasindex, relhasrules, relhasoids " + "from pg_class " + "where relkind in ('%c', '%c', '%c') " "order by oid", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW); } @@ -2140,11 +2016,12 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs, const char *tablename) /* all tables have oids in 7.1 */ appendPQExpBuffer(query, "SELECT pg_class.oid, relname, relacl, relkind, " + "0::oid as relnamespace, " "(select usename from pg_user where relowner = usesysid) as usename, " - "relchecks, reltriggers, relhasindex, 't'::bool as relhasoids " + "relchecks, reltriggers, " + "relhasindex, relhasrules, 't'::bool as relhasoids " "from pg_class " - "where relname !~ '^pg_' " - "and relkind in ('%c', '%c', '%c') " + "where relkind in ('%c', '%c', '%c') " "order by oid", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW); } @@ -2161,14 +2038,15 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs, const char *tablename) " 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, " - "relchecks, reltriggers, relhasindex, 't'::bool as relhasoids " + "relchecks, reltriggers, " + "relhasindex, relhasrules, 't'::bool as relhasoids " "from pg_class c " - "where relname !~ '^pg_' " - "and relkind in ('%c', '%c', '%c') " + "where relkind in ('%c', '%c') " "order by oid", RELKIND_VIEW, - RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW); + RELKIND_RELATION, RELKIND_SEQUENCE); } res = PQexec(g_conn, query->data); @@ -2185,64 +2063,72 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs, const char *tablename) *numTables = ntups; /* - * First pass: extract data from result and lock tables. We do the + * 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 collect info about all tables here, even when - * dumping only one, because we don't know which tables might be - * inheritance ancestors of the target table. Possible future - * improvement: suppress later collection of schema info about tables - * that are determined not to be either targets or ancestors of - * targets. + * 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)); 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_relchecks = PQfnumber(res, "relchecks"); i_reltriggers = PQfnumber(res, "reltriggers"); i_relhasindex = PQfnumber(res, "relhasindex"); + i_relhasrules = PQfnumber(res, "relhasrules"); i_relhasoids = PQfnumber(res, "relhasoids"); 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].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].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0); tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0); - tblinfo[i].usename = strdup(PQgetvalue(res, i, i_usename)); tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks)); tblinfo[i].ntrig = atoi(PQgetvalue(res, i, i_reltriggers)); + /* other fields were zeroed above */ + + /* + * Decide whether we want to dump this table. + */ + selectDumpableTable(&tblinfo[i]); + tblinfo[i].interesting = tblinfo[i].dump; + /* * Read-lock target tables to make sure they aren't DROPPED or * altered in schema before we get around to dumping them. * - * If no target tablename was specified, lock all tables we see, - * otherwise lock only the specified table. (This is incomplete - * because we'll still try to collect schema info about all - * tables, and could possibly lose during that phase. But for the - * typical use where we're dumping all tables anyway, it matters - * not.) + * 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].relkind == RELKIND_RELATION) && - (tablename == NULL || strcmp(tblinfo[i].relname, tablename) == 0)) + if (tblinfo[i].dump && tblinfo[i].relkind == RELKIND_RELATION) { PGresult *lres; resetPQExpBuffer(lockquery); appendPQExpBuffer(lockquery, "LOCK TABLE %s IN ACCESS SHARE MODE", - fmtId(tblinfo[i].relname, force_quotes)); + fmtQualifiedId(tblinfo[i].relnamespace->nspname, + tblinfo[i].relname)); lres = PQexec(g_conn, lockquery->data); if (!lres || PQresultStatus(lres) != PGRES_COMMAND_OK) { @@ -2252,525 +2138,14 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs, const char *tablename) } PQclear(lres); } - } - PQclear(res); - res = NULL; - - /* - * Second pass: pick up additional information about each table, as - * required. - */ - for (i = 0; i < *numTables; i++) - { /* Emit notice if join for owner failed */ if (strlen(tblinfo[i].usename) == 0) write_msg(NULL, "WARNING: owner of table \"%s\" appears to be invalid\n", tblinfo[i].relname); - - /* Get definition if it's a view */ - if (tblinfo[i].relkind == RELKIND_VIEW) - { - PGresult *res2; - - resetPQExpBuffer(query); - - if (g_fout->remoteVersion < 70300) - { - appendPQExpBuffer(query, "SELECT definition as viewdef, " - "(select oid from pg_rewrite where " - " rulename=('_RET' || viewname)::name) as view_oid" - " from pg_views where viewname = "); - formatStringLiteral(query, tblinfo[i].relname, CONV_ALL); - appendPQExpBuffer(query, ";"); - } - else - { - /* Beginning in 7.3, viewname is not unique; use OID */ - appendPQExpBuffer(query, "SELECT pg_get_viewdef(ev_class) as viewdef, " - "oid as view_oid" - " from pg_rewrite where" - " ev_class = '%s'::oid and" - " rulename = '_RETURN';", - tblinfo[i].oid); - } - - res2 = PQexec(g_conn, query->data); - if (!res2 || PQresultStatus(res2) != PGRES_TUPLES_OK) - { - write_msg(NULL, "query to obtain definition of view \"%s\" failed: %s", - tblinfo[i].relname, PQerrorMessage(g_conn)); - exit_nicely(); - } - - if (PQntuples(res2) != 1) - { - if (PQntuples(res2) < 1) - write_msg(NULL, "query to obtain definition of view \"%s\" returned no data\n", - tblinfo[i].relname); - else - write_msg(NULL, "query to obtain definition of view \"%s\" returned more than one definition\n", - tblinfo[i].relname); - exit_nicely(); - } - - if (PQgetisnull(res2, 0, 1)) - { - write_msg(NULL, "query to obtain definition of view \"%s\" returned NULL oid\n", - tblinfo[i].relname); - exit_nicely(); - } - - tblinfo[i].viewdef = strdup(PQgetvalue(res2, 0, 0)); - tblinfo[i].viewoid = strdup(PQgetvalue(res2, 0, 1)); - - if (strlen(tblinfo[i].viewdef) == 0) - { - write_msg(NULL, "definition of view \"%s\" appears to be empty (length zero)\n", - tblinfo[i].relname); - exit_nicely(); - } - PQclear(res2); - } - else - tblinfo[i].viewdef = NULL; - - /* - * Get non-inherited CHECK constraints, if any. - * - * Exclude inherited CHECKs from CHECK constraints total. 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. - */ - if (tblinfo[i].ncheck > 0) - { - PGresult *res2; - int i_rcname, - i_rcsrc; - int ntups2; - int i2; - - if (g_verbose) - write_msg(NULL, "finding CHECK constraints for table %s\n", - tblinfo[i].relname); - - resetPQExpBuffer(query); - appendPQExpBuffer(query, "SELECT rcname, rcsrc from pg_relcheck " - " where rcrelid = '%s'::oid " - " and not exists " - " (select * from pg_relcheck as c, pg_inherits as i " - " where i.inhrelid = pg_relcheck.rcrelid " - " and (c.rcname = pg_relcheck.rcname " - " or ( c.rcname[0] = '$' " - " and pg_relcheck.rcname[0] = '$')" - " )" - " and c.rcsrc = pg_relcheck.rcsrc " - " and c.rcrelid = i.inhparent) " - " order by rcname ", - tblinfo[i].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 > tblinfo[i].ncheck) - { - write_msg(NULL, "expected %d check constraints on table \"%s\" but found %d\n", - tblinfo[i].ncheck, tblinfo[i].relname, ntups2); - write_msg(NULL, "(The system catalogs might be corrupted.)\n"); - exit_nicely(); - } - - /* - * Set ncheck to the number of *non-inherited* CHECK - * constraints - */ - tblinfo[i].ncheck = ntups2; - - i_rcname = PQfnumber(res2, "rcname"); - i_rcsrc = PQfnumber(res2, "rcsrc"); - tblinfo[i].check_expr = (char **) malloc(ntups2 * sizeof(char *)); - for (i2 = 0; i2 < ntups2; i2++) - { - const char *name = PQgetvalue(res2, i2, i_rcname); - const char *expr = PQgetvalue(res2, i2, i_rcsrc); - - resetPQExpBuffer(query); - if (name[0] != '$') - { - appendPQExpBuffer(query, "CONSTRAINT %s ", - fmtId(name, force_quotes)); - } - appendPQExpBuffer(query, "CHECK (%s)", expr); - tblinfo[i].check_expr[i2] = strdup(query->data); - } - PQclear(res2); - } - else - tblinfo[i].check_expr = NULL; - - /* Get primary key */ - if (tblinfo[i].hasindex) - { - PGresult *res2; - - resetPQExpBuffer(query); - appendPQExpBuffer(query, - "SELECT indexrelid FROM pg_index i WHERE i.indisprimary AND i.indrelid = '%s'::oid ", - tblinfo[i].oid); - res2 = PQexec(g_conn, query->data); - if (!res2 || PQresultStatus(res2) != PGRES_TUPLES_OK) - { - write_msg(NULL, "query to obtain primary key of table \"%s\" failed: %s", - tblinfo[i].relname, PQerrorMessage(g_conn)); - exit_nicely(); - } - - if (PQntuples(res2) > 1) - { - write_msg(NULL, "query to obtain primary key of table \"%s\" produced more than one result\n", - tblinfo[i].relname); - exit_nicely(); - } - - if (PQntuples(res2) == 1) - tblinfo[i].pkIndexOid = strdup(PQgetvalue(res2, 0, 0)); - else - tblinfo[i].pkIndexOid = NULL; - - } - else - tblinfo[i].pkIndexOid = NULL; - - /* Get primary key name (if primary key exist) */ - if (tblinfo[i].pkIndexOid != NULL) - { - PGresult *res2; - int n; - - resetPQExpBuffer(query); - appendPQExpBuffer(query, - "SELECT relname FROM pg_class " - "WHERE oid = '%s'::oid", - tblinfo[i].pkIndexOid); - - res2 = PQexec(g_conn, query->data); - if (!res2 || PQresultStatus(res2) != PGRES_TUPLES_OK) - { - write_msg(NULL, "query to obtain name of primary key of table \"%s\" failed: %s", - tblinfo[i].relname, PQerrorMessage(g_conn)); - exit_nicely(); - } - - n = PQntuples(res2); - if (n != 1) - { - if (n == 0) - write_msg(NULL, "query to obtain name of primary key of table \"%s\" returned no rows\n", - tblinfo[i].relname); - else - write_msg(NULL, "query to obtain name of primary key of table \"%s\" returned %d rows\n", - tblinfo[i].relname, n); - exit_nicely(); - } - - tblinfo[i].primary_key_name = - strdup(fmtId(PQgetvalue(res2, 0, 0), force_quotes)); - if (tblinfo[i].primary_key_name == NULL) - { - write_msg(NULL, "out of memory\n"); - exit_nicely(); - } - } - else - tblinfo[i].primary_key_name = NULL; - - /* Get Triggers */ - if (tblinfo[i].ntrig > 0) - { - PGresult *res2; - int i_tgoid, - i_tgname, - i_tgfoid, - i_tgtype, - i_tgnargs, - i_tgargs, - i_tgisconstraint, - i_tgconstrname, - i_tgdeferrable, - i_tgconstrrelid, - i_tgconstrrelname, - i_tginitdeferred; - int ntups2; - int i2; - - if (g_verbose) - write_msg(NULL, "finding triggers for table %s\n", tblinfo[i].relname); - - resetPQExpBuffer(query); - appendPQExpBuffer(query, - "SELECT tgname, tgfoid, 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 ", - tblinfo[i].oid); - res2 = PQexec(g_conn, query->data); - if (!res2 || - PQresultStatus(res2) != PGRES_TUPLES_OK) - { - write_msg(NULL, "query to obtain list of triggers failed: %s", PQerrorMessage(g_conn)); - exit_nicely(); - } - ntups2 = PQntuples(res2); - if (ntups2 != tblinfo[i].ntrig) - { - write_msg(NULL, "expected %d triggers on table \"%s\" but found %d\n", - tblinfo[i].ntrig, tblinfo[i].relname, ntups2); - exit_nicely(); - } - i_tgname = PQfnumber(res2, "tgname"); - i_tgfoid = PQfnumber(res2, "tgfoid"); - i_tgtype = PQfnumber(res2, "tgtype"); - i_tgnargs = PQfnumber(res2, "tgnargs"); - i_tgargs = PQfnumber(res2, "tgargs"); - i_tgoid = PQfnumber(res2, "oid"); - i_tgisconstraint = PQfnumber(res2, "tgisconstraint"); - i_tgconstrname = PQfnumber(res2, "tgconstrname"); - i_tgdeferrable = PQfnumber(res2, "tgdeferrable"); - i_tgconstrrelid = PQfnumber(res2, "tgconstrrelid"); - i_tgconstrrelname = PQfnumber(res2, "tgconstrrelname"); - i_tginitdeferred = PQfnumber(res2, "tginitdeferred"); - - tblinfo[i].triggers = (TrigInfo *) malloc(ntups2 * sizeof(TrigInfo)); - resetPQExpBuffer(query); - for (i2 = 0; i2 < ntups2; i2++) - { - const char *tgfuncoid = PQgetvalue(res2, i2, i_tgfoid); - char *tgfunc = NULL; - int2 tgtype = atoi(PQgetvalue(res2, i2, i_tgtype)); - int tgnargs = atoi(PQgetvalue(res2, i2, i_tgnargs)); - const char *tgargs = PQgetvalue(res2, i2, i_tgargs); - int tgisconstraint; - int tgdeferrable; - int tginitdeferred; - char *tgconstrrelid; - char *tgname; - const char *p; - int findx; - - tgname = PQgetvalue(res2, i2, i_tgname); - - if (strcmp(PQgetvalue(res2, i2, i_tgisconstraint), "f") == 0) - tgisconstraint = 0; - else - tgisconstraint = 1; - - if (strcmp(PQgetvalue(res2, i2, i_tgdeferrable), "f") == 0) - tgdeferrable = 0; - else - tgdeferrable = 1; - - if (strcmp(PQgetvalue(res2, i2, i_tginitdeferred), "f") == 0) - tginitdeferred = 0; - else - tginitdeferred = 1; - - for (findx = 0; findx < numFuncs; findx++) - { - if (strcmp(finfo[findx].oid, tgfuncoid) == 0 && - finfo[findx].nargs == 0 && - strcmp(finfo[findx].prorettype, "0") == 0) - break; - } - - if (findx == numFuncs) - { - PGresult *r; - int numFuncs; - - /* - * the funcname is an oid which we use to find the - * name of the pg_proc. We need to do this because - * getFuncs() only reads in the user-defined funcs not - * all the funcs. We might not find what we want by - * looking in FuncInfo* - */ - resetPQExpBuffer(query); - appendPQExpBuffer(query, - "SELECT proname from pg_proc " - "where pg_proc.oid = '%s'::oid", - tgfuncoid); - - r = PQexec(g_conn, query->data); - if (!r || PQresultStatus(r) != PGRES_TUPLES_OK) - { - write_msg(NULL, "query to obtain procedure name for trigger \"%s\" failed: %s", - tgname, PQerrorMessage(g_conn)); - exit_nicely(); - } - - /* Sanity: Check we got only one tuple */ - numFuncs = PQntuples(r); - if (numFuncs != 1) - { - if (numFuncs == 0) - write_msg(NULL, "query to obtain procedure name for trigger \"%s\" (procedure OID %s) returned no rows\n", - tgname, tgfuncoid); - else - write_msg(NULL, "query to obtain procedure name for trigger \"%s\" (procedure OID %s) returned %d rows\n", - tgname, tgfuncoid, numFuncs); - exit_nicely(); - } - - tgfunc = strdup(PQgetvalue(r, 0, PQfnumber(r, "proname"))); - PQclear(r); - } - else - tgfunc = strdup(finfo[findx].proname); - - appendPQExpBuffer(delqry, "DROP TRIGGER %s ", fmtId(tgname, force_quotes)); - appendPQExpBuffer(delqry, "ON %s;\n", - fmtId(tblinfo[i].relname, force_quotes)); - - resetPQExpBuffer(query); - if (tgisconstraint) - { - appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER "); - appendPQExpBuffer(query, fmtId(PQgetvalue(res2, i2, i_tgconstrname), force_quotes)); - } - else - { - appendPQExpBuffer(query, "CREATE TRIGGER "); - appendPQExpBuffer(query, fmtId(tgname, force_quotes)); - } - appendPQExpBufferChar(query, ' '); - /* 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 ", fmtId(tblinfo[i].relname, force_quotes)); - - if (tgisconstraint) - { - tgconstrrelid = PQgetvalue(res2, i2, i_tgconstrrelid); - - if (strcmp(tgconstrrelid, "0") != 0) - { - - if (PQgetisnull(res2, i2, 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, tblinfo[i].relname, tgconstrrelid); - exit_nicely(); - } - - appendPQExpBuffer(query, " FROM %s", - fmtId(PQgetvalue(res2, i2, i_tgconstrrelname), force_quotes)); - } - if (!tgdeferrable) - appendPQExpBuffer(query, " NOT"); - appendPQExpBuffer(query, " DEFERRABLE INITIALLY "); - if (tginitdeferred) - appendPQExpBuffer(query, "DEFERRED"); - else - appendPQExpBuffer(query, "IMMEDIATE"); - - } - - appendPQExpBuffer(query, " FOR EACH ROW"); - appendPQExpBuffer(query, " EXECUTE PROCEDURE %s (", - fmtId(tgfunc, force_quotes)); - for (findx = 0; findx < tgnargs; findx++) - { - const char *s; - - for (p = tgargs;;) - { - p = strchr(p, '\\'); - if (p == NULL) - { - write_msg(NULL, "bad argument string (%s) for trigger \"%s\" on table \"%s\"\n", - PQgetvalue(res2, i2, i_tgargs), - tgname, - tblinfo[i].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"); - - tblinfo[i].triggers[i2].tgsrc = strdup(query->data); - - /*** Initialize trcomments and troids ***/ - - resetPQExpBuffer(query); - appendPQExpBuffer(query, "TRIGGER %s ", - fmtId(tgname, force_quotes)); - appendPQExpBuffer(query, "ON %s", - fmtId(tblinfo[i].relname, force_quotes)); - tblinfo[i].triggers[i2].tgcomment = strdup(query->data); - tblinfo[i].triggers[i2].oid = strdup(PQgetvalue(res2, i2, i_tgoid)); - tblinfo[i].triggers[i2].tgname = strdup(fmtId(tgname, false)); - tblinfo[i].triggers[i2].tgdel = strdup(delqry->data); - - if (tgfunc) - free(tgfunc); - } - PQclear(res2); - } - else - tblinfo[i].triggers = NULL; - } + PQclear(res); destroyPQExpBuffer(query); destroyPQExpBuffer(delqry); destroyPQExpBuffer(lockquery); @@ -2783,7 +2158,7 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs, const char *tablename) * read all the inheritance information * from the system catalogs return them in the InhInfo* structure * - * numInherits is set to the number of tables read in + * numInherits is set to the number of pairs read in */ InhInfo * getInherits(int *numInherits) @@ -2797,6 +2172,9 @@ getInherits(int *numInherits) int i_inhrelid; int i_inhparent; + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + /* find all the inheritance information */ appendPQExpBuffer(query, "SELECT inhrelid, inhparent from pg_inherits"); @@ -2834,7 +2212,7 @@ getInherits(int *numInherits) /* * getTableAttrs - - * for each table in tblinfo, read its attributes types and names + * 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 @@ -2849,25 +2227,39 @@ getTableAttrs(TableInfo *tblinfo, int numTables) j; PQExpBuffer q = createPQExpBuffer(); int i_attname; - int i_typname; + int i_atttypname; int i_atttypmod; int i_attnotnull; int i_atthasdef; - int i_atttypedefn; PGresult *res; int ntups; + bool hasdefaults; for (i = 0; i < numTables; i++) { + /* Don't bother to collect info for sequences */ if (tblinfo[i].relkind == RELKIND_SEQUENCE) continue; - /* find all the user attributes and their types */ - /* we must read the attribute names in attribute number order! */ + /* Don't bother with uninteresting tables, either */ + if (!tblinfo[i].interesting) + continue; /* + * Make sure we are in proper schema for this table; this allows + * correct retrieval of formatted type names and default exprs + */ + selectSourceSchema(tblinfo[i].relnamespace->nspname); + + /* 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 + * 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 for table %s\n", @@ -2875,33 +2267,27 @@ getTableAttrs(TableInfo *tblinfo, int numTables) resetPQExpBuffer(q); - if (g_fout->remoteVersion < 70100) + if (g_fout->remoteVersion >= 70100) { - /* Fake the LOJ below */ - appendPQExpBuffer(q, - " SELECT a.attnum, a.attname, t.typname, a.atttypmod, " - " a.attnotnull, a.atthasdef, NULL as atttypedefn " - " from pg_attribute a, pg_type t " - " where a.attrelid = '%s'::oid " - " and a.attnum > 0 " - " and a.atttypid = t.oid " - " UNION ALL SELECT a.attnum, a.attname, NULL as typname, a.atttypmod, " - " a.attnotnull, a.atthasdef, NULL as atttypedefn " - " from pg_attribute a " - " where a.attrelid = '%s'::oid " - " and a.attnum > 0 " - " and Not Exists(Select * From pg_type t where a.atttypid = t.oid)" - " order by attnum", - tblinfo[i].oid, tblinfo[i].oid); - + appendPQExpBuffer(q, "SELECT attnum, attname, atttypmod, " + "attnotnull, atthasdef, " + "format_type(atttypid,atttypmod) as atttypname " + "from pg_attribute a " + "where attrelid = '%s'::oid " + "and attnum > 0::int2 " + "order by attrelid, attnum", + tblinfo[i].oid); } else { - appendPQExpBuffer(q, "SELECT a.attnum, a.attname, t.typname, a.atttypmod, " - "a.attnotnull, a.atthasdef, format_type(a.atttypid, a.atttypmod) as atttypedefn " - "from pg_attribute a LEFT OUTER JOIN pg_type t ON a.atttypid = t.oid " - "where a.attrelid = '%s'::oid " - "and a.attnum > 0 order by attnum", + /* format_type not available before 7.1 */ + appendPQExpBuffer(q, "SELECT attnum, attname, atttypmod, " + "attnotnull, atthasdef, " + "(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", tblinfo[i].oid); } @@ -2916,82 +2302,87 @@ getTableAttrs(TableInfo *tblinfo, int numTables) ntups = PQntuples(res); i_attname = PQfnumber(res, "attname"); - i_typname = PQfnumber(res, "typname"); + i_atttypname = PQfnumber(res, "atttypname"); i_atttypmod = PQfnumber(res, "atttypmod"); i_attnotnull = PQfnumber(res, "attnotnull"); i_atthasdef = PQfnumber(res, "atthasdef"); - i_atttypedefn = PQfnumber(res, "atttypedefn"); tblinfo[i].numatts = ntups; tblinfo[i].attnames = (char **) malloc(ntups * sizeof(char *)); - tblinfo[i].atttypedefns = (char **) malloc(ntups * sizeof(char *)); - tblinfo[i].typnames = (char **) malloc(ntups * sizeof(char *)); + tblinfo[i].atttypnames = (char **) malloc(ntups * sizeof(char *)); tblinfo[i].atttypmod = (int *) malloc(ntups * sizeof(int)); - tblinfo[i].inhAttrs = (int *) malloc(ntups * sizeof(int)); - tblinfo[i].inhAttrDef = (int *) malloc(ntups * sizeof(int)); - tblinfo[i].inhNotNull = (int *) malloc(ntups * sizeof(int)); tblinfo[i].notnull = (bool *) malloc(ntups * sizeof(bool)); tblinfo[i].adef_expr = (char **) malloc(ntups * sizeof(char *)); - tblinfo[i].parentRels = NULL; - tblinfo[i].numParents = 0; + tblinfo[i].inhAttrs = (bool *) malloc(ntups * sizeof(bool)); + tblinfo[i].inhAttrDef = (bool *) malloc(ntups * sizeof(bool)); + tblinfo[i].inhNotNull = (bool *) malloc(ntups * sizeof(bool)); + hasdefaults = false; + for (j = 0; j < ntups; j++) { - /* Sanity check on LOJ */ - if (PQgetisnull(res, j, i_typname)) + tblinfo[i].attnames[j] = strdup(PQgetvalue(res, j, i_attname)); + tblinfo[i].atttypnames[j] = strdup(PQgetvalue(res, j, i_atttypname)); + tblinfo[i].atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod)); + tblinfo[i].notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't'); + tblinfo[i].adef_expr[j] = NULL; /* fix below */ + if (PQgetvalue(res, j, i_atthasdef)[0] == 't') + hasdefaults = true; + /* these flags will be set in flagInhAttrs() */ + tblinfo[i].inhAttrs[j] = false; + tblinfo[i].inhAttrDef[j] = false; + tblinfo[i].inhNotNull[j] = false; + } + + PQclear(res); + + if (hasdefaults) + { + int numDefaults; + + if (g_verbose) + write_msg(NULL, "finding DEFAULT expressions for table %s\n", + tblinfo[i].relname); + + resetPQExpBuffer(q); + if (g_fout->remoteVersion >= 70200) { - write_msg(NULL, "query produced NULL name for data type of column %d of table %s\n", - j + 1, tblinfo[i].relname); + appendPQExpBuffer(q, "SELECT adnum, " + "pg_get_expr(adbin, adrelid) AS adsrc " + "FROM pg_attrdef " + "WHERE adrelid = '%s'::oid", + tblinfo[i].oid); + } + else + { + /* no pg_get_expr, so must rely on adsrc */ + appendPQExpBuffer(q, "SELECT adnum, adsrc FROM pg_attrdef " + "WHERE adrelid = '%s'::oid", + tblinfo[i].oid); + } + res = PQexec(g_conn, q->data); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) + { + write_msg(NULL, "query to get column default values failed: %s", + PQerrorMessage(g_conn)); exit_nicely(); } - tblinfo[i].attnames[j] = strdup(PQgetvalue(res, j, i_attname)); - tblinfo[i].atttypedefns[j] = strdup(PQgetvalue(res, j, i_atttypedefn)); - tblinfo[i].typnames[j] = strdup(PQgetvalue(res, j, i_typname)); - tblinfo[i].atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod)); - tblinfo[i].inhAttrs[j] = 0; /* this flag is set in - * flagInhAttrs() */ - tblinfo[i].inhAttrDef[j] = 0; - tblinfo[i].inhNotNull[j] = 0; - - tblinfo[i].notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't') ? true : false; - if (PQgetvalue(res, j, i_atthasdef)[0] == 't') + numDefaults = PQntuples(res); + for (j = 0; j < numDefaults; j++) { - PGresult *res2; - int numAttr; + int adnum = atoi(PQgetvalue(res, j, 0)); - if (g_verbose) - write_msg(NULL, "finding DEFAULT expression for column %s\n", - tblinfo[i].attnames[j]); - - resetPQExpBuffer(q); - appendPQExpBuffer(q, "SELECT adsrc from pg_attrdef " - "where adrelid = '%s'::oid and adnum = %d ", - tblinfo[i].oid, j + 1); - res2 = PQexec(g_conn, q->data); - if (!res2 || - PQresultStatus(res2) != PGRES_TUPLES_OK) + if (adnum <= 0 || adnum > ntups) { - write_msg(NULL, "query to get column default value failed: %s", - PQerrorMessage(g_conn)); + write_msg(NULL, "bogus adnum value %d for table %s\n", + adnum, tblinfo[i].relname); exit_nicely(); } - - /* Sanity: Check we got only one tuple */ - numAttr = PQntuples(res2); - if (numAttr != 1) - { - write_msg(NULL, "query to get default value for column \"%s\" returned %d rows; expected 1\n", - tblinfo[i].attnames[j], numAttr); - exit_nicely(); - } - - tblinfo[i].adef_expr[j] = strdup(PQgetvalue(res2, 0, PQfnumber(res2, "adsrc"))); - PQclear(res2); + tblinfo[i].adef_expr[adnum-1] = strdup(PQgetvalue(res, j, 1)); } - else - tblinfo[i].adef_expr[j] = NULL; + PQclear(res); } - PQclear(res); } destroyPQExpBuffer(q); @@ -2999,121 +2390,23 @@ getTableAttrs(TableInfo *tblinfo, int numTables) /* - * getIndexes - * read all the user-defined indexes information - * from the system catalogs return them in the InhInfo* structure - * - * numIndexes is set to the number of indexes read in - * - * - */ -IndInfo * -getIndexes(int *numIndexes) -{ - int i; - PQExpBuffer query = createPQExpBuffer(); - PGresult *res; - int ntups; - IndInfo *indinfo; - - int i_indexreloid; - int i_indreloid; - int i_indexrelname; - int i_indrelname; - int i_indexdef; - int i_indisprimary; - int i_indnkeys; - int i_indkey; - - /* - * find all the user-defined indexes. - * - * Notice we skip indexes on system classes - * - * XXXX: Use LOJ - */ - - appendPQExpBuffer(query, - "SELECT i.indexrelid as indexreloid, " - "i.indrelid as indreloid, " - "t1.relname as indexrelname, t2.relname as indrelname, " - "pg_get_indexdef(i.indexrelid) as indexdef, " - "i.indisprimary, i.indkey, " - "CASE WHEN i.indproc <> 0 " - " THEN (SELECT pronargs FROM pg_proc WHERE pg_proc.oid = i.indproc) " - " ELSE t1.relnatts END as indnkeys " - "FROM pg_index i, pg_class t1, pg_class t2 " - "WHERE t1.oid = i.indexrelid and t2.oid = i.indrelid " - "and i.indexrelid > '%u'::oid " - "and t2.relname !~ '^pg_' ", - g_last_builtin_oid); - - if (g_fout->remoteVersion < 70100) - appendPQExpBuffer(query, " and t2.relkind != 'l'"); - - res = PQexec(g_conn, query->data); - if (!res || - PQresultStatus(res) != PGRES_TUPLES_OK) - { - write_msg(NULL, "query to obtain list of indexes failed: %s", PQerrorMessage(g_conn)); - exit_nicely(); - } - - ntups = PQntuples(res); - - *numIndexes = ntups; - - indinfo = (IndInfo *) malloc(ntups * sizeof(IndInfo)); - - memset((char *) indinfo, 0, ntups * sizeof(IndInfo)); - - i_indexreloid = PQfnumber(res, "indexreloid"); - i_indreloid = PQfnumber(res, "indreloid"); - i_indexrelname = PQfnumber(res, "indexrelname"); - i_indrelname = PQfnumber(res, "indrelname"); - i_indexdef = PQfnumber(res, "indexdef"); - i_indisprimary = PQfnumber(res, "indisprimary"); - i_indnkeys = PQfnumber(res, "indnkeys"); - i_indkey = PQfnumber(res, "indkey"); - - for (i = 0; i < ntups; i++) - { - indinfo[i].indexreloid = strdup(PQgetvalue(res, i, i_indexreloid)); - indinfo[i].indreloid = strdup(PQgetvalue(res, i, i_indreloid)); - indinfo[i].indexrelname = strdup(PQgetvalue(res, i, i_indexrelname)); - indinfo[i].indrelname = strdup(PQgetvalue(res, i, i_indrelname)); - indinfo[i].indexdef = strdup(PQgetvalue(res, i, i_indexdef)); - indinfo[i].indisprimary = strdup(PQgetvalue(res, i, i_indisprimary)); - indinfo[i].indnkeys = atoi(PQgetvalue(res, i, i_indnkeys)); - indinfo[i].indkey = malloc(indinfo[i].indnkeys * sizeof(indinfo[i].indkey[0])); - parseNumericArray(PQgetvalue(res, i, i_indkey), - indinfo[i].indkey, - indinfo[i].indnkeys); - } - PQclear(res); - - destroyPQExpBuffer(query); - - return indinfo; -} - -/*------------------------------------------------------------------ * dumpComment -- * * This routine is used to dump any comments associated with the * oid handed to this routine. The routine takes a constant character * string for the target part of the comment-creation command, plus - * OID, class name, and subid which are the primary key for pg_description. + * 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. * 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. - *------------------------------------------------------------------ -*/ + */ static void -dumpComment(Archive *fout, const char *target, const char *oid, - const char *classname, int subid, +dumpComment(Archive *fout, const char *target, + const char *namespace, const char *owner, + const char *oid, const char *classname, int subid, const char *((*deps)[])) { PGresult *res; @@ -3124,11 +2417,24 @@ dumpComment(Archive *fout, const char *target, const char *oid, 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 >= 70200) + if (fout->remoteVersion >= 70300) + { + appendPQExpBuffer(query, "SELECT description FROM pg_catalog.pg_description " + "WHERE objoid = '%s'::oid and classoid = " + "'pg_catalog.%s'::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 = " @@ -3163,27 +2469,140 @@ dumpComment(Archive *fout, const char *target, const char *oid, PASS_LFTAB); appendPQExpBuffer(query, ";\n"); - ArchiveEntry(fout, oid, target, "COMMENT", deps, - query->data, "" /* Del */ , - "" /* Copy */ , "" /* Owner */ , NULL, NULL); + ArchiveEntry(fout, oid, target, namespace, owner, + "COMMENT", deps, + query->data, "", NULL, NULL, NULL); } - /*** Clear the statement buffer and return ***/ - PQclear(res); 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. + */ +static void +dumpTableComment(Archive *fout, TableInfo *tbinfo, + const char *reltypename, + const char *((*deps)[])) +{ + PGresult *res; + 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. + */ + + /*** Build query to find comments ***/ + + query = createPQExpBuffer(); + target = createPQExpBuffer(); + + if (fout->remoteVersion >= 70300) + { + appendPQExpBuffer(query, "SELECT description, objsubid FROM pg_catalog.pg_description " + "WHERE objoid = '%s'::oid and classoid = " + "'pg_catalog.pg_class'::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++) + { + const char *descr = PQgetvalue(res, i, i_description); + int objsubid = atoi(PQgetvalue(res, i, i_objsubid)); + + if (objsubid == 0) + { + resetPQExpBuffer(target); + appendPQExpBuffer(target, "%s %s", reltypename, + fmtId(tbinfo->relname, force_quotes)); + + resetPQExpBuffer(query); + appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data); + formatStringLiteral(query, descr, PASS_LFTAB); + appendPQExpBuffer(query, ";\n"); + + ArchiveEntry(fout, tbinfo->oid, target->data, + tbinfo->relnamespace->nspname, tbinfo->usename, + "COMMENT", deps, + query->data, "", NULL, NULL, NULL); + } + else if (objsubid > 0 && objsubid <= tbinfo->numatts) + { + resetPQExpBuffer(target); + appendPQExpBuffer(target, "COLUMN %s.", + fmtId(tbinfo->relname, force_quotes)); + appendPQExpBuffer(target, "%s", + fmtId(tbinfo->attnames[objsubid-1], + force_quotes)); + + resetPQExpBuffer(query); + appendPQExpBuffer(query, "COMMENT ON %s IS ", target->data); + formatStringLiteral(query, descr, PASS_LFTAB); + appendPQExpBuffer(query, ";\n"); + + ArchiveEntry(fout, tbinfo->oid, target->data, + tbinfo->relnamespace->nspname, tbinfo->usename, + "COMMENT", deps, + query->data, "", NULL, NULL, NULL); + } + } + + PQclear(res); + destroyPQExpBuffer(query); + destroyPQExpBuffer(target); +} + +/* * dumpDBComment -- * * This routine is used to dump any comments associated with the * database to which we are currently connected. If the user chose * to dump the schema of the database, then this is the first * statement issued. - *------------------------------------------------------------------ -*/ - + */ void dumpDBComment(Archive *fout) { @@ -3191,6 +2610,9 @@ dumpDBComment(Archive *fout) PQExpBuffer query; int i_oid; + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + /*** Build query to find comment ***/ query = createPQExpBuffer(); @@ -3214,19 +2636,303 @@ dumpDBComment(Archive *fout) i_oid = PQfnumber(res, "oid"); resetPQExpBuffer(query); appendPQExpBuffer(query, "DATABASE %s", fmtId(PQdb(g_conn), force_quotes)); - dumpComment(fout, query->data, PQgetvalue(res, 0, i_oid), - "pg_database", 0, NULL); + dumpComment(fout, query->data, NULL, "", + PQgetvalue(res, 0, i_oid), "pg_database", 0, NULL); } - /*** Clear the statement buffer and return ***/ - PQclear(res); destroyPQExpBuffer(query); } +/* + * dumpNamespaces + * writes out to fout the queries to recreate user-defined namespaces + */ +void +dumpNamespaces(Archive *fout, NamespaceInfo *nsinfo, int numNamespaces) +{ + PQExpBuffer q = createPQExpBuffer(); + PQExpBuffer delq = createPQExpBuffer(); + int i; + + for (i = 0; i < numNamespaces; i++) + { + /* skip if not to be dumped */ + if (!nsinfo[i].dump) + continue; + + /* don't dump dummy namespace from pre-7.3 source */ + if (strlen(nsinfo[i].nspname) == 0) + continue; + + /* quick hack: don't dump CREATE SCHEMA for public namespace */ + /* XXX need a better idea */ + if (strcmp(nsinfo[i].nspname, "public") == 0) + continue; + + resetPQExpBuffer(q); + resetPQExpBuffer(delq); + +#ifdef NOTYET /* suppress till DROP SCHEMA works */ + appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", + fmtId(nsinfo[i].nspname, force_quotes)); +#endif + + appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", + fmtId(nsinfo[i].nspname, force_quotes)); + + ArchiveEntry(fout, nsinfo[i].oid, nsinfo[i].nspname, + NULL, + nsinfo[i].usename, "SCHEMA", NULL, + q->data, delq->data, NULL, NULL, NULL); + +#ifdef NOTYET /* suppress till COMMENT ON SCHEMA works */ + /*** Dump Schema Comments ***/ + resetPQExpBuffer(q); + appendPQExpBuffer(q, "SCHEMA %s", + fmtId(nsinfo[i].nspname, force_quotes)); + dumpComment(fout, q->data, + NULL, nsinfo[i].usename, + nsinfo[i].oid, "pg_namespace", 0, NULL); +#endif + } + + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); +} + +/* + * dumpOneBaseType + * 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) +{ + PQExpBuffer q = createPQExpBuffer(); + PQExpBuffer delq = createPQExpBuffer(); + PQExpBuffer query = createPQExpBuffer(); + PGresult *res; + int ntups; + int funcInd; + char *typlen; + char *typprtlen; + char *typinput; + char *typoutput; + char *typreceive; + char *typsend; + char *typinputoid; + char *typoutputoid; + char *typreceiveoid; + char *typsendoid; + char *typdelim; + char *typdefault; + char *typbyval; + char *typalign; + char *typstorage; + const char *((*deps)[]); + int depIdx = 0; + + deps = malloc(sizeof(char *) * 10); + + /* Set proper schema search path so regproc references list correctly */ + selectSourceSchema(tinfo->typnamespace->nspname); + + /* Fetch type-specific details */ + if (fout->remoteVersion >= 70100) + { + appendPQExpBuffer(query, "SELECT typlen, typprtlen, " + "typinput, typoutput, typreceive, typsend, " + "typinput::oid as typinputoid, " + "typoutput::oid as typoutputoid, " + "typreceive::oid as typreceiveoid, " + "typsend::oid as typsendoid, " + "typdelim, typdefault, typbyval, typalign, " + "typstorage " + "FROM pg_type " + "WHERE oid = '%s'::oid", + tinfo->oid); + } + else + { + appendPQExpBuffer(query, "SELECT typlen, typprtlen, " + "typinput, typoutput, typreceive, typsend, " + "typinput::oid as typinputoid, " + "typoutput::oid as typoutputoid, " + "typreceive::oid as typreceiveoid, " + "typsend::oid as typsendoid, " + "typdelim, typdefault, typbyval, typalign, " + "'p'::char as typstorage " + "FROM pg_type " + "WHERE oid = '%s'::oid", + tinfo->oid); + } + + res = PQexec(g_conn, query->data); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) + { + write_msg(NULL, "query to obtain type information for %s failed: %s", + tinfo->typname, PQerrorMessage(g_conn)); + exit_nicely(); + } + + /* 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(); + } + + typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen")); + typprtlen = PQgetvalue(res, 0, PQfnumber(res, "typprtlen")); + typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput")); + 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")); + typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim")); + if (PQgetisnull(res, 0, PQfnumber(res, "typdefault"))) + typdefault = NULL; + else + typdefault = strdup(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, typinputoid) != 0) + { + funcInd = findFuncByOid(g_finfo, numFuncs, typreceiveoid); + if (funcInd >= 0 && g_finfo[funcInd].pronamespace->dump) + (*deps)[depIdx++] = strdup(typreceiveoid); + } + + if (strcmp(typsendoid, typoutputoid) != 0) + { + funcInd = findFuncByOid(g_finfo, numFuncs, typsendoid); + if (funcInd >= 0 && g_finfo[funcInd].pronamespace->dump) + (*deps)[depIdx++] = strdup(typsendoid); + } + + appendPQExpBuffer(delq, "DROP TYPE %s;\n", + fmtId(tinfo->typname, force_quotes)); + + appendPQExpBuffer(q, + "CREATE TYPE %s " + "( internallength = %s, externallength = %s,", + fmtId(tinfo->typname, force_quotes), + (strcmp(typlen, "-1") == 0) ? "variable" : typlen, + (strcmp(typprtlen, "-1") == 0) ? "variable" : typprtlen); + + if (fout->remoteVersion >= 70300) + { + /* regproc result is correctly quoted in 7.3 */ + appendPQExpBuffer(q, " input = %s, output = %s, " + "send = %s, receive = %s", + typinput, typoutput, typsend, typreceive); + } + else + { + /* regproc delivers an unquoted name before 7.3 */ + /* cannot combine these because fmtId uses static result area */ + appendPQExpBuffer(q, " input = %s,", + fmtId(typinput, force_quotes)); + appendPQExpBuffer(q, " output = %s,", + fmtId(typoutput, force_quotes)); + appendPQExpBuffer(q, " send = %s,", + fmtId(typsend, force_quotes)); + appendPQExpBuffer(q, " receive = %s", + fmtId(typreceive, force_quotes)); + } + + if (typdefault != NULL) + { + appendPQExpBuffer(q, ", default = "); + formatStringLiteral(q, typdefault, CONV_ALL); + } + + if (tinfo->isArray) + { + char *elemType; + + /* reselect schema in case changed by function dump */ + selectSourceSchema(tinfo->typnamespace->nspname); + elemType = getFormattedTypeName(tinfo->typelem, zeroAsOpaque); + appendPQExpBuffer(q, ", element = %s, delimiter = ", elemType); + formatStringLiteral(q, typdelim, CONV_ALL); + free(elemType); + + (*deps)[depIdx++] = strdup(tinfo->typelem); + } + + if (strcmp(typalign, "c") == 0) + appendPQExpBuffer(q, ", alignment = char"); + else if (strcmp(typalign, "s") == 0) + appendPQExpBuffer(q, ", alignment = int2"); + else if (strcmp(typalign, "i") == 0) + appendPQExpBuffer(q, ", alignment = int4"); + else if (strcmp(typalign, "d") == 0) + appendPQExpBuffer(q, ", alignment = double"); + + if (strcmp(typstorage, "p") == 0) + appendPQExpBuffer(q, ", storage = plain"); + else if (strcmp(typstorage, "e") == 0) + appendPQExpBuffer(q, ", storage = external"); + else if (strcmp(typstorage, "x") == 0) + appendPQExpBuffer(q, ", storage = extended"); + else if (strcmp(typstorage, "m") == 0) + appendPQExpBuffer(q, ", storage = main"); + + if (strcmp(typbyval, "t") == 0) + appendPQExpBuffer(q, ", passedbyvalue);\n"); + else + appendPQExpBuffer(q, ");\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); + + /*** Dump Type Comments ***/ + resetPQExpBuffer(q); + + appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->typname, force_quotes)); + dumpComment(fout, q->data, + tinfo->typnamespace->nspname, tinfo->usename, + tinfo->oid, "pg_type", 0, NULL); + + PQclear(res); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); + destroyPQExpBuffer(query); +} + /* * dumpOneDomain - * wites out to fout the queries to recrease a user-defined domains + * writes out to fout the queries to recreate a user-defined domain * as requested by dumpTypes */ static void @@ -3234,24 +2940,28 @@ dumpOneDomain(Archive *fout, TypeInfo *tinfo) { PQExpBuffer q = createPQExpBuffer(); PQExpBuffer delq = createPQExpBuffer(); - - PGresult *res; PQExpBuffer query = createPQExpBuffer(); + PGresult *res; int ntups; + char *typnotnull; + char *typdefn; + char *typdefault; + char *typbasetype; const char *((*deps)[]); int depIdx = 0; - deps = malloc(sizeof(char *) * 10); + /* Set proper schema search path so type references list correctly */ + selectSourceSchema(tinfo->typnamespace->nspname); + /* Fetch domain specific details */ - resetPQExpBuffer(query); appendPQExpBuffer(query, "SELECT typnotnull, " - "format_type(typbasetype, typtypmod) as typdefn, " - "typbasetype " - "FROM pg_type " - "WHERE typname = '%s'", - tinfo->typname); + "format_type(typbasetype, typtypmod) as typdefn, " + "typdefault, typbasetype " + "FROM pg_type " + "WHERE oid = '%s'::oid", + tinfo->oid); res = PQexec(g_conn, query->data); if (!res || @@ -3264,68 +2974,79 @@ dumpOneDomain(Archive *fout, TypeInfo *tinfo) /* Expecting a single result only */ ntups = PQntuples(res); if (ntups != 1) - write_msg(NULL, "Domain %s non-existant.", fmtId(tinfo->typname, force_quotes)); + { + write_msg(NULL, "Got %d rows instead of one from: %s", + ntups, query->data); + exit_nicely(); + } + typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull")); + typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn")); + if (PQgetisnull(res, 0, PQfnumber(res, "typdefault"))) + typdefault = NULL; + else + typdefault = strdup(PQgetvalue(res, 0, PQfnumber(res, "typdefault"))); + typbasetype = PQgetvalue(res, 0, PQfnumber(res, "typbasetype")); - /* Drop the old copy */ - resetPQExpBuffer(delq); - appendPQExpBuffer(delq, "DROP DOMAIN %s RESTRICT;\n", fmtId(tinfo->typname, force_quotes)); + /* Command to drop the old copy */ + appendPQExpBuffer(delq, "DROP DOMAIN %s RESTRICT;\n", + fmtId(tinfo->typname, force_quotes)); - resetPQExpBuffer(q); appendPQExpBuffer(q, "CREATE DOMAIN %s AS %s", fmtId(tinfo->typname, force_quotes), - PQgetvalue(res, 0, PQfnumber(res, "typdefn")) - ); + typdefn); /* Depends on the base type */ - (*deps)[depIdx++] = strdup(PQgetvalue(res, 0, PQfnumber(res, "typbasetype"))); + (*deps)[depIdx++] = strdup(typbasetype); - if (PQgetvalue(res, 0, PQfnumber(res, "typnotnull"))[0] == 't') + if (typnotnull[0] == 't') appendPQExpBuffer(q, " NOT NULL"); - if (tinfo->typdefault) + if (typdefault) { appendPQExpBuffer(q, " DEFAULT %s", - tinfo->typdefault); + typdefault); } appendPQExpBuffer(q, ";\n"); - (*deps)[depIdx++] = NULL; /* End of List */ - ArchiveEntry(fout, tinfo->oid, tinfo->typname, "DOMAIN", deps, - q->data, delq->data, "", tinfo->usename, NULL, NULL); + ArchiveEntry(fout, tinfo->oid, tinfo->typname, + tinfo->typnamespace->nspname, + tinfo->usename, "DOMAIN", deps, + q->data, delq->data, NULL, NULL, NULL); /*** Dump Domain Comments ***/ resetPQExpBuffer(q); appendPQExpBuffer(q, "DOMAIN %s", fmtId(tinfo->typname, force_quotes)); - dumpComment(fout, q->data, tinfo->oid, "pg_type", 0, NULL); + dumpComment(fout, q->data, + tinfo->typnamespace->nspname, tinfo->usename, + tinfo->oid, "pg_type", 0, NULL); + + PQclear(res); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); + destroyPQExpBuffer(query); } /* * dumpTypes * writes out to fout the queries to recreate all the user-defined types - * */ void dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs, TypeInfo *tinfo, int numTypes) { int i; - PQExpBuffer q = createPQExpBuffer(); - PQExpBuffer delq = createPQExpBuffer(); - int funcInd; - const char *((*deps)[]); - int depIdx; for (i = 0; i < numTypes; i++) { - /* skip all the builtin types */ - if (atooid(tinfo[i].oid) <= g_last_builtin_oid) + /* Dump only types in dumpable namespaces */ + if (!tinfo[i].typnamespace->dump) continue; /* skip relation types */ @@ -3338,133 +3059,24 @@ dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs, /* skip all array types that start w/ underscore */ if ((tinfo[i].typname[0] == '_') && - (strcmp(tinfo[i].typinput, "array_in") == 0)) + atooid(tinfo[i].typelem) != 0) continue; - /* Dump out domains as we run across them */ - if (strcmp(tinfo[i].typtype, "d") == 0) { + /* 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]); - continue; - } - - - deps = malloc(sizeof(char *) * 10); - depIdx = 0; - - /* - * before we create a type, we need to create the input and output - * functions for it, if they haven't been created already - */ - funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typinput); - if (funcInd != -1) - { - (*deps)[depIdx++] = strdup(finfo[funcInd].oid); - dumpOneFunc(fout, finfo, funcInd, tinfo, numTypes); - } - - funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typoutput); - if (funcInd != -1) - { - (*deps)[depIdx++] = strdup(finfo[funcInd].oid); - dumpOneFunc(fout, finfo, funcInd, tinfo, numTypes); - } - - resetPQExpBuffer(delq); - appendPQExpBuffer(delq, "DROP TYPE %s;\n", fmtId(tinfo[i].typname, force_quotes)); - - resetPQExpBuffer(q); - appendPQExpBuffer(q, - "CREATE TYPE %s " - "( internallength = %s, externallength = %s,", - fmtId(tinfo[i].typname, force_quotes), - (strcmp(tinfo[i].typlen, "-1") == 0) ? - "variable" : tinfo[i].typlen, - (strcmp(tinfo[i].typprtlen, "-1") == 0) ? - "variable" : tinfo[i].typprtlen); - /* cannot combine these because fmtId uses static result area */ - appendPQExpBuffer(q, " input = %s,", - fmtId(tinfo[i].typinput, force_quotes)); - appendPQExpBuffer(q, " output = %s,", - fmtId(tinfo[i].typoutput, force_quotes)); - appendPQExpBuffer(q, " send = %s,", - fmtId(tinfo[i].typsend, force_quotes)); - appendPQExpBuffer(q, " receive = %s", - fmtId(tinfo[i].typreceive, force_quotes)); - - if (tinfo[i].typdefault != NULL) - { - appendPQExpBuffer(q, ", default = "); - formatStringLiteral(q, tinfo[i].typdefault, CONV_ALL); - } - - if (tinfo[i].isArray) - { - char *elemType; - - elemType = findTypeByOid(tinfo, numTypes, tinfo[i].typelem, zeroAsOpaque); - if (elemType == NULL) - { - write_msg(NULL, "notice: array type %s - type for elements (oid %s) is not dumped\n", - tinfo[i].typname, tinfo[i].typelem); - continue; - } - - appendPQExpBuffer(q, ", element = %s, delimiter = ", elemType); - formatStringLiteral(q, tinfo[i].typdelim, CONV_ALL); - - (*deps)[depIdx++] = strdup(tinfo[i].typelem); - } - - if (strcmp(tinfo[i].typalign, "c") == 0) - appendPQExpBuffer(q, ", alignment = char"); - else if (strcmp(tinfo[i].typalign, "s") == 0) - appendPQExpBuffer(q, ", alignment = int2"); - else if (strcmp(tinfo[i].typalign, "i") == 0) - appendPQExpBuffer(q, ", alignment = int4"); - else if (strcmp(tinfo[i].typalign, "d") == 0) - appendPQExpBuffer(q, ", alignment = double"); - - if (strcmp(tinfo[i].typstorage, "p") == 0) - appendPQExpBuffer(q, ", storage = plain"); - else if (strcmp(tinfo[i].typstorage, "e") == 0) - appendPQExpBuffer(q, ", storage = external"); - else if (strcmp(tinfo[i].typstorage, "x") == 0) - appendPQExpBuffer(q, ", storage = extended"); - else if (strcmp(tinfo[i].typstorage, "m") == 0) - appendPQExpBuffer(q, ", storage = main"); - - if (tinfo[i].passedbyvalue) - appendPQExpBuffer(q, ", passedbyvalue);\n"); - else - appendPQExpBuffer(q, ");\n"); - - (*deps)[depIdx++] = NULL; /* End of List */ - - ArchiveEntry(fout, tinfo[i].oid, tinfo[i].typname, "TYPE", deps, - q->data, delq->data, "", tinfo[i].usename, NULL, NULL); - - - - /*** Dump Type Comments ***/ - - resetPQExpBuffer(q); - - appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo[i].typname, force_quotes)); - dumpComment(fout, q->data, tinfo[i].oid, "pg_type", 0, NULL); } - - destroyPQExpBuffer(q); - destroyPQExpBuffer(delq); } /* * dumpProcLangs * writes out to fout the queries to recreate user-defined procedural languages - * */ void -dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs, - TypeInfo *tinfo, int numTypes) +dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs) { PGresult *res; PQExpBuffer query = createPQExpBuffer(); @@ -3476,13 +3088,18 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs, int i_lanpltrusted; int i_lanplcallfoid; int i_lancompiler; - Oid lanoid; + char *lanoid; char *lanname; char *lancompiler; const char *lanplcallfoid; + const char *((*deps)[]); + int depIdx; int i, fidx; + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + appendPQExpBuffer(query, "SELECT oid, * FROM pg_language " "WHERE lanispl " "ORDER BY oid"); @@ -3504,29 +3121,38 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs, for (i = 0; i < ntups; i++) { - lanoid = atooid(PQgetvalue(res, i, i_oid)); - if (lanoid <= g_last_builtin_oid) - continue; - + lanoid = PQgetvalue(res, i, i_oid); lanplcallfoid = PQgetvalue(res, i, i_lanplcallfoid); + lanname = PQgetvalue(res, i, i_lanname); + lancompiler = PQgetvalue(res, i, i_lancompiler); - - for (fidx = 0; fidx < numFuncs; fidx++) - { - if (!strcmp(finfo[fidx].oid, lanplcallfoid)) - break; - } - if (fidx >= numFuncs) + fidx = findFuncByOid(finfo, numFuncs, lanplcallfoid); + if (fidx < 0) { write_msg(NULL, "handler procedure for procedural language %s not found\n", - PQgetvalue(res, i, i_lanname)); + lanname); exit_nicely(); } - dumpOneFunc(fout, finfo, fidx, tinfo, numTypes); + /* + * 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; - lanname = PQgetvalue(res, i, i_lanname); - lancompiler = PQgetvalue(res, i, i_lancompiler); + resetPQExpBuffer(defqry); + resetPQExpBuffer(delqry); + + /* Make a dependency to ensure function is dumped first */ + deps = malloc(sizeof(char *) * 2); + depIdx = 0; + + (*deps)[depIdx++] = strdup(lanplcallfoid); appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE "); formatStringLiteral(delqry, lanname, CONV_ALL); @@ -3541,11 +3167,12 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs, formatStringLiteral(defqry, lancompiler, CONV_ALL); appendPQExpBuffer(defqry, ";\n"); - ArchiveEntry(fout, PQgetvalue(res, i, i_oid), lanname, "PROCEDURAL LANGUAGE", - NULL, defqry->data, delqry->data, "", "", NULL, NULL); + (*deps)[depIdx++] = NULL; /* End of List */ - resetPQExpBuffer(defqry); - resetPQExpBuffer(delqry); + ArchiveEntry(fout, lanoid, lanname, + finfo[fidx].pronamespace->nspname, "", + "PROCEDURAL LANGUAGE", deps, + defqry->data, delqry->data, NULL, NULL, NULL); } PQclear(res); @@ -3558,179 +3185,199 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs, /* * dumpFuncs * writes out to fout the queries to recreate all the user-defined functions - * */ void -dumpFuncs(Archive *fout, FuncInfo *finfo, int numFuncs, - TypeInfo *tinfo, int numTypes) +dumpFuncs(Archive *fout, FuncInfo *finfo, int numFuncs) { int i; for (i = 0; i < numFuncs; i++) - dumpOneFunc(fout, finfo, i, tinfo, numTypes); + { + /* Dump only funcs in dumpable namespaces */ + if (!finfo[i].pronamespace->dump) + continue; + + dumpOneFunc(fout, &finfo[i]); + } } /* * dumpOneFunc: - * dump out only one function, the index of which is given in the third - * argument - * + * dump out only one function */ - static void -dumpOneFunc(Archive *fout, FuncInfo *finfo, int i, - TypeInfo *tinfo, int numTypes) +dumpOneFunc(Archive *fout, FuncInfo *finfo) { + PQExpBuffer query = createPQExpBuffer(); PQExpBuffer q = createPQExpBuffer(); PQExpBuffer fn = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); - PQExpBuffer fnlist = createPQExpBuffer(); PQExpBuffer asPart = createPQExpBuffer(); - char *func_lang = NULL; - PGresult *res; - int nlangs; + PGresult *res = NULL; + int ntups; int j; - int i_lanname; - char query[256]; - + char *proretset; + char *prosrc; + char *probin; + char *provolatile; + char *proimplicit; + char *proisstrict; + char *lanname; char *listSep; char *listSepComma = ","; char *listSepNone = ""; char *rettypename; - if (finfo[i].dumped) + if (finfo->dumped) goto done; - finfo[i].dumped = 1; + finfo->dumped = true; - /* becomeUser(fout, finfo[i].usename); */ + /* Set proper schema search path so type references list correctly */ + selectSourceSchema(finfo->pronamespace->nspname); - sprintf(query, "SELECT lanname FROM pg_language WHERE oid = '%u'::oid", - finfo[i].lang); - res = PQexec(g_conn, query); + /* Fetch function-specific details */ + if (g_fout->remoteVersion >= 70300) + { + appendPQExpBuffer(query, + "SELECT proretset, prosrc, probin, " + "provolatile, proimplicit, proisstrict, " + "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname " + "FROM pg_proc " + "WHERE oid = '%s'::oid", + finfo->oid); + } + else if (g_fout->remoteVersion >= 70100) + { + appendPQExpBuffer(query, + "SELECT proretset, prosrc, probin, " + "case when proiscachable then 'i' else 'v' end as provolatile, " + "'f'::boolean as proimplicit, " + "proisstrict, " + "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname " + "FROM pg_proc " + "WHERE oid = '%s'::oid", + finfo->oid); + } + else + { + appendPQExpBuffer(query, + "SELECT proretset, prosrc, probin, " + "case when proiscachable then 'i' else 'v' end as provolatile, " + "'f'::boolean as proimplicit, " + "'f'::boolean as proisstrict, " + "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname " + "FROM pg_proc " + "WHERE oid = '%s'::oid", + finfo->oid); + } + + res = PQexec(g_conn, query->data); if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) { - write_msg(NULL, "query to get name of procedural language failed: %s", PQerrorMessage(g_conn)); + write_msg(NULL, "query to obtain function information for %s failed: %s", + finfo->proname, PQerrorMessage(g_conn)); exit_nicely(); } - nlangs = PQntuples(res); - if (nlangs != 1) + /* Expecting a single result only */ + ntups = PQntuples(res); + if (ntups != 1) { - write_msg(NULL, "procedural language for function %s not found\n", finfo[i].proname); + write_msg(NULL, "Got %d rows instead of one from: %s", + ntups, query->data); exit_nicely(); } - i_lanname = PQfnumber(res, "lanname"); + proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset")); + prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc")); + probin = PQgetvalue(res, 0, PQfnumber(res, "probin")); + provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile")); + proimplicit = PQgetvalue(res, 0, PQfnumber(res, "proimplicit")); + proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict")); + lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); /* * See backend/commands/define.c for details of how the 'AS' clause is * used. */ - if (strcmp(finfo[i].probin, "-") != 0) + if (strcmp(probin, "-") != 0) { appendPQExpBuffer(asPart, "AS "); - formatStringLiteral(asPart, finfo[i].probin, CONV_ALL); - if (strcmp(finfo[i].prosrc, "-") != 0) + formatStringLiteral(asPart, probin, CONV_ALL); + if (strcmp(prosrc, "-") != 0) { appendPQExpBuffer(asPart, ", "); - formatStringLiteral(asPart, finfo[i].prosrc, PASS_LFTAB); + formatStringLiteral(asPart, prosrc, PASS_LFTAB); } } else { - if (strcmp(finfo[i].prosrc, "-") != 0) + if (strcmp(prosrc, "-") != 0) { appendPQExpBuffer(asPart, "AS "); - formatStringLiteral(asPart, finfo[i].prosrc, PASS_LFTAB); + formatStringLiteral(asPart, prosrc, PASS_LFTAB); } } - func_lang = strdup(PQgetvalue(res, 0, i_lanname)); - - PQclear(res); - - resetPQExpBuffer(fn); - appendPQExpBuffer(fn, "%s (", fmtId(finfo[i].proname, force_quotes)); - for (j = 0; j < finfo[i].nargs; j++) + appendPQExpBuffer(fn, "%s (", fmtId(finfo->proname, force_quotes)); + for (j = 0; j < finfo->nargs; j++) { char *typname; - typname = findTypeByOid(tinfo, numTypes, finfo[i].argtypes[j], zeroAsOpaque); - if (typname == NULL) - { - write_msg(NULL, "WARNING: function \"%s\" not dumped\n", - finfo[i].proname); - - write_msg(NULL, "reason: data type name of argument %d (oid %s) not found\n", - j, finfo[i].argtypes[j]); - goto done; - } - + typname = getFormattedTypeName(finfo->argtypes[j], zeroAsOpaque); appendPQExpBuffer(fn, "%s%s", (j > 0) ? "," : "", typname); - appendPQExpBuffer(fnlist, "%s%s", - (j > 0) ? "," : "", - typname); + free(typname); } appendPQExpBuffer(fn, ")"); - resetPQExpBuffer(delqry); appendPQExpBuffer(delqry, "DROP FUNCTION %s;\n", fn->data); - rettypename = findTypeByOid(tinfo, numTypes, finfo[i].prorettype, zeroAsOpaque); + rettypename = getFormattedTypeName(finfo->prorettype, zeroAsOpaque); - if (rettypename == NULL) - { - write_msg(NULL, "WARNING: function \"%s\" not dumped\n", - finfo[i].proname); - - write_msg(NULL, "reason: name of return data type (oid %s) not found\n", - finfo[i].prorettype); - goto done; - } - - resetPQExpBuffer(q); appendPQExpBuffer(q, "CREATE FUNCTION %s ", fn->data); appendPQExpBuffer(q, "RETURNS %s%s %s LANGUAGE ", - (finfo[i].retset) ? "SETOF " : "", + (proretset[0] == 't') ? "SETOF " : "", rettypename, asPart->data); - formatStringLiteral(q, func_lang, CONV_ALL); + formatStringLiteral(q, lanname, CONV_ALL); - if (finfo[i].provolatile != PROVOLATILE_VOLATILE || - finfo[i].isimplicit || - finfo[i].isstrict) /* OR in new attrs here */ + free(rettypename); + + if (provolatile[0] != PROVOLATILE_VOLATILE || + proimplicit[0] == 't' || + proisstrict[0] == 't') /* OR in new attrs here */ { appendPQExpBuffer(q, " WITH ("); listSep = listSepNone; - if (finfo[i].provolatile == PROVOLATILE_IMMUTABLE) + if (provolatile[0] == PROVOLATILE_IMMUTABLE) { appendPQExpBuffer(q, "%s isImmutable", listSep); listSep = listSepComma; } - else if (finfo[i].provolatile == PROVOLATILE_STABLE) + else if (provolatile[0] == PROVOLATILE_STABLE) { appendPQExpBuffer(q, "%s isStable", listSep); listSep = listSepComma; } - else if (finfo[i].provolatile != PROVOLATILE_VOLATILE) + else if (provolatile[0] != PROVOLATILE_VOLATILE) { write_msg(NULL, "Unexpected provolatile value for function %s\n", - finfo[i].proname); + finfo->proname); exit_nicely(); } - if (finfo[i].isimplicit) + if (proimplicit[0] == 't') { appendPQExpBuffer(q, "%s implicitCoercion", listSep); listSep = listSepComma; } - if (finfo[i].isstrict) + if (proisstrict[0] == 't') { appendPQExpBuffer(q, "%s isStrict", listSep); listSep = listSepComma; @@ -3741,331 +3388,608 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo, int i, appendPQExpBuffer(q, ";\n"); - ArchiveEntry(fout, finfo[i].oid, fn->data, "FUNCTION", NULL, q->data, delqry->data, - "", finfo[i].usename, NULL, NULL); + ArchiveEntry(fout, finfo->oid, fn->data, finfo->pronamespace->nspname, + finfo->usename, "FUNCTION", NULL, + q->data, delqry->data, + NULL, NULL, NULL); /*** Dump Function Comments ***/ resetPQExpBuffer(q); - appendPQExpBuffer(q, "FUNCTION %s ", - fmtId(finfo[i].proname, force_quotes)); - appendPQExpBuffer(q, "( %s )", fnlist->data); - dumpComment(fout, q->data, finfo[i].oid, "pg_proc", 0, NULL); + appendPQExpBuffer(q, "FUNCTION %s ", fn->data); + dumpComment(fout, q->data, + finfo->pronamespace->nspname, finfo->usename, + finfo->oid, "pg_proc", 0, NULL); done: + PQclear(res); + + destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(fn); destroyPQExpBuffer(delqry); - destroyPQExpBuffer(fnlist); destroyPQExpBuffer(asPart); - free(func_lang); } /* * dumpOprs * writes out to fout the queries to recreate all the user-defined operators - * */ void -dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators, - TypeInfo *tinfo, int numTypes) +dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators) { int i; - PQExpBuffer q = createPQExpBuffer(); - PQExpBuffer delq = createPQExpBuffer(); - PQExpBuffer leftarg = createPQExpBuffer(); - PQExpBuffer rightarg = createPQExpBuffer(); - PQExpBuffer commutator = createPQExpBuffer(); - PQExpBuffer negator = createPQExpBuffer(); - PQExpBuffer restrictor = createPQExpBuffer(); - PQExpBuffer join = createPQExpBuffer(); - PQExpBuffer sort1 = createPQExpBuffer(); - PQExpBuffer sort2 = createPQExpBuffer(); for (i = 0; i < numOperators; i++) { - char *name; - - resetPQExpBuffer(leftarg); - resetPQExpBuffer(rightarg); - resetPQExpBuffer(commutator); - resetPQExpBuffer(negator); - resetPQExpBuffer(restrictor); - resetPQExpBuffer(join); - resetPQExpBuffer(sort1); - resetPQExpBuffer(sort2); - - /* skip all the builtin oids */ - if (atooid(oprinfo[i].oid) <= g_last_builtin_oid) + /* Dump only operators in dumpable namespaces */ + if (!oprinfo[i].oprnamespace->dump) continue; /* - * some operator are invalid because they were the result of user + * some operators are invalid because they were the result of user * defining operators before commutators exist */ - if (strcmp(oprinfo[i].oprcode, "-") == 0) + if (strcmp(oprinfo[i].oprcode, "0") == 0) continue; - /* - * right unary means there's a left arg and left unary means - * there's a right arg - */ - if (strcmp(oprinfo[i].oprkind, "r") == 0 || - strcmp(oprinfo[i].oprkind, "b") == 0) - { - name = findTypeByOid(tinfo, numTypes, - oprinfo[i].oprleft, zeroAsOpaque); - if (name == NULL) - { - write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n", - oprinfo[i].oprname, oprinfo[i].oid); - write_msg(NULL, "reason: oprleft (oid %s) not found\n", - oprinfo[i].oprleft); - continue; - } - appendPQExpBuffer(leftarg, ",\n\tLEFTARG = %s ", name); - } + /* OK, dump it */ + dumpOneOpr(fout, &oprinfo[i], + oprinfo, numOperators); + } +} - if (strcmp(oprinfo[i].oprkind, "l") == 0 || - strcmp(oprinfo[i].oprkind, "b") == 0) - { - name = findTypeByOid(tinfo, numTypes, - oprinfo[i].oprright, zeroAsOpaque); - if (name == NULL) - { - write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n", - oprinfo[i].oprname, oprinfo[i].oid); - write_msg(NULL, "reason: oprright (oid %s) not found\n", - oprinfo[i].oprright); - continue; - } - appendPQExpBuffer(rightarg, ",\n\tRIGHTARG = %s ", name); - } +/* + * dumpOneOpr + * write out a single operator definition + */ +static void +dumpOneOpr(Archive *fout, OprInfo *oprinfo, + OprInfo *g_oprinfo, int numOperators) +{ + PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer q = createPQExpBuffer(); + PQExpBuffer delq = createPQExpBuffer(); + PQExpBuffer oprid = createPQExpBuffer(); + PQExpBuffer details = createPQExpBuffer(); + const char *name; + PGresult *res; + int ntups; + int i_oprkind; + int i_oprcode; + int i_oprleft; + int i_oprright; + int i_oprcom; + int i_oprnegate; + int i_oprrest; + int i_oprjoin; + int i_oprcanhash; + int i_oprlsortop; + int i_oprrsortop; + int i_oprltcmpop; + int i_oprgtcmpop; + char *oprkind; + char *oprcode; + char *oprleft; + char *oprright; + char *oprcom; + char *oprnegate; + char *oprrest; + char *oprjoin; + char *oprcanhash; + char *oprlsortop; + char *oprrsortop; + char *oprltcmpop; + char *oprgtcmpop; - if (!(strcmp(oprinfo[i].oprcom, "0") == 0)) - { - name = findOprByOid(oprinfo, numOperators, oprinfo[i].oprcom); - if (name == NULL) - { - write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n", - oprinfo[i].oprname, oprinfo[i].oid); - write_msg(NULL, "reason: oprcom (oid %s) not found\n", - oprinfo[i].oprcom); - continue; - } - appendPQExpBuffer(commutator, ",\n\tCOMMUTATOR = %s ", name); - } + /* Make sure we are in proper schema so regoperator works correctly */ + selectSourceSchema(oprinfo->oprnamespace->nspname); - if (!(strcmp(oprinfo[i].oprnegate, "0") == 0)) - { - name = findOprByOid(oprinfo, numOperators, oprinfo[i].oprnegate); - if (name == NULL) - { - write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n", - oprinfo[i].oprname, oprinfo[i].oid); - write_msg(NULL, "reason: oprnegate (oid %s) not found\n", - oprinfo[i].oprnegate); - continue; - } - appendPQExpBuffer(negator, ",\n\tNEGATOR = %s ", name); - } - - if (!(strcmp(oprinfo[i].oprrest, "-") == 0)) - appendPQExpBuffer(restrictor, ",\n\tRESTRICT = %s ", oprinfo[i].oprrest); - - if (!(strcmp(oprinfo[i].oprjoin, "-") == 0)) - appendPQExpBuffer(join, ",\n\tJOIN = %s ", oprinfo[i].oprjoin); - - if (!(strcmp(oprinfo[i].oprlsortop, "0") == 0)) - { - name = findOprByOid(oprinfo, numOperators, oprinfo[i].oprlsortop); - if (name == NULL) - { - write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n", - oprinfo[i].oprname, oprinfo[i].oid); - write_msg(NULL, "reason: oprlsortop (oid %s) not found\n", - oprinfo[i].oprlsortop); - continue; - } - appendPQExpBuffer(sort1, ",\n\tSORT1 = %s ", name); - } - - if (!(strcmp(oprinfo[i].oprrsortop, "0") == 0)) - { - name = findOprByOid(oprinfo, numOperators, oprinfo[i].oprrsortop); - if (name == NULL) - { - write_msg(NULL, "WARNING: operator \"%s\" (oid %s) not dumped\n", - oprinfo[i].oprname, oprinfo[i].oid); - write_msg(NULL, "reason: oprrsortop (oid %s) not found\n", - oprinfo[i].oprrsortop); - continue; - } - appendPQExpBuffer(sort2, ",\n\tSORT2 = %s ", name); - } - - resetPQExpBuffer(delq); - appendPQExpBuffer(delq, "DROP OPERATOR %s (%s", - oprinfo[i].oprname, - findTypeByOid(tinfo, numTypes, oprinfo[i].oprleft, - zeroAsNone)); - appendPQExpBuffer(delq, ", %s);\n", - findTypeByOid(tinfo, numTypes, oprinfo[i].oprright, - zeroAsNone)); - - resetPQExpBuffer(q); - appendPQExpBuffer(q, - "CREATE OPERATOR %s " - "(PROCEDURE = %s %s%s%s%s%s%s%s%s%s);\n", - oprinfo[i].oprname, - oprinfo[i].oprcode, - leftarg->data, - rightarg->data, - commutator->data, - negator->data, - restrictor->data, - (strcmp(oprinfo[i].oprcanhash, "t") == 0) ? ",\n\tHASHES" : "", - join->data, - sort1->data, - sort2->data); - - ArchiveEntry(fout, oprinfo[i].oid, oprinfo[i].oprname, "OPERATOR", NULL, - q->data, delq->data, "", oprinfo[i].usename, NULL, NULL); + if (g_fout->remoteVersion >= 70300) + { + appendPQExpBuffer(query, "SELECT oprkind, oprcode::regprocedure, " + "oprleft::regtype, oprright::regtype, " + "oprcom::regoperator, oprnegate::regoperator, " + "oprrest::regprocedure, oprjoin::regprocedure, " + "oprcanhash, " + "oprlsortop::regoperator, oprrsortop::regoperator, " + "oprltcmpop::regoperator, oprgtcmpop::regoperator " + "from pg_operator " + "where oid = '%s'::oid", + oprinfo->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, " + "CASE WHEN oprright = 0 THEN '-' " + "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); + } + else + { + appendPQExpBuffer(query, "SELECT oprkind, oprcode, " + "CASE WHEN oprleft = 0 THEN '-'::name " + "ELSE (select typname from pg_type where oid = oprleft) END as oprleft, " + "CASE WHEN oprright = 0 THEN '-'::name " + "ELSE (select typname from pg_type where oid = oprright) 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); } + 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(); + } + + /* 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_oprkind = PQfnumber(res, "oprkind"); + i_oprcode = PQfnumber(res, "oprcode"); + i_oprleft = PQfnumber(res, "oprleft"); + i_oprright = PQfnumber(res, "oprright"); + i_oprcom = PQfnumber(res, "oprcom"); + i_oprnegate = PQfnumber(res, "oprnegate"); + i_oprrest = PQfnumber(res, "oprrest"); + i_oprjoin = PQfnumber(res, "oprjoin"); + i_oprcanhash = PQfnumber(res, "oprcanhash"); + i_oprlsortop = PQfnumber(res, "oprlsortop"); + i_oprrsortop = PQfnumber(res, "oprrsortop"); + i_oprltcmpop = PQfnumber(res, "oprltcmpop"); + i_oprgtcmpop = PQfnumber(res, "oprgtcmpop"); + + oprkind = PQgetvalue(res, 0, i_oprkind); + oprcode = PQgetvalue(res, 0, i_oprcode); + oprleft = PQgetvalue(res, 0, i_oprleft); + oprright = PQgetvalue(res, 0, i_oprright); + oprcom = PQgetvalue(res, 0, i_oprcom); + oprnegate = PQgetvalue(res, 0, i_oprnegate); + oprrest = PQgetvalue(res, 0, i_oprrest); + oprjoin = PQgetvalue(res, 0, i_oprjoin); + oprcanhash = PQgetvalue(res, 0, i_oprcanhash); + oprlsortop = PQgetvalue(res, 0, i_oprlsortop); + oprrsortop = PQgetvalue(res, 0, i_oprrsortop); + oprltcmpop = PQgetvalue(res, 0, i_oprltcmpop); + oprgtcmpop = PQgetvalue(res, 0, i_oprgtcmpop); + + appendPQExpBuffer(details, "PROCEDURE = %s ", + convertRegProcReference(oprcode)); + + appendPQExpBuffer(oprid, "%s (", + oprinfo->oprname); + + /* + * right unary means there's a left arg and left unary means + * there's a right arg + */ + if (strcmp(oprkind, "r") == 0 || + strcmp(oprkind, "b") == 0) + { + if (g_fout->remoteVersion >= 70100) + name = oprleft; + else + name = fmtId(oprleft, force_quotes); + appendPQExpBuffer(details, ",\n\tLEFTARG = %s ", name); + appendPQExpBuffer(oprid, "%s", name); + } + else + appendPQExpBuffer(oprid, "NONE"); + + if (strcmp(oprkind, "l") == 0 || + strcmp(oprkind, "b") == 0) + { + if (g_fout->remoteVersion >= 70100) + name = oprright; + else + name = fmtId(oprright, force_quotes); + appendPQExpBuffer(details, ",\n\tRIGHTARG = %s ", name); + appendPQExpBuffer(oprid, ", %s)", name); + } + else + appendPQExpBuffer(oprid, ", NONE)"); + + name = convertOperatorReference(oprcom, g_oprinfo, numOperators); + if (name) + appendPQExpBuffer(details, ",\n\tCOMMUTATOR = %s ", name); + + name = convertOperatorReference(oprnegate, g_oprinfo, numOperators); + if (name) + appendPQExpBuffer(details, ",\n\tNEGATOR = %s ", name); + + if (strcmp(oprcanhash, "t") == 0) + appendPQExpBuffer(details, ",\n\tHASHES"); + + name = convertRegProcReference(oprrest); + if (name) + appendPQExpBuffer(details, ",\n\tRESTRICT = %s ", name); + + name = convertRegProcReference(oprjoin); + if (name) + appendPQExpBuffer(details, ",\n\tJOIN = %s ", name); + + name = convertOperatorReference(oprlsortop, g_oprinfo, numOperators); + if (name) + appendPQExpBuffer(details, ",\n\tSORT1 = %s ", name); + + name = convertOperatorReference(oprrsortop, g_oprinfo, numOperators); + if (name) + appendPQExpBuffer(details, ",\n\tSORT2 = %s ", name); + + name = convertOperatorReference(oprltcmpop, g_oprinfo, numOperators); + if (name) + appendPQExpBuffer(details, ",\n\tLTCMP = %s ", name); + + name = convertOperatorReference(oprgtcmpop, g_oprinfo, numOperators); + if (name) + appendPQExpBuffer(details, ",\n\tGTCMP = %s ", name); + + appendPQExpBuffer(delq, "DROP OPERATOR %s;\n", + oprid->data); + + appendPQExpBuffer(q, "CREATE OPERATOR %s (%s);\n", + oprinfo->oprname, details->data); + + ArchiveEntry(fout, oprinfo->oid, oprinfo->oprname, + oprinfo->oprnamespace->nspname, oprinfo->usename, + "OPERATOR", NULL, + q->data, delq->data, + NULL, NULL, NULL); + + /* + * Note: no need to dump operator comment; we expect that the comment + * is attached to the underlying function instead. (If the function + * isn't getting dumped ... you lose.) + */ + + PQclear(res); + + destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); - destroyPQExpBuffer(leftarg); - destroyPQExpBuffer(rightarg); - destroyPQExpBuffer(commutator); - destroyPQExpBuffer(negator); - destroyPQExpBuffer(restrictor); - destroyPQExpBuffer(join); - destroyPQExpBuffer(sort1); - destroyPQExpBuffer(sort2); + destroyPQExpBuffer(oprid); + destroyPQExpBuffer(details); +} + +/* + * Convert a function reference obtained from pg_operator + * + * Returns what to print, or NULL if function references is InvalidOid + * + * In 7.3 the input is a REGPROCEDURE display; we have to strip the + * argument-types part. In prior versions, the input is a REGPROC display. + */ +static const char * +convertRegProcReference(const char *proc) +{ + /* In all cases "-" means a null reference */ + if (strcmp(proc, "-") == 0) + return NULL; + + if (g_fout->remoteVersion >= 70300) + { + char *name; + char *paren; + bool inquote; + + name = strdup(proc); + /* find non-double-quoted left paren */ + inquote = false; + for (paren = name; *paren; paren++) + { + if (*paren == '(' && !inquote) + { + *paren = '\0'; + break; + } + if (*paren == '"') + inquote = !inquote; + } + return name; + } + + /* REGPROC before 7.3 does not quote its result */ + return fmtId(proc, false); +} + +/* + * Convert an operator cross-reference obtained from pg_operator + * + * 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. + */ +static const char * +convertOperatorReference(const char *opr, + OprInfo *g_oprinfo, int numOperators) +{ + char *name; + + /* In all cases "0" means a null reference */ + if (strcmp(opr, "0") == 0) + return NULL; + + if (g_fout->remoteVersion >= 70300) + { + char *paren; + bool inquote; + + name = strdup(opr); + /* find non-double-quoted left paren */ + inquote = false; + for (paren = name; *paren; paren++) + { + if (*paren == '(' && !inquote) + { + *paren = '\0'; + break; + } + if (*paren == '"') + inquote = !inquote; + } + return name; + } + + name = findOprByOid(g_oprinfo, numOperators, opr); + if (name == NULL) + write_msg(NULL, "WARNING: cannot find operator with OID %s\n", + opr); + return name; } /* * dumpAggs * writes out to fout the queries to create all the user-defined aggregates - * */ void -dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs, - TypeInfo *tinfo, int numTypes) +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]); + } +} + +/* + * dumpOneAgg + * write out a single aggregate definition + */ +static void +dumpOneAgg(Archive *fout, AggInfo *agginfo) +{ + PQExpBuffer query = createPQExpBuffer(); PQExpBuffer q = createPQExpBuffer(); PQExpBuffer delq = createPQExpBuffer(); PQExpBuffer aggSig = createPQExpBuffer(); PQExpBuffer details = createPQExpBuffer(); + PGresult *res; + int ntups; + int i_aggtransfn; + int i_aggfinalfn; + int i_aggtranstype; + int i_aggbasetype; + int i_agginitval; + int i_convertok; + const char *aggtransfn; + const char *aggfinalfn; + const char *aggtranstype; + const char *aggbasetype; + const char *agginitval; + bool convertok; + bool anybasetype; - for (i = 0; i < numAggs; i++) + /* Make sure we are in proper schema */ + selectSourceSchema(agginfo->aggnamespace->nspname); + + /* Get aggregate-specific details */ + if (g_fout->remoteVersion >= 70300) { - char *name; - - resetPQExpBuffer(details); - - /* skip all the builtin oids */ - if (oidle(atooid(agginfo[i].oid), g_last_builtin_oid)) - continue; - - resetPQExpBuffer(aggSig); - appendPQExpBuffer(aggSig, "%s(%s)", agginfo[i].aggname, - findTypeByOid(tinfo, numTypes, - agginfo[i].aggbasetype, - zeroAsStar + useBaseTypeName)); - - if (!agginfo[i].convertok) - { - write_msg(NULL, "WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n", - aggSig->data); - - resetPQExpBuffer(q); - appendPQExpBuffer(q, "-- WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n", - aggSig->data); - ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "WARNING", NULL, - q->data, "" /* Del */ , "", agginfo[i].usename, NULL, NULL); - continue; - } - - name = findTypeByOid(tinfo, numTypes, agginfo[i].aggbasetype, - zeroAsAny + useBaseTypeName); - if (name == NULL) - { - write_msg(NULL, "WARNING: aggregate function \"%s\" (oid %s) not dumped\n", - agginfo[i].aggname, agginfo[i].oid); - write_msg(NULL, "reason: aggbasetype (oid %s) not found\n", - agginfo[i].aggbasetype); - - resetPQExpBuffer(q); - appendPQExpBuffer(q, "-- WARNING: aggregate function \"%s\" (oid %s) not dumped\n", agginfo[i].aggname, agginfo[i].oid); - appendPQExpBuffer(q, "-- reason: aggbasetype (oid %s) not found\n", agginfo[i].aggbasetype); - ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "WARNING", NULL, - q->data, "" /* Del */ , "", agginfo[i].usename, NULL, NULL); - continue; - } - appendPQExpBuffer(details, "BASETYPE = %s, ", name); - - name = findTypeByOid(tinfo, numTypes, agginfo[i].aggtranstype, - zeroAsOpaque + useBaseTypeName); - if (name == NULL) - { - write_msg(NULL, "WARNING: aggregate function \"%s\" (oid %s) not dumped\n", - agginfo[i].aggname, agginfo[i].oid); - write_msg(NULL, "reason: aggtranstype (oid %s) not found\n", - agginfo[i].aggtranstype); - - resetPQExpBuffer(q); - appendPQExpBuffer(q, "-- WARNING: aggregate function \"%s\" (oid %s) not dumped\n", agginfo[i].aggname, agginfo[i].oid); - appendPQExpBuffer(q, "-- reason: aggtranstype (oid %s) not found\n", agginfo[i].aggtranstype); - ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "WARNING", NULL, - q->data, "" /* Del */ , "", agginfo[i].usename, NULL, NULL); - continue; - } - appendPQExpBuffer(details, - "SFUNC = %s, STYPE = %s", - agginfo[i].aggtransfn, name); - - if (agginfo[i].agginitval) - { - appendPQExpBuffer(details, ", INITCOND = "); - formatStringLiteral(details, agginfo[i].agginitval, CONV_ALL); - } - - if (!(strcmp(agginfo[i].aggfinalfn, "-") == 0)) - appendPQExpBuffer(details, ", FINALFUNC = %s", - agginfo[i].aggfinalfn); - - resetPQExpBuffer(delq); - appendPQExpBuffer(delq, "DROP AGGREGATE %s;\n", aggSig->data); - - resetPQExpBuffer(q); - appendPQExpBuffer(q, "CREATE AGGREGATE %s ( %s );\n", - agginfo[i].aggname, - details->data); - - ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "AGGREGATE", NULL, - q->data, delq->data, "", agginfo[i].usename, NULL, NULL); - - /*** Dump Aggregate Comments ***/ - - resetPQExpBuffer(q); - appendPQExpBuffer(q, "AGGREGATE %s", aggSig->data); - if (g_fout->remoteVersion < 70300) - dumpComment(fout, q->data, agginfo[i].oid, "pg_aggregate", - 0, NULL); - else - dumpComment(fout, q->data, agginfo[i].oid, "pg_proc", - 0, NULL); + appendPQExpBuffer(query, "SELECT aggtransfn, " + "aggfinalfn, aggtranstype::regtype, " + "proargtypes[0]::regtype as aggbasetype, " + "agginitval, " + "'t'::boolean as convertok " + "from pg_aggregate a, pg_proc p " + "where a.aggfnoid = p.oid " + "and p.oid = '%s'::oid", + agginfo->oid); + } + else if (g_fout->remoteVersion >= 70100) + { + appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, " + "format_type(aggtranstype, NULL) as aggtranstype, " + "CASE WHEN aggbasetype = 0 THEN '-' " + "ELSE format_type(aggbasetype, NULL) END as aggbasetype, " + "agginitval, 't'::boolean as convertok " + "from pg_aggregate " + "where oid = '%s'::oid", + agginfo->oid); + } + else + { + appendPQExpBuffer(query, "SELECT aggtransfn1 as aggtransfn, " + "aggfinalfn, " + "(select typname from pg_type where oid = aggtranstype1) as aggtranstype, " + "CASE WHEN aggbasetype = 0 THEN '-'::name " + "ELSE (select typname from pg_type where oid = aggbasetype) END as aggbasetype, " + "agginitval1 as agginitval, " + "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) as convertok " + "from pg_aggregate " + "where oid = '%s'::oid", + agginfo->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(); + } + + /* 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_aggtransfn = PQfnumber(res, "aggtransfn"); + i_aggfinalfn = PQfnumber(res, "aggfinalfn"); + i_aggtranstype = PQfnumber(res, "aggtranstype"); + i_aggbasetype = PQfnumber(res, "aggbasetype"); + i_agginitval = PQfnumber(res, "agginitval"); + i_convertok = PQfnumber(res, "convertok"); + + aggtransfn = PQgetvalue(res, 0, i_aggtransfn); + aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn); + aggtranstype = PQgetvalue(res, 0, i_aggtranstype); + aggbasetype = PQgetvalue(res, 0, i_aggbasetype); + agginitval = PQgetvalue(res, 0, i_agginitval); + convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't'); + + anybasetype = (strcmp(aggbasetype, "-") == 0); + + appendPQExpBuffer(aggSig, "%s", + fmtId(agginfo->aggname, force_quotes)); + + /* If using regtype or format_type, name is already quoted */ + if (g_fout->remoteVersion >= 70100) + { + if (anybasetype) + appendPQExpBuffer(aggSig, "(*)"); + else + appendPQExpBuffer(aggSig, "(%s)", aggbasetype); + } + else + { + if (anybasetype) + appendPQExpBuffer(aggSig, "(*)"); + else + appendPQExpBuffer(aggSig, "(%s)", + fmtId(aggbasetype, force_quotes)); + } + + if (!convertok) + { + write_msg(NULL, "WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n", + aggSig->data); + + appendPQExpBuffer(q, "-- WARNING: aggregate function %s could not be dumped correctly for this database version; ignored\n", + aggSig->data); + ArchiveEntry(fout, agginfo->oid, aggSig->data, + 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, SFUNC = %s, STYPE = %s", + anybasetype ? "'any'" : aggbasetype, + aggtransfn, + aggtranstype); + } + else if (g_fout->remoteVersion >= 70100) + { + /* format_type quotes, regproc does not */ + appendPQExpBuffer(details, "BASETYPE = %s, SFUNC = %s, STYPE = %s", + anybasetype ? "'any'" : aggbasetype, + fmtId(aggtransfn, force_quotes), + aggtranstype); + } + else + { + /* need quotes all around */ + appendPQExpBuffer(details, "BASETYPE = %s, ", + anybasetype ? "'any'" : + fmtId(aggbasetype, force_quotes)); + appendPQExpBuffer(details, "SFUNC = %s, ", + fmtId(aggtransfn, force_quotes)); + appendPQExpBuffer(details, "STYPE = %s", + fmtId(aggtranstype, force_quotes)); + } + + if (!PQgetisnull(res, 0, i_agginitval)) + { + appendPQExpBuffer(details, ", INITCOND = "); + formatStringLiteral(details, agginitval, CONV_ALL); + } + + if (strcmp(aggfinalfn, "-") != 0) + { + appendPQExpBuffer(details, ", FINALFUNC = %s", + aggfinalfn); + } + + appendPQExpBuffer(delq, "DROP AGGREGATE %s;\n", aggSig->data); + + appendPQExpBuffer(q, "CREATE AGGREGATE %s ( %s );\n", + fmtId(agginfo->aggname, force_quotes), + details->data); + + ArchiveEntry(fout, agginfo->oid, aggSig->data, + agginfo->aggnamespace->nspname, agginfo->usename, + "AGGREGATE", NULL, + q->data, delq->data, + NULL, NULL, NULL); + + /*** Dump Aggregate Comments ***/ + + resetPQExpBuffer(q); + appendPQExpBuffer(q, "AGGREGATE %s", aggSig->data); + 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); + + PQclear(res); + + destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); destroyPQExpBuffer(aggSig); destroyPQExpBuffer(details); } + /* * These are some support functions to fix the acl problem of pg_dump * @@ -4237,312 +4161,347 @@ dumpACL(Archive *fout, TableInfo *tbinfo) free(aclbuf); - if (tbinfo->viewdef != NULL) + if (tbinfo->viewoid != NULL) objoid = tbinfo->viewoid; else objoid = tbinfo->oid; - ArchiveEntry(fout, objoid, tbinfo->relname, "ACL", - NULL, sql->data, "", "", "", NULL, NULL); + ArchiveEntry(fout, objoid, tbinfo->relname, + tbinfo->relnamespace->nspname, + tbinfo->usename, "ACL", NULL, + sql->data, "", NULL, NULL, NULL); destroyPQExpBuffer(sql); } -static void -_dumpTableAttr70(TableInfo *tblinfo, int i, int j, PQExpBuffer q) +/* + * dumpTables: + * write out to fout the declarations (not data) of all user-defined tables + */ +void +dumpTables(Archive *fout, TableInfo *tblinfo, int numTables, + const bool aclsSkip, const bool schemaOnly, const bool dataOnly) { - int32 tmp_typmod; - int precision; - int scale; + int i; - /* Show lengths on bpchar and varchar */ - if (!strcmp(tblinfo[i].typnames[j], "bpchar")) + /* Dump sequences first, in case they are referenced in table defn's */ + for (i = 0; i < numTables; i++) { - int len = (tblinfo[i].atttypmod[j] - VARHDRSZ); + TableInfo *tbinfo = &tblinfo[i]; - appendPQExpBuffer(q, "character"); - if (len > 1) - appendPQExpBuffer(q, "(%d)", - tblinfo[i].atttypmod[j] - VARHDRSZ); - } - else if (!strcmp(tblinfo[i].typnames[j], "varchar")) - { - appendPQExpBuffer(q, "character varying"); - if (tblinfo[i].atttypmod[j] != -1) + if (tbinfo->relkind != RELKIND_SEQUENCE) + continue; + if (tbinfo->dump) { - appendPQExpBuffer(q, "(%d)", - tblinfo[i].atttypmod[j] - VARHDRSZ); - } - } - else if (!strcmp(tblinfo[i].typnames[j], "numeric")) - { - appendPQExpBuffer(q, "numeric"); - if (tblinfo[i].atttypmod[j] != -1) - { - tmp_typmod = tblinfo[i].atttypmod[j] - VARHDRSZ; - precision = (tmp_typmod >> 16) & 0xffff; - scale = tmp_typmod & 0xffff; - appendPQExpBuffer(q, "(%d,%d)", - precision, scale); + dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly); + if (!dataOnly && !aclsSkip) + dumpACL(fout, tbinfo); } } - /* - * 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(tblinfo[i].typnames[j], "char")) + if (!dataOnly) { - appendPQExpBuffer(q, "%s", - fmtId(tblinfo[i].typnames[j], true)); - } - else - { - appendPQExpBuffer(q, "%s", - fmtId(tblinfo[i].typnames[j], false)); + 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) + dumpACL(fout, tbinfo); + } + } } } /* - * dumpTables: - * write out to fout all the user-define tables + * dumpOneTable + * write the declaration (not data) of one user-defined table or view */ - -void -dumpTables(Archive *fout, TableInfo *tblinfo, int numTables, - const char *tablename, const bool aclsSkip, - const bool schemaOnly, const bool dataOnly) +static void +dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo) { - int i, - j, - k; + PQExpBuffer query = createPQExpBuffer(); PQExpBuffer q = createPQExpBuffer(); PQExpBuffer delq = createPQExpBuffer(); - char *serialSeq = NULL; /* implicit sequence name created - * by SERIAL datatype */ - const char *serialSeqSuffix = "_id_seq"; /* suffix for implicit - * SERIAL sequences */ - char **parentRels; /* list of names of parent relations */ + PGresult *res; int numParents; + int *parentIndexes; int actual_atts; /* number of attrs in this CREATE statment */ char *reltypename; char *objoid; const char *((*commentDeps)[]); + int j, + k; - /* First - dump SEQUENCEs */ - if (tablename && strlen(tablename) > 0) + /* Make sure we are in proper schema */ + selectSourceSchema(tbinfo->relnamespace->nspname); + + /* Is it a table or a view? */ + if (tbinfo->relkind == RELKIND_VIEW) { - /* XXX this code only works for serial columns named "id" */ - /* We really need dependency analysis! */ - serialSeq = malloc(strlen(tablename) + strlen(serialSeqSuffix) + 1); - strcpy(serialSeq, tablename); - strcat(serialSeq, serialSeqSuffix); - } - for (i = 0; i < numTables; i++) - { - if (tblinfo[i].relkind != RELKIND_SEQUENCE) - continue; - if (!tablename || (!strcmp(tblinfo[i].relname, tablename)) - || (serialSeq && !strcmp(tblinfo[i].relname, serialSeq))) + char *viewdef; + + reltypename = "VIEW"; + + /* Fetch the view definition */ + if (g_fout->remoteVersion >= 70300) { - /* becomeUser(fout, tblinfo[i].usename); */ - dumpSequence(fout, tblinfo[i], schemaOnly, dataOnly); - if (!aclsSkip) - dumpACL(fout, &tblinfo[i]); + /* Beginning in 7.3, viewname is not unique; use OID */ + appendPQExpBuffer(query, "SELECT pg_get_viewdef(ev_class) as viewdef, " + "oid as view_oid" + " from pg_rewrite where" + " ev_class = '%s'::oid and" + " rulename = '_RETURN';", + tbinfo->oid); } - } - if (serialSeq) - free(serialSeq); - - for (i = 0; i < numTables; i++) - { - if (tblinfo[i].relkind == RELKIND_SEQUENCE) /* already dumped */ - continue; - - if (!tablename || (!strcmp(tblinfo[i].relname, tablename)) || (strlen(tablename) == 0)) + else { + appendPQExpBuffer(query, "SELECT definition as viewdef, " + "(select oid from pg_rewrite where " + " rulename=('_RET' || viewname)::name) as view_oid" + " from pg_views where viewname = "); + formatStringLiteral(query, tbinfo->relname, CONV_ALL); + appendPQExpBuffer(query, ";"); + } - resetPQExpBuffer(delq); - resetPQExpBuffer(q); + 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(); + } - /* Use the view definition if there is one */ - if (tblinfo[i].viewdef != NULL) - { - reltypename = "VIEW"; - objoid = tblinfo[i].viewoid; - appendPQExpBuffer(delq, "DROP VIEW %s;\n", fmtId(tblinfo[i].relname, force_quotes)); - appendPQExpBuffer(q, "CREATE VIEW %s as %s\n", fmtId(tblinfo[i].relname, force_quotes), tblinfo[i].viewdef); - - /* - * 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 < tblinfo[i].numatts; j++) - { - if (tblinfo[i].adef_expr[j] != NULL && tblinfo[i].inhAttrDef[j] == 0) { - appendPQExpBuffer(q, "ALTER TABLE %s ", fmtId(tblinfo[i].relname, force_quotes)); - appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n", - fmtId(tblinfo[i].attnames[j], force_quotes), - tblinfo[i].adef_expr[j]); - } - } - - commentDeps = malloc(sizeof(char *) * 2); - (*commentDeps)[0] = strdup(objoid); - (*commentDeps)[1] = NULL; /* end of list */ - } + if (PQntuples(res) != 1) + { + if (PQntuples(res) < 1) + write_msg(NULL, "query to obtain definition of view \"%s\" returned no data\n", + tbinfo->relname); else - { - reltypename = "TABLE"; - objoid = tblinfo[i].oid; - commentDeps = NULL; - parentRels = tblinfo[i].parentRels; - numParents = tblinfo[i].numParents; - - appendPQExpBuffer(delq, "DROP TABLE %s;\n", fmtId(tblinfo[i].relname, force_quotes)); - - appendPQExpBuffer(q, "CREATE TABLE %s (\n\t", fmtId(tblinfo[i].relname, force_quotes)); - actual_atts = 0; - for (j = 0; j < tblinfo[i].numatts; j++) - { - /* Is this one of the table's own attrs ? */ - if (tblinfo[i].inhAttrs[j] == 0) - { - /* Format properly if not first attr */ - if (actual_atts > 0) - appendPQExpBuffer(q, ",\n\t"); - - /* Attr name & type */ - appendPQExpBuffer(q, "%s ", fmtId(tblinfo[i].attnames[j], force_quotes)); - - if (g_fout->remoteVersion >= 70100) - appendPQExpBuffer(q, "%s", tblinfo[i].atttypedefns[j]); - else - _dumpTableAttr70(tblinfo, i, j, q); - - /* Default value */ - if (tblinfo[i].adef_expr[j] != NULL && tblinfo[i].inhAttrDef[j] == 0) - appendPQExpBuffer(q, " DEFAULT %s", - tblinfo[i].adef_expr[j]); - - /* Not Null constraint */ - if (tblinfo[i].notnull[j] && tblinfo[i].inhNotNull[j] == 0) - appendPQExpBuffer(q, " NOT NULL"); - - actual_atts++; - } - } - - - - /* Put the CONSTRAINTS inside the table def */ - for (k = 0; k < tblinfo[i].ncheck; k++) - { - if (actual_atts + k > 0) - appendPQExpBuffer(q, ",\n\t"); - - appendPQExpBuffer(q, "%s", - tblinfo[i].check_expr[k]); - } - - /* - * 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 is 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++) - { - appendPQExpBuffer(q, "%s%s", - (k > 0) ? ", " : "", - fmtId(parentRels[k], force_quotes)); - } - appendPQExpBuffer(q, ")"); - } - - if (!tblinfo[i].hasoids) - appendPQExpBuffer(q, " WITHOUT OIDS"); - - appendPQExpBuffer(q, ";\n"); - } - - if (!dataOnly) - { - - ArchiveEntry(fout, objoid, tblinfo[i].relname, - reltypename, NULL, q->data, delq->data, "", tblinfo[i].usename, - NULL, NULL); - - if (!aclsSkip) - dumpACL(fout, &tblinfo[i]); - - } - - /* Dump Field Comments */ - - for (j = 0; j < tblinfo[i].numatts; j++) - { - resetPQExpBuffer(q); - appendPQExpBuffer(q, "COLUMN %s", fmtId(tblinfo[i].relname, force_quotes)); - appendPQExpBuffer(q, "."); - appendPQExpBuffer(q, "%s", fmtId(tblinfo[i].attnames[j], force_quotes)); - dumpComment(fout, q->data, tblinfo[i].oid, - "pg_class", j + 1, commentDeps); - } - - /* Dump Table Comments */ - - resetPQExpBuffer(q); - appendPQExpBuffer(q, "%s %s", reltypename, fmtId(tblinfo[i].relname, force_quotes)); - dumpComment(fout, q->data, tblinfo[i].oid, - "pg_class", 0, commentDeps); - + 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); + exit_nicely(); + } + + viewdef = PQgetvalue(res, 0, 0); + + if (strlen(viewdef) == 0) + { + write_msg(NULL, "definition of view \"%s\" appears to be empty (length zero)\n", + tbinfo->relname); + 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; + + appendPQExpBuffer(delq, "DROP VIEW %s;\n", + fmtId(tbinfo->relname, force_quotes)); + appendPQExpBuffer(q, "CREATE VIEW %s AS %s\n", + fmtId(tbinfo->relname, force_quotes), 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, force_quotes)); + appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n", + fmtId(tbinfo->attnames[j], force_quotes), + 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; + + appendPQExpBuffer(delq, "DROP TABLE %s;\n", + fmtId(tbinfo->relname, force_quotes)); + + appendPQExpBuffer(q, "CREATE TABLE %s (\n\t", + fmtId(tbinfo->relname, force_quotes)); + actual_atts = 0; + for (j = 0; j < tbinfo->numatts; j++) + { + /* Is this one of the table's own attrs ? */ + if (!tbinfo->inhAttrs[j]) + { + /* Format properly if not first attr */ + if (actual_atts > 0) + appendPQExpBuffer(q, ",\n\t"); + + /* Attr name & type */ + appendPQExpBuffer(q, "%s ", + fmtId(tbinfo->attnames[j], force_quotes)); + + /* If no format_type, fake it */ + if (g_fout->remoteVersion >= 70100) + appendPQExpBuffer(q, "%s", tbinfo->atttypnames[j]); + else + appendPQExpBuffer(q, "%s", + myFormatType(tbinfo->atttypnames[j], + tbinfo->atttypmod[j])); + + /* Default value */ + if (tbinfo->adef_expr[j] != NULL && !tbinfo->inhAttrDef[j]) + appendPQExpBuffer(q, " DEFAULT %s", + tbinfo->adef_expr[j]); + + /* Not Null constraint */ + if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j]) + appendPQExpBuffer(q, " NOT NULL"); + + actual_atts++; + } + } + + /* + * 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. + */ + if (tbinfo->ncheck > 0) + { + PGresult *res2; + int i_rcname, + i_rcsrc; + int ntups2; + + if (g_verbose) + write_msg(NULL, "finding CHECK constraints for table %s\n", + tbinfo->relname); + + resetPQExpBuffer(query); + appendPQExpBuffer(query, "SELECT rcname, rcsrc from pg_relcheck " + " where rcrelid = '%s'::oid " + " and not exists " + " (select 1 from pg_relcheck as c, " + " pg_inherits as i " + " where i.inhrelid = pg_relcheck.rcrelid " + " and (c.rcname = pg_relcheck.rcname " + " or (c.rcname[0] = '$' " + " and pg_relcheck.rcname[0] = '$')" + " )" + " and c.rcsrc = pg_relcheck.rcsrc " + " and c.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(); + } + + i_rcname = PQfnumber(res2, "rcname"); + i_rcsrc = PQfnumber(res2, "rcsrc"); + + for (j = 0; j < ntups2; j++) + { + const char *name = PQgetvalue(res2, j, i_rcname); + const char *expr = PQgetvalue(res2, j, i_rcsrc); + + if (actual_atts + j > 0) + appendPQExpBuffer(q, ",\n\t"); + + if (name[0] != '$') + appendPQExpBuffer(q, "CONSTRAINT %s ", + fmtId(name, force_quotes)); + appendPQExpBuffer(q, "CHECK (%s)", expr); + } + PQclear(res2); + } + + /* + * 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]]; + + if (k > 0) + appendPQExpBuffer(q, ", "); + if (parentRel->relnamespace != tbinfo->relnamespace) + appendPQExpBuffer(q, "%s.", + fmtId(parentRel->relnamespace->nspname, + force_quotes)); + appendPQExpBuffer(q, "%s", + fmtId(parentRel->relname, force_quotes)); + } + appendPQExpBuffer(q, ")"); + } + + if (!tbinfo->hasoids) + appendPQExpBuffer(q, " WITHOUT OIDS"); + + appendPQExpBuffer(q, ";\n"); } + ArchiveEntry(fout, objoid, tbinfo->relname, + tbinfo->relnamespace->nspname, tbinfo->usename, + reltypename, NULL, q->data, delq->data, + NULL, NULL, NULL); + + /* Dump Table Comments */ + dumpTableComment(fout, tbinfo, reltypename, commentDeps); + + destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); } -static PQExpBuffer -getPKconstraint(TableInfo *tblInfo, IndInfo *indInfo) -{ - PQExpBuffer pkBuf = createPQExpBuffer(); - int k; - - appendPQExpBuffer(pkBuf, "Constraint %s Primary Key (", - tblInfo->primary_key_name); - - for (k = 0; k < indInfo->indnkeys; k++) - { - int indkey; - const char *attname; - - indkey = atoi(indInfo->indkey[k]); - if (indkey == InvalidAttrNumber) - break; - attname = getAttrName(indkey, tblInfo); - - appendPQExpBuffer(pkBuf, "%s%s", - (k == 0) ? "" : ", ", - fmtId(attname, force_quotes)); - } - - appendPQExpBuffer(pkBuf, ")"); - - return pkBuf; -} - /* * getAttrName: extract the correct name for an attribute * @@ -4580,150 +4539,155 @@ getAttrName(int attrnum, TableInfo *tblInfo) /* * dumpIndexes: - * write out to fout all the user-defined indexes + * write out to fout all the user-defined indexes for dumpable tables */ void -dumpIndexes(Archive *fout, IndInfo *indinfo, int numIndexes, - TableInfo *tblinfo, int numTables, const char *tablename) +dumpIndexes(Archive *fout, TableInfo *tblinfo, int numTables) { - int i; - int tableInd; + int i, + j; + PQExpBuffer query = createPQExpBuffer(); PQExpBuffer q = createPQExpBuffer(); PQExpBuffer delq = createPQExpBuffer(); - PQExpBuffer id1 = createPQExpBuffer(); + PGresult *res; + int ntups; + int i_indexreloid; + int i_indexrelname; + int i_indexdef; + int i_indisprimary; + int i_indkey; + int i_indnkeys; - for (i = 0; i < numIndexes; i++) + for (i = 0; i < numTables; i++) { - if (tablename && tablename[0] && - (strcmp(indinfo[i].indrelname, tablename) != 0)) + TableInfo *tbinfo = &tblinfo[i]; + + /* Only plain tables have indexes */ + if (tbinfo->relkind != RELKIND_RELATION || !tbinfo->hasindex) continue; - tableInd = findTableByName(tblinfo, numTables, - indinfo[i].indrelname); - if (tableInd < 0) + if (!tbinfo->dump) + continue; + + /* Make sure we are in proper schema so indexdef is right */ + selectSourceSchema(tbinfo->relnamespace->nspname); + + resetPQExpBuffer(query); + appendPQExpBuffer(query, + "SELECT i.indexrelid as indexreloid, " + "t.relname as indexrelname, " + "pg_get_indexdef(i.indexrelid) as indexdef, " + "i.indisprimary, i.indkey, " + "t.relnatts as indnkeys " + "FROM pg_index i, pg_class t " + "WHERE t.oid = i.indexrelid " + "AND i.indrelid = '%s'::oid " + "ORDER BY indexrelname", + tbinfo->oid); + + res = PQexec(g_conn, query->data); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) { - write_msg(NULL, "dumpIndexes(): failed sanity check, table %s was not found\n", - indinfo[i].indrelname); + write_msg(NULL, "query to obtain list of indexes failed: %s", PQerrorMessage(g_conn)); exit_nicely(); } - /* Handle PK indexes */ - if (strcmp(indinfo[i].indisprimary, "t") == 0) + ntups = PQntuples(res); + + i_indexreloid = PQfnumber(res, "indexreloid"); + i_indexrelname = PQfnumber(res, "indexrelname"); + i_indexdef = PQfnumber(res, "indexdef"); + i_indisprimary = PQfnumber(res, "indisprimary"); + i_indkey = PQfnumber(res, "indkey"); + i_indnkeys = PQfnumber(res, "indnkeys"); + + for (j = 0; j < ntups; j++) { - PQExpBuffer consDef = getPKconstraint(&tblinfo[tableInd], &indinfo[i]); + const char *indexreloid = PQgetvalue(res, j, i_indexreloid); + const char *indexrelname = PQgetvalue(res, j, i_indexrelname); + const char *indexdef = PQgetvalue(res, j, i_indexdef); + const char *indisprimary = PQgetvalue(res, j, i_indisprimary); resetPQExpBuffer(q); + resetPQExpBuffer(delq); - appendPQExpBuffer(q, "Alter Table %s Add %s;", - fmtId(tblinfo[tableInd].relname, force_quotes), - consDef->data); + if (strcmp(indisprimary, "t") == 0) + { + /* Handle PK indexes specially */ + int indnkeys = atoi(PQgetvalue(res, j, i_indnkeys)); + char **indkeys = (char **) malloc(indnkeys * sizeof(char *)); + int k; - ArchiveEntry(fout, indinfo[i].indexreloid, tblinfo[tableInd].primary_key_name, - "CONSTRAINT", NULL, q->data, "", - "", tblinfo[tableInd].usename, NULL, NULL); + parseNumericArray(PQgetvalue(res, j, i_indkey), + indkeys, indnkeys); - destroyPQExpBuffer(consDef); + appendPQExpBuffer(q, "ALTER TABLE %s ADD ", + fmtId(tbinfo->relname, force_quotes)); + appendPQExpBuffer(q, "CONSTRAINT %s PRIMARY KEY (", + fmtId(indexrelname, force_quotes)); - /* - * Don't need to do anything else for this system-generated - * index - */ - continue; + for (k = 0; k < indnkeys; k++) + { + int indkey = atoi(indkeys[k]); + const char *attname; + + if (indkey == InvalidAttrNumber) + break; + attname = getAttrName(indkey, tbinfo); + + appendPQExpBuffer(q, "%s%s", + (k == 0) ? "" : ", ", + fmtId(attname, force_quotes)); + } + + appendPQExpBuffer(q, ");\n"); + + ArchiveEntry(fout, indexreloid, + indexrelname, + tbinfo->relnamespace->nspname, + tbinfo->usename, + "CONSTRAINT", NULL, + q->data, "", + NULL, NULL, NULL); + + free(indkeys); + } + else + { + /* Plain secondary index */ + appendPQExpBuffer(q, "%s;\n", indexdef); + + appendPQExpBuffer(delq, "DROP INDEX %s;\n", + fmtId(indexrelname, force_quotes)); + + ArchiveEntry(fout, indexreloid, + indexrelname, + tbinfo->relnamespace->nspname, + tbinfo->usename, + "INDEX", NULL, + q->data, delq->data, + NULL, NULL, NULL); + } + + /* Dump Index Comments */ + resetPQExpBuffer(q); + appendPQExpBuffer(q, "INDEX %s", + fmtId(indexrelname, force_quotes)); + dumpComment(fout, q->data, + tbinfo->relnamespace->nspname, + tbinfo->usename, + indexreloid, "pg_class", 0, NULL); } - resetPQExpBuffer(id1); - appendPQExpBuffer(id1, fmtId(indinfo[i].indexrelname, force_quotes)); - - resetPQExpBuffer(q); - appendPQExpBuffer(q, "%s;\n", indinfo[i].indexdef); - - resetPQExpBuffer(delq); - appendPQExpBuffer(delq, "DROP INDEX %s;\n", id1->data); - - /* - * We make the index belong to the owner of its table, which is - * not necessarily right but should answer 99% of the time. Would - * have to add owner name to IndInfo to do it right. - */ - ArchiveEntry(fout, indinfo[i].indexreloid, id1->data, - "INDEX", NULL, q->data, delq->data, - "", tblinfo[tableInd].usename, NULL, NULL); - - /* Dump Index Comments */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "INDEX %s", id1->data); - dumpComment(fout, q->data, indinfo[i].indexreloid, - "pg_class", 0, NULL); + PQclear(res); } + destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); - destroyPQExpBuffer(id1); } -/* - * dumpTuples - * prints out the tuples in ASCII representation. The output is a valid - * input to COPY FROM stdin. - * - * We only need to do this for POSTGRES 4.2 databases since the - * COPY TO statment doesn't escape newlines properly. It's been fixed - * in PostgreSQL. - * - * the attrmap passed in tells how to map the attributes copied in to the - * attributes copied out - */ -#ifdef NOT_USED -void -dumpTuples(PGresult *res, FILE *fout, int *attrmap) -{ - int j, - k; - int m, - n; - char **outVals = NULL; /* values to copy out */ - - n = PQntuples(res); - m = PQnfields(res); - - if (m > 0) - { - /* - * Print out the tuples but only print tuples with at least 1 - * field. - */ - outVals = (char **) malloc(m * sizeof(char *)); - - for (j = 0; j < n; j++) - { - for (k = 0; k < m; k++) - outVals[attrmap[k]] = PQgetvalue(res, j, k); - for (k = 0; k < m; k++) - { - char *pval = outVals[k]; - - if (k != 0) - fputc('\t', fout); /* delimiter for attribute */ - - if (pval) - { - while (*pval != '\0') - { - /* escape tabs, newlines and backslashes */ - if (*pval == '\t' || *pval == '\n' || *pval == '\\') - fputc('\\', fout); - fputc(*pval, fout); - pval++; - } - } - } - fputc('\n', fout); /* delimiter for a tuple */ - } - free(outVals); - } -} -#endif - /* * setMaxOid - * find the maximum oid and generate a COPY statement to set it @@ -4776,7 +4740,10 @@ setMaxOid(Archive *fout) "DROP TABLE pgdump_oid;\n", max_oid); - ArchiveEntry(fout, "0", "Max OID", "", NULL, sql, "", "", "", NULL, NULL); + ArchiveEntry(fout, "0", "Max OID", NULL, "", + "", NULL, + sql, "", + NULL, NULL, NULL); } /* @@ -4860,7 +4827,8 @@ findLastBuiltinOid_V70(void) } static void -dumpSequence(Archive *fout, TableInfo tbinfo, const bool schemaOnly, const bool dataOnly) +dumpOneSequence(Archive *fout, TableInfo *tbinfo, + const bool schemaOnly, const bool dataOnly) { PGresult *res; char *last, @@ -4873,31 +4841,34 @@ dumpSequence(Archive *fout, TableInfo tbinfo, const bool schemaOnly, const bool PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); + /* Make sure we are in proper schema */ + selectSourceSchema(tbinfo->relnamespace->nspname); + appendPQExpBuffer(query, "SELECT sequence_name, last_value, increment_by, max_value, " "min_value, cache_value, is_cycled, is_called from %s", - fmtId(tbinfo.relname, force_quotes)); + fmtId(tbinfo->relname, force_quotes)); 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)); + write_msg(NULL, "query to get data of sequence \"%s\" failed: %s", tbinfo->relname, PQerrorMessage(g_conn)); exit_nicely(); } 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->relname, 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->relname) != 0) { write_msg(NULL, "query to get data of sequence \"%s\" returned name \"%s\"\n", - tbinfo.relname, PQgetvalue(res, 0, 0)); + tbinfo->relname, PQgetvalue(res, 0, 0)); exit_nicely(); } #endif @@ -4923,33 +4894,37 @@ dumpSequence(Archive *fout, TableInfo tbinfo, const bool schemaOnly, const bool { resetPQExpBuffer(delqry); appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n", - fmtId(tbinfo.relname, force_quotes)); + fmtId(tbinfo->relname, force_quotes)); resetPQExpBuffer(query); appendPQExpBuffer(query, "CREATE SEQUENCE %s start %s increment %s " "maxvalue %s minvalue %s cache %s%s;\n", - fmtId(tbinfo.relname, force_quotes), + fmtId(tbinfo->relname, force_quotes), (called ? minv : last), incby, maxv, minv, cache, (cycled ? " cycle" : "")); - ArchiveEntry(fout, tbinfo.oid, tbinfo.relname, "SEQUENCE", NULL, - query->data, delqry->data, "", tbinfo.usename, - NULL, NULL); + ArchiveEntry(fout, tbinfo->oid, tbinfo->relname, + tbinfo->relnamespace->nspname, tbinfo->usename, + "SEQUENCE", NULL, + query->data, delqry->data, + NULL, NULL, NULL); } if (!schemaOnly) { resetPQExpBuffer(query); appendPQExpBuffer(query, "SELECT setval ("); - formatStringLiteral(query, fmtId(tbinfo.relname, force_quotes), CONV_ALL); + formatStringLiteral(query, fmtId(tbinfo->relname, force_quotes), CONV_ALL); appendPQExpBuffer(query, ", %s, %s);\n", last, (called ? "true" : "false")); - ArchiveEntry(fout, tbinfo.oid, tbinfo.relname, "SEQUENCE SET", NULL, - query->data, "" /* Del */ , "", tbinfo.usename, - NULL, NULL); + ArchiveEntry(fout, tbinfo->oid, tbinfo->relname, + tbinfo->relnamespace->nspname, tbinfo->usename, + "SEQUENCE SET", NULL, + query->data, "" /* Del */ , + NULL, NULL, NULL); } if (!dataOnly) @@ -4957,9 +4932,10 @@ dumpSequence(Archive *fout, TableInfo tbinfo, const bool schemaOnly, const bool /* Dump Sequence Comments */ resetPQExpBuffer(query); - appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo.relname, force_quotes)); - dumpComment(fout, query->data, tbinfo.oid, - "pg_class", 0, NULL); + appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->relname, force_quotes)); + dumpComment(fout, query->data, + tbinfo->relnamespace->nspname, tbinfo->usename, + tbinfo->oid, "pg_class", 0, NULL); } PQclear(res); @@ -4970,45 +4946,286 @@ dumpSequence(Archive *fout, TableInfo tbinfo, const bool schemaOnly, const bool static void -dumpTriggers(Archive *fout, const char *tablename, - TableInfo *tblinfo, int numTables) +dumpTriggers(Archive *fout, TableInfo *tblinfo, int numTables) { int i, j; - - if (g_verbose) - write_msg(NULL, "dumping out triggers\n"); + 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; for (i = 0; i < numTables; i++) { - if (tablename && (strcmp(tblinfo[i].relname, tablename) != 0) && (strlen(tablename) > 0)) + TableInfo *tbinfo = &tblinfo[i]; + + if (tbinfo->ntrig == 0 || !tbinfo->dump) continue; - for (j = 0; j < tblinfo[i].ntrig; j++) + if (g_verbose) + write_msg(NULL, "dumping triggers for table %s\n", + tbinfo->relname); + + /* select table schema to ensure regproc name is qualified if needed */ + selectSourceSchema(tbinfo->relnamespace->nspname); + + resetPQExpBuffer(query); + if (g_fout->remoteVersion >= 70300) { - ArchiveEntry(fout, tblinfo[i].triggers[j].oid, tblinfo[i].triggers[j].tgname, - "TRIGGER", NULL, tblinfo[i].triggers[j].tgsrc, "", "", - tblinfo[i].usename, NULL, NULL); - dumpComment(fout, tblinfo[i].triggers[j].tgcomment, tblinfo[i].triggers[j].oid, - "pg_trigger", 0, NULL); + appendPQExpBuffer(query, + "SELECT tgname, tgfoid::regproc as tgfname, " + "tgtype, tgnargs, tgargs, " + "tgisconstraint, tgconstrname, tgdeferrable, " + "tgconstrrelid, tginitdeferred, oid, " + "tgconstrrelid::regclass as tgconstrrelname " + "from pg_trigger " + "where tgrelid = '%s'::oid", + 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) + { + write_msg(NULL, "query to obtain list of triggers failed: %s", PQerrorMessage(g_conn)); + exit_nicely(); + } + ntups = PQntuples(res); + if (ntups != tbinfo->ntrig) + { + write_msg(NULL, "expected %d triggers on table \"%s\" but found %d\n", + tbinfo->ntrig, tbinfo->relname, ntups); + exit_nicely(); + } + 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; + + if (strcmp(PQgetvalue(res, j, i_tgdeferrable), "f") == 0) + tgdeferrable = 0; + else + tgdeferrable = 1; + + if (strcmp(PQgetvalue(res, j, i_tginitdeferred), "f") == 0) + tginitdeferred = 0; + else + tginitdeferred = 1; + + resetPQExpBuffer(delqry); + appendPQExpBuffer(delqry, "DROP TRIGGER %s ", + fmtId(tgname, force_quotes)); + appendPQExpBuffer(delqry, "ON %s;\n", + fmtId(tbinfo->relname, force_quotes)); + + resetPQExpBuffer(query); + if (tgisconstraint) + { + appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER "); + appendPQExpBuffer(query, fmtId(PQgetvalue(res, j, i_tgconstrname), force_quotes)); + } + else + { + appendPQExpBuffer(query, "CREATE TRIGGER "); + appendPQExpBuffer(query, fmtId(tgname, force_quotes)); + } + appendPQExpBufferChar(query, ' '); + /* 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 ", + fmtId(tbinfo->relname, force_quotes)); + + if (tgisconstraint) + { + tgconstrrelid = PQgetvalue(res, j, i_tgconstrrelid); + + if (strcmp(tgconstrrelid, "0") != 0) + { + + 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 we are using regclass, name is already quoted */ + if (g_fout->remoteVersion >= 70300) + appendPQExpBuffer(query, " FROM %s", + PQgetvalue(res, j, i_tgconstrrelname)); + else + appendPQExpBuffer(query, " FROM %s", + fmtId(PQgetvalue(res, j, i_tgconstrrelname), force_quotes)); + } + if (!tgdeferrable) + appendPQExpBuffer(query, " NOT"); + appendPQExpBuffer(query, " DEFERRABLE INITIALLY "); + if (tginitdeferred) + appendPQExpBuffer(query, "DEFERRED"); + else + appendPQExpBuffer(query, "IMMEDIATE"); + + } + + appendPQExpBuffer(query, " FOR EACH ROW"); + /* 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, force_quotes)); + for (findx = 0; findx < tgnargs; findx++) + { + const char *s; + + for (p = tgargs;;) + { + p = strchr(p, '\\'); + if (p == NULL) + { + write_msg(NULL, "bad 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"); + + ArchiveEntry(fout, tgoid, + tgname, + tbinfo->relnamespace->nspname, + tbinfo->usename, + "TRIGGER", NULL, + query->data, delqry->data, + NULL, NULL, NULL); + + resetPQExpBuffer(query); + appendPQExpBuffer(query, "TRIGGER %s ", + fmtId(tgname, force_quotes)); + appendPQExpBuffer(query, "ON %s", + fmtId(tbinfo->relname, force_quotes)); + + dumpComment(fout, query->data, + tbinfo->relnamespace->nspname, tbinfo->usename, + tgoid, "pg_trigger", 0, NULL); + } + + PQclear(res); } + + destroyPQExpBuffer(query); + destroyPQExpBuffer(delqry); } static void -dumpRules(Archive *fout, const char *tablename, - TableInfo *tblinfo, int numTables) +dumpRules(Archive *fout, TableInfo *tblinfo, int numTables) { PGresult *res; int nrules; int i, t; PQExpBuffer query = createPQExpBuffer(); - int i_definition; int i_oid; - int i_owner; int i_rulename; if (g_verbose) @@ -5019,9 +5236,14 @@ dumpRules(Archive *fout, const char *tablename, */ for (t = 0; t < numTables; t++) { - if (tablename && (strcmp(tblinfo[t].relname, tablename) != 0) && (strlen(tablename) > 0)) + TableInfo *tbinfo = &tblinfo[t]; + + if (!tbinfo->hasrules || !tbinfo->dump) continue; + /* Make sure we are in proper schema */ + selectSourceSchema(tbinfo->relnamespace->nspname); + /* * Get all rules defined for this table, except view select rules */ @@ -5034,11 +5256,10 @@ dumpRules(Archive *fout, const char *tablename, * rules (pjw 15-Sep-2000). */ appendPQExpBuffer(query, "SELECT definition," - " (select usename from pg_user where pg_class.relowner = usesysid) AS viewowner, " " pg_rewrite.oid, pg_rewrite.rulename " "FROM pg_rewrite, pg_class, pg_rules " "WHERE pg_class.relname = "); - formatStringLiteral(query, tblinfo[t].relname, CONV_ALL); + formatStringLiteral(query, tbinfo->relname, CONV_ALL); appendPQExpBuffer(query, " AND pg_rewrite.ev_class = pg_class.oid " " AND pg_rules.tablename = pg_class.relname " @@ -5047,15 +5268,14 @@ dumpRules(Archive *fout, const char *tablename, } else { - appendPQExpBuffer(query, "SELECT pg_get_ruledef(pg_rewrite.oid) AS definition," - " (select usename from pg_user where pg_class.relowner = usesysid) AS viewowner, " - " pg_rewrite.oid, pg_rewrite.rulename " - "FROM pg_rewrite, pg_class " - "WHERE pg_class.oid = '%s'::oid " - " AND pg_rewrite.ev_class = pg_class.oid " - " AND pg_rewrite.rulename != '_RETURN' " - "ORDER BY pg_rewrite.oid", - tblinfo[t].oid); + appendPQExpBuffer(query, + "SELECT pg_get_ruledef(oid) AS definition," + " oid, rulename " + "FROM pg_rewrite " + "WHERE ev_class = '%s'::oid " + "AND rulename != '_RETURN' " + "ORDER BY oid", + tbinfo->oid); } res = PQexec(g_conn, query->data); @@ -5063,13 +5283,12 @@ dumpRules(Archive *fout, const char *tablename, PQresultStatus(res) != PGRES_TUPLES_OK) { write_msg(NULL, "query to get rules associated with table \"%s\" failed: %s", - tblinfo[t].relname, PQerrorMessage(g_conn)); + tbinfo->relname, PQerrorMessage(g_conn)); exit_nicely(); } nrules = PQntuples(res); i_definition = PQfnumber(res, "definition"); - i_owner = PQfnumber(res, "viewowner"); i_oid = PQfnumber(res, "oid"); i_rulename = PQfnumber(res, "rulename"); @@ -5079,17 +5298,24 @@ dumpRules(Archive *fout, const char *tablename, for (i = 0; i < nrules; i++) { - ArchiveEntry(fout, PQgetvalue(res, i, i_oid), PQgetvalue(res, i, i_rulename), - "RULE", NULL, PQgetvalue(res, i, i_definition), - "", "", PQgetvalue(res, i, i_owner), NULL, NULL); + ArchiveEntry(fout, PQgetvalue(res, i, i_oid), + PQgetvalue(res, i, i_rulename), + tbinfo->relnamespace->nspname, + tbinfo->usename, + "RULE", NULL, + PQgetvalue(res, i, i_definition), + "", /* Del */ + NULL, NULL, NULL); /* Dump rule comments */ resetPQExpBuffer(query); appendPQExpBuffer(query, "RULE %s", fmtId(PQgetvalue(res, i, i_rulename), force_quotes)); - appendPQExpBuffer(query, " ON %s", fmtId(tblinfo[t].relname, force_quotes)); - dumpComment(fout, query->data, PQgetvalue(res, i, i_oid), - "pg_rewrite", 0, NULL); + appendPQExpBuffer(query, " ON %s", fmtId(tbinfo->relname, force_quotes)); + dumpComment(fout, query->data, + tbinfo->relnamespace->nspname, + tbinfo->usename, + PQgetvalue(res, i, i_oid), "pg_rewrite", 0, NULL); } @@ -5098,3 +5324,206 @@ dumpRules(Archive *fout, const char *tablename, destroyPQExpBuffer(query); } + +/* + * selectSourceSchema - make the specified schema the active search path + * in the source database. + * + * NB: pg_catalog is implicitly searched before the specified schema; + * so system names are never qualified, and user names are only qualified + * if they are cross-schema references or duplicate system names. + */ +static void +selectSourceSchema(const char *schemaName) +{ + static char *curSchemaName = NULL; + PQExpBuffer query; + PGresult *res; + + /* Not relevant if fetching from pre-7.3 DB */ + if (g_fout->remoteVersion < 70300) + return; + /* Ignore null schema names */ + if (schemaName == NULL || *schemaName == '\0') + return; + /* Optimize away repeated selection of same schema */ + if (curSchemaName && strcmp(curSchemaName, schemaName) == 0) + return; + + query = createPQExpBuffer(); + appendPQExpBuffer(query, "SET search_path = %s", + fmtId(schemaName, force_quotes)); + res = PQexec(g_conn, query->data); + if (!res || + PQresultStatus(res) != PGRES_COMMAND_OK) + { + write_msg(NULL, "query to set search_path failed: %s", + PQerrorMessage(g_conn)); + exit_nicely(); + } + PQclear(res); + destroyPQExpBuffer(query); + + if (curSchemaName) + free(curSchemaName); + curSchemaName = strdup(schemaName); +} + +/* + * getFormattedTypeName - retrieve a nicely-formatted type name for the + * given type name. + * + * NB: in 7.3 and up the result may depend on the currently-selected + * schema; this is why we don't try to cache the names. + */ +static char * +getFormattedTypeName(const char *oid, OidOptions opts) +{ + char *result; + PQExpBuffer query; + PGresult *res; + int ntups; + + if (atooid(oid) == 0) + { + if ((opts & zeroAsOpaque) != 0) + return strdup(g_opaque_type); + else if ((opts & zeroAsAny) != 0) + return strdup("'any'"); + else if ((opts & zeroAsStar) != 0) + return strdup("*"); + else if ((opts & zeroAsNone) != 0) + return strdup("NONE"); + } + + query = createPQExpBuffer(); + if (g_fout->remoteVersion >= 70100) + { + appendPQExpBuffer(query, "SELECT format_type('%s'::oid, NULL)", + oid); + } + else + { + appendPQExpBuffer(query, "SELECT typname " + "FROM pg_type " + "WHERE oid = '%s'::oid", + oid); + } + + res = PQexec(g_conn, query->data); + if (!res || + PQresultStatus(res) != PGRES_TUPLES_OK) + { + write_msg(NULL, "query to obtain type name for %s failed: %s", + oid, PQerrorMessage(g_conn)); + exit_nicely(); + } + + /* 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(); + } + + result = strdup(PQgetvalue(res, 0, 0)); + + PQclear(res); + destroyPQExpBuffer(query); + + return result; +} + +/* + * myFormatType --- local implementation of format_type for use with 7.0. + */ +static char * +myFormatType(const char *typname, int32 typmod) +{ + char *result; + PQExpBuffer buf = createPQExpBuffer(); + + /* Show lengths on bpchar and varchar */ + if (!strcmp(typname, "bpchar")) + { + int len = (typmod - VARHDRSZ); + + appendPQExpBuffer(buf, "character"); + if (len > 1) + appendPQExpBuffer(buf, "(%d)", + typmod - VARHDRSZ); + } + else if (!strcmp(typname, "varchar")) + { + appendPQExpBuffer(buf, "character varying"); + if (typmod != -1) + appendPQExpBuffer(buf, "(%d)", + typmod - VARHDRSZ); + } + else if (!strcmp(typname, "numeric")) + { + appendPQExpBuffer(buf, "numeric"); + if (typmod != -1) + { + int32 tmp_typmod; + int precision; + int scale; + + tmp_typmod = typmod - VARHDRSZ; + precision = (tmp_typmod >> 16) & 0xffff; + scale = tmp_typmod & 0xffff; + appendPQExpBuffer(buf, "(%d,%d)", + precision, scale); + } + } + + /* + * 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")) + { + appendPQExpBuffer(buf, "%s", + fmtId(typname, true)); + } + else + { + appendPQExpBuffer(buf, "%s", + fmtId(typname, false)); + } + + result = strdup(buf->data); + destroyPQExpBuffer(buf); + + return result; +} + +/* + * fmtQualifiedId - convert a qualified name to the proper format for + * the source database. + * + * Like fmtId, use the result before calling again. + */ +static const char * +fmtQualifiedId(const char *schema, const char *id) +{ + static PQExpBuffer id_return = NULL; + + if (id_return) /* first time through? */ + resetPQExpBuffer(id_return); + else + id_return = createPQExpBuffer(); + + /* Suppress schema name if fetching from pre-7.3 DB */ + if (g_fout->remoteVersion >= 70300 && schema && *schema) + { + appendPQExpBuffer(id_return, "%s.", + fmtId(schema, force_quotes)); + } + appendPQExpBuffer(id_return, "%s", + fmtId(id, force_quotes)); + + return id_return->data; +} diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 580dacf66d..1cf7a02e30 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -1,27 +1,12 @@ /*------------------------------------------------------------------------- * * pg_dump.h - * header file for the pg_dump utility + * Common header file for the pg_dump utility * * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_dump.h,v 1.84 2002/04/24 22:39:49 petere Exp $ - * - * Modifications - 6/12/96 - dave@bensoft.com - version 1.13.dhb.2 - * - * - Fixed dumpTable output to output lengths for char and varchar types! - * - Added single. quote to twin single quote expansion for 'insert' string - * mode. - * - * Modifications - 6/1/97 - igor@sba.miami.edu - * - Added extern's for the functions that clear allocated memory - * in pg_dump.c - * - * Modifications - 14-Sep-2000 - pjw@rhyme.com.au - * - Added typedefn fields to typeinfo and relinfo - * - Added enum for findTypeByOid to allow special handling of - * '0' OID. + * $Id: pg_dump.h,v 1.85 2002/05/10 22:36:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,171 +17,133 @@ #include "pg_backup.h" #include "pqexpbuffer.h" -/* The data structures used to store system catalog information */ +/* + * The data structures used to store system catalog information + * + * NOTE: the structures described here live for the entire pg_dump run; + * and in most cases we make a struct for every object we can find in the + * catalogs, not only those we are actually going to dump. Hence, it's + * best to store a minimal amount of per-object info in these structs, + * and retrieve additional per-object info when and if we dump a specific + * object. In particular, try to avoid retrieving expensive-to-compute + * information until it's known to be needed. + */ + +typedef struct _namespaceInfo +{ + char *oid; + char *nspname; + char *usename; /* name of owner, or empty string */ + char *nspacl; + bool dump; /* true if need to dump definition */ +} NamespaceInfo; typedef struct _typeInfo { char *oid; - char *typowner; - char *typname; - char *typlen; - char *typprtlen; - char *typinput; - char *typoutput; - char *typreceive; - char *typsend; - char *typelem; - char *typdelim; - char *typdefault; - char *typrelid; - char *typalign; - char *typstorage; - char *usename; - char *typedefn; - char *typtype; - int passedbyvalue; - int isArray; - int isDefined; + char *typname; /* name as seen in catalog */ + /* Note: format_type might produce something different than typname */ + NamespaceInfo *typnamespace; /* link to containing namespace */ + char *usename; /* name of owner, or empty string */ + char *typelem; /* OID */ + char *typrelid; /* OID */ + char typtype; /* 'b', 'c', etc */ + bool isArray; /* true if user-defined array type */ + bool isDefined; /* true if typisdefined */ } TypeInfo; typedef struct _funcInfo { char *oid; char *proname; - char *proowner; + NamespaceInfo *pronamespace; /* link to containing namespace */ + char *usename; /* name of owner, or empty string */ Oid lang; int nargs; - char **argtypes; - char *prorettype; - int retset; /* 1 if the function returns a set, else 0 */ - char *prosrc; - char *probin; - char *usename; - char provolatile; /* Attr */ - bool isimplicit; /* Attr */ - bool isstrict; /* Attr */ - int dumped; /* 1 if already dumped */ + char **argtypes; /* OIDs */ + char *prorettype; /* OID */ + bool dumped; /* true if already dumped */ } FuncInfo; -typedef struct _trigInfo -{ - char *oid; - char *tgname; - char *tgsrc; - char *tgdel; - char *tgcomment; -} TrigInfo; - -typedef struct _tableInfo -{ - char *oid; - char *relname; - char *relacl; - char *viewdef; - char *viewoid; /* OID of view - should be >= oid of table - * important because views may be - * constructed manually from rules, and - * rule may ref things created after the - * base table was created. */ - char relkind; - bool hasindex; /* does it have any indexes? */ - bool hasoids; /* does it have OIDs? */ - int numatts; /* number of attributes */ - int *inhAttrs; /* an array of flags, one for each - * attribute if the value is 1, then this - * attribute is an inherited attribute */ - int *inhAttrDef; /* Flags indicating if attrdef is - * inherited */ - int *inhNotNull; /* Flags indicating if NOT NULL in - * inherited */ - char **attnames; /* the attribute names */ - char **atttypedefns; /* formatted column type definitions */ - char **typnames; /* fill out attributes */ - bool *notnull; /* Not null constraints of an attribute */ - char **adef_expr; /* DEFAULT expressions */ - int numParents; /* number of (immediate) parent - * supertables */ - char **parentRels; /* names of parent relations, NULL if - * numParents == 0 */ - char **out_attnames; /* the attribute names, in the order they - * would be in, when the table is created - * in the target query language. this is - * needed because the SQL tables will not - * have the same order of attributes as - * the POSTQUEL tables */ - int *atttypmod; /* type-specific type modifier */ - char *usename; - int ncheck; /* # of CHECK expressions */ - char **check_expr; /* [CONSTRAINT name] CHECK expressions */ - int ntrig; /* # of triggers */ - TrigInfo *triggers; /* Triggers on the table */ - char *pkIndexOid; /* Primary Key index OID */ - char *primary_key_name; /* PRIMARY KEY name, if any */ -} TableInfo; - -typedef struct _inhInfo -{ - char *inhrelid; - char *inhparent; -} InhInfo; - -typedef struct _indInfo -{ - char *indexreloid; /* oid of the index itself */ - char *indreloid; /* oid of the table the index is on */ - char *indexrelname; /* name of the index itself */ - char *indrelname; /* name of the indexed table */ - char *indexdef; /* index definitional command */ - char *indisprimary; /* is this a PK index? */ - int indnkeys; /* number of keys in index */ - char **indkey; /* attribute numbers of the key - * attributes */ -} IndInfo; - typedef struct _aggInfo { char *oid; char *aggname; - char *aggtransfn; - char *aggfinalfn; - char *aggtranstype; - char *aggbasetype; - char *agginitval; + NamespaceInfo *aggnamespace; /* link to containing namespace */ char *usename; - int convertok; /* Flag to indicate of version convertsion - * is OK */ } AggInfo; typedef struct _oprInfo { char *oid; char *oprname; - char *oprkind; /*---------- - * b = binary, - * l = left unary - * r = right unary - *---------- - */ - char *oprcode; /* operator function name */ - char *oprleft; /* left operand type */ - char *oprright; /* right operand type */ - char *oprcom; /* oid of the commutator operator */ - char *oprnegate; /* oid of the negator operator */ - char *oprrest; /* name of the function to calculate - * operator restriction selectivity */ - char *oprjoin; /* name of the function to calculate - * operator join selectivity */ - char *oprcanhash; /* can we use hash join strategy ? */ - char *oprlsortop; /* oid's of the left and right sort - * operators */ - char *oprrsortop; + NamespaceInfo *oprnamespace; /* link to containing namespace */ char *usename; + char *oprcode; /* as OID, not regproc name */ } OprInfo; +typedef struct _tableInfo +{ + /* + * These fields are collected for every table in the database. + */ + char *oid; + char *relname; + NamespaceInfo *relnamespace; /* link to containing namespace */ + char *usename; /* name of owner, or empty string */ + char *relacl; + char relkind; + bool hasindex; /* does it have any indexes? */ + bool hasrules; /* does it have any rules? */ + bool hasoids; /* does it have OIDs? */ + int ncheck; /* # of CHECK expressions */ + int ntrig; /* # of triggers */ + + bool interesting; /* true if need to collect more data */ + bool dump; /* true if we want to dump it */ + + /* + * These fields are computed only if we decide the table is interesting + * (it's either a table to dump, or a direct parent of a dumpable table). + */ + int numatts; /* number of attributes */ + char **attnames; /* the attribute names */ + char **atttypnames; /* attribute type names */ + int *atttypmod; /* type-specific type modifiers */ + /* + * Note: we need to store per-attribute notnull and default stuff for + * all interesting tables so that we can tell which constraints were + * inherited. + */ + bool *notnull; /* Not null constraints on attributes */ + char **adef_expr; /* DEFAULT expressions */ + bool *inhAttrs; /* true if each attribute is inherited */ + bool *inhAttrDef; /* true if attr's default is inherited */ + bool *inhNotNull; /* true if NOT NULL is inherited */ + + /* + * Stuff computed only for dumpable tables. + */ + int numParents; /* number of (immediate) parent tables */ + int *parentIndexes; /* TableInfo indexes of immediate parents */ + + char *viewoid; /* OID of view - should be >= oid of table + * important because views may be + * constructed manually from rules, and + * rule may ref things created after the + * base table was created. */ +} TableInfo; + +typedef struct _inhInfo +{ + char *inhrelid; /* OID of a child table */ + char *inhparent; /* OID of its parent */ +} InhInfo; + + /* global decls */ extern bool force_quotes; /* double-quotes for identifiers flag */ extern bool g_verbose; /* verbose flag */ -extern Oid g_last_builtin_oid; /* value of the last builtin oid */ extern Archive *g_fout; /* the script file */ /* placeholders for comment starting and ending delimiters */ @@ -212,73 +159,55 @@ extern char g_opaque_type[10]; /* name for the opaque type */ */ /* * common utility functions -*/ + */ extern TableInfo *dumpSchema(Archive *fout, int *numTablesPtr, - const char *tablename, - const bool acls, - const bool oids, + const bool aclsSkip, const bool schemaOnly, const bool dataOnly); -extern void dumpSchemaIdx(Archive *fout, - const char *tablename, - TableInfo *tblinfo, - int numTables); typedef enum _OidOptions { zeroAsOpaque = 1, zeroAsAny = 2, zeroAsStar = 4, - zeroAsNone = 8, - useBaseTypeName = 1024 + zeroAsNone = 8 } OidOptions; -extern char *findTypeByOid(TypeInfo *tinfo, int numTypes, const char *oid, OidOptions opts); +extern int findTableByOid(TableInfo *tbinfo, int numTables, const char *oid); extern char *findOprByOid(OprInfo *oprinfo, int numOprs, const char *oid); -extern int findFuncByName(FuncInfo *finfo, int numFuncs, const char *name); -extern int findTableByName(TableInfo *tbinfo, int numTables, const char *relname); +extern int findFuncByOid(FuncInfo *finfo, int numFuncs, const char *oid); extern void check_conn_and_db(void); +extern void exit_nicely(void); + extern void parseNumericArray(const char *str, char **array, int arraysize); /* * version specific routines */ +extern NamespaceInfo *getNamespaces(int *numNamespaces); extern TypeInfo *getTypes(int *numTypes); extern FuncInfo *getFuncs(int *numFuncs); extern AggInfo *getAggregates(int *numAggregates); - -extern void clearAggInfo(AggInfo *, int); -extern void clearFuncInfo(FuncInfo *, int); -extern void clearInhInfo(InhInfo *, int); -extern void clearIndInfo(IndInfo *, int); -extern void clearOprInfo(OprInfo *, int); -extern void clearTypeInfo(TypeInfo *, int); - extern OprInfo *getOperators(int *numOperators); -extern TableInfo *getTables(int *numTables, FuncInfo *finfo, int numFuncs, - const char* tablename); +extern TableInfo *getTables(int *numTables); extern InhInfo *getInherits(int *numInherits); + extern void getTableAttrs(TableInfo *tbinfo, int numTables); -extern IndInfo *getIndexes(int *numIndexes); extern void dumpDBComment(Archive *outfile); +extern void dumpNamespaces(Archive *fout, + NamespaceInfo *nsinfo, int numNamespaces); extern void dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs, TypeInfo *tinfo, int numTypes); -extern void dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs, - TypeInfo *tinfo, int numTypes); -extern void dumpFuncs(Archive *fout, FuncInfo *finfo, int numFuncs, - TypeInfo *tinfo, int numTypes); -extern void dumpAggs(Archive *fout, AggInfo *agginfo, int numAggregates, - TypeInfo *tinfo, int numTypes); -extern void dumpOprs(Archive *fout, OprInfo *agginfo, int numOperators, - TypeInfo *tinfo, int numTypes); -extern void dumpTables(Archive *fout, TableInfo *tbinfo, int numTables, - const char *tablename, const bool acls, - const bool schemaOnly, const bool dataOnly); -extern void dumpIndexes(Archive *fout, IndInfo *indinfo, int numIndexes, - TableInfo *tbinfo, int numTables, const char *tablename); -extern void exit_nicely(void); +extern void dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs); +extern void dumpFuncs(Archive *fout, FuncInfo *finfo, int numFuncs); +extern void dumpAggs(Archive *fout, AggInfo *agginfo, int numAggregates); +extern void dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators); +extern void dumpTables(Archive *fout, TableInfo *tblinfo, int numTables, + const bool aclsSkip, + const bool schemaOnly, const bool dataOnly); +extern void dumpIndexes(Archive *fout, TableInfo *tbinfo, int numTables); #endif /* PG_DUMP_H */ diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c index e6747fd53f..9d2af919ac 100644 --- a/src/bin/pg_dump/pg_restore.c +++ b/src/bin/pg_dump/pg_restore.c @@ -34,22 +34,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_restore.c,v 1.33 2002/01/18 19:17:05 momjian Exp $ - * - * Modifications - 28-Jun-2000 - pjw@rhyme.com.au - * - * Initial version. Command processing taken from original pg_dump. - * - * Modifications - 28-Jul-2000 - pjw@rhyme.com.au (1.45) - * - * Added --create, --no-owner, --superuser, --no-reconnect (pg_dump & pg_restore) - * Added code to dump 'Create Schema' statement (pg_dump) - * Don't bother to disable/enable triggers if we don't have a superuser (pg_restore) - * Cleaned up code for reconnecting to database. - * Force a reconnect as superuser before enabling/disabling triggers. - * - * Modifications - 6-Mar-2001 - pjw@rhyme.com.au - * Change -U option to -L to allow -U to specify username in future. + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_restore.c,v 1.34 2002/05/10 22:36:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -94,6 +79,7 @@ main(int argc, char **argv) extern int optind; extern char *optarg; static int use_setsessauth = 0; + static int disable_triggers = 0; #ifdef HAVE_GETOPT_LONG struct option cmdopts[] = { @@ -130,6 +116,8 @@ main(int argc, char **argv) * letter, but are available as '-X long-name' */ {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, + {"disable-triggers", no_argument, &disable_triggers, 1}, + {NULL, 0, NULL, 0} }; #endif /* HAVE_GETOPT_LONG */ @@ -281,6 +269,8 @@ main(int argc, char **argv) case 'X': if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; + else if (strcmp(optarg, "disable-triggers") == 0) + disable_triggers = 1; else { fprintf(stderr, @@ -309,6 +299,7 @@ main(int argc, char **argv) fileSpec = NULL; opts->use_setsessauth = use_setsessauth; + opts->disable_triggers = disable_triggers; if (opts->formatName) { @@ -385,74 +376,77 @@ usage(const char *progname) progname, progname); #ifdef HAVE_GETOPT_LONG puts(gettext( - " -a, --data-only restore only the data, no schema\n" - " -c, --clean clean (drop) schema prior to create\n" - " -C, --create issue commands to create the database\n" - " -d, --dbname=NAME output database name\n" - " -f, --file=FILENAME output file name\n" - " -F, --format={c|t} specify backup file format\n" - " -h, --host=HOSTNAME server host name\n" - " -i, --index=NAME restore named index\n" - " -l, --list print summarized TOC of the archive\n" - " -L, --use-list=FILENAME use specified table of contents for ordering\n" - " output from this file\n" - " -N, --orig-order restore in original dump order\n" - " -o, --oid-order restore in OID order\n" - " -O, --no-owner do not reconnect to database to match\n" - " object owner\n" - " -p, --port=PORT server port number\n" - " -P, --function=NAME restore named function\n" - " -r, --rearrange rearrange output to put indexes etc. at end\n" - " -R, --no-reconnect disallow ALL reconnections to the database\n" - " -s, --schema-only restore only the schema, no data\n" - " -S, --superuser=NAME specify the superuser user name to use for\n" - " disabling triggers\n" - " -t, --table=NAME restore named table\n" - " -T, --trigger=NAME restore named trigger\n" + " -a, --data-only restore only the data, no schema\n" + " -c, --clean clean (drop) schema prior to create\n" + " -C, --create issue commands to create the database\n" + " -d, --dbname=NAME output database name\n" + " -f, --file=FILENAME output file name\n" + " -F, --format={c|t} specify backup file format\n" + " -h, --host=HOSTNAME server host name\n" + " -i, --index=NAME restore named index\n" + " -l, --list print summarized TOC of the archive\n" + " -L, --use-list=FILENAME use specified table of contents for ordering\n" + " output from this file\n" + " -N, --orig-order restore in original dump order\n" + " -o, --oid-order restore in OID order\n" + " -O, --no-owner do not reconnect to database to match\n" + " object owner\n" + " -p, --port=PORT server port number\n" + " -P, --function=NAME restore named function\n" + " -r, --rearrange rearrange output to put indexes etc. at end\n" + " -R, --no-reconnect disallow ALL reconnections to the database\n" + " -s, --schema-only restore only the schema, no data\n" + " -S, --superuser=NAME specify the superuser user name to use for\n" + " disabling triggers\n" + " -t, --table=NAME restore named table\n" + " -T, --trigger=NAME restore named trigger\n" " -U, --username=NAME connect as specified database user\n" - " -v, --verbose verbose mode\n" - " -W, --password force password prompt (should happen automatically)\n" - " -x, --no-privileges skip restoration of access privileges (grant/revoke)\n" - " -X use-set-session-authorization, --use-set-session-authorization\n" - " use SET SESSION AUTHORIZATION commands instead\n" - " of reconnecting, if possible\n" - )); + " -v, --verbose verbose mode\n" + " -W, --password force password prompt (should happen automatically)\n" + " -x, --no-privileges skip restoration of access privileges (grant/revoke)\n" + " -X use-set-session-authorization, --use-set-session-authorization\n" + " use SET SESSION AUTHORIZATION commands instead\n" + " of reconnecting, if possible\n" + " -X disable-triggers, --disable-triggers\n" + " disable triggers during data-only restore\n" + )); #else /* not HAVE_GETOPT_LONG */ puts(gettext( - " -a restore only the data, no schema\n" - " -c clean (drop) schema prior to create\n" - " -C issue commands to create the database\n" - " -d NAME output database name\n" - " -f FILENAME output file name\n" - " -F {c|t} specify backup file format\n" - " -h HOSTNAME server host name\n" - " -i NAME restore named index\n" - " -l print summarized TOC of the archive\n" - " -L FILENAME use specified table of contents for ordering\n" - " output from this file\n" - " -N restore in original dump order\n" - " -o restore in OID order\n" - " -O do not reconnect to database to match\n" - " object owner\n" - " -p PORT server port number\n" - " -P NAME restore named function\n" - " -r rearrange output to put indexes etc. at end\n" - " -R disallow ALL reconnections to the database\n" - " -s restore only the schema, no data\n" - " -S NAME specify the superuser user name to use for\n" - " disabling triggers\n" - " -t NAME restore named table\n" - " -T NAME restore named trigger\n" + " -a restore only the data, no schema\n" + " -c clean (drop) schema prior to create\n" + " -C issue commands to create the database\n" + " -d NAME output database name\n" + " -f FILENAME output file name\n" + " -F {c|t} specify backup file format\n" + " -h HOSTNAME server host name\n" + " -i NAME restore named index\n" + " -l print summarized TOC of the archive\n" + " -L FILENAME use specified table of contents for ordering\n" + " output from this file\n" + " -N restore in original dump order\n" + " -o restore in OID order\n" + " -O do not reconnect to database to match\n" + " object owner\n" + " -p PORT server port number\n" + " -P NAME restore named function\n" + " -r rearrange output to put indexes etc. at end\n" + " -R disallow ALL reconnections to the database\n" + " -s restore only the schema, no data\n" + " -S NAME specify the superuser user name to use for\n" + " disabling triggers\n" + " -t NAME restore named table\n" + " -T NAME restore named trigger\n" " -U NAME connect as specified database user\n" - " -v verbose mode\n" - " -W force password prompt (should happen automatically)\n" - " -x skip restoration of access privileges (grant/revoke)\n" - " -X use-set-session-authorization\n" - " use SET SESSION AUTHORIZATION commands instead\n" - " of reconnecting, if possible\n" - )); + " -v verbose mode\n" + " -W force password prompt (should happen automatically)\n" + " -x skip restoration of access privileges (grant/revoke)\n" + " -X use-set-session-authorization\n" + " use SET SESSION AUTHORIZATION commands instead\n" + " of reconnecting, if possible\n" + " -X disable-triggers disable triggers during data-only restore\n" + )); #endif puts(gettext("If no input file name is supplied, then standard input is used.\n")); puts(gettext("Report bugs to ."));