From 24e97528631e7e810ce61fc0f5fbcaca0c001c4c Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 9 Oct 2006 23:36:59 +0000 Subject: [PATCH] Revise psql pattern-matching switches as per discussion. The rule is now to process all inclusion switches then all exclusion switches, so that the behavior is independent of switch ordering. Use of -T does not cause non-table objects to be suppressed. And the patterns are now interpreted the same way psql's \d commands do it, rather than as pure regex commands; this allows for example -t schema.tab to do what it should have been doing all along. Re-enable the --blobs switch to do something useful, ie, add back blobs into a dump they were otherwise suppressed from. --- doc/src/sgml/ref/pg_dump.sgml | 303 ++++++++++++++--------- src/bin/pg_dump/common.c | 144 +++++++++-- src/bin/pg_dump/pg_dump.c | 445 +++++++++++++++------------------- src/bin/pg_dump/pg_dump.h | 41 +++- 4 files changed, 553 insertions(+), 380 deletions(-) diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index deafd7c9a9..9aa4baf84e 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -1,5 +1,5 @@ @@ -14,7 +14,7 @@ PostgreSQL documentation pg_dump - extract a PostgreSQL database into a script file or other archive file + extract a PostgreSQL database into a script file or other archive file @@ -126,6 +126,19 @@ PostgreSQL documentation + + + + + + Include large objects in the dump. This is the default behavior + except when + + + @@ -170,12 +183,14 @@ PostgreSQL documentation Dump data as INSERT commands (rather than COPY). This will make restoration very slow; it is mainly useful for making dumps that can be loaded into - non-PostgreSQL databases. Note that + non-PostgreSQL databases. + Also, since this option generates a separate command for each row, + an error in reloading a row causes only that row to be lost rather + than the entire table contents. + Note that the restore may fail altogether if you have rearranged column order. - The option is safer, though even slower. - Also, while this option generates errors for invalid data, - it allows other INSERTs to continue loading - data into the table. + The option is safe against column order changes, + though even slower. @@ -193,9 +208,9 @@ PostgreSQL documentation ...). This will make restoration very slow; it is mainly useful for making dumps that can be loaded into non-PostgreSQL databases. - Also, while this option generates errors for invalid data, - it allows other INSERTs to continue loading - data into the table. + Also, since this option generates a separate command for each row, + an error in reloading a row causes only that row to be lost rather + than the entire table contents. @@ -238,7 +253,7 @@ PostgreSQL documentation plain - Output a plain-text SQL script file (default) + Output a plain-text SQL script file (the default). @@ -248,10 +263,10 @@ PostgreSQL documentation custom - Output a custom archive suitable for input into - pg_restore. This is the most flexible - format in that it allows reordering of loading data as well - as object definitions. This format is also compressed by default. + Output a custom archive suitable for input into + pg_restore. This is the most flexible + format in that it allows reordering of loading data as well + as object definitions. This format is also compressed by default. @@ -261,11 +276,11 @@ PostgreSQL documentation tar - Output a tar archive suitable for input into - pg_restore. Using this archive format - allows reordering and/or exclusion of database objects - at the time the database is restored. It is also possible to limit - which data is reloaded at restore time. + Output a tar archive suitable for input into + pg_restore. Using this archive format + allows reordering and/or exclusion of database objects + at the time the database is restored. It is also possible to limit + which data is reloaded at restore time. @@ -286,9 +301,11 @@ PostgreSQL documentation - pg_dump can handle databases from + pg_dump can dump from servers running previous releases of PostgreSQL, but very old - versions are not supported anymore (currently prior to 7.0). + versions are not supported anymore (currently, those prior to 7.0). + Dumping from a server newer than pg_dump + is likely not to work at all. Use this option if you need to override the version check (and if pg_dump then fails, don't say you weren't warned). @@ -301,20 +318,61 @@ PostgreSQL documentation - Dump the contents of schema - only. If this option is not specified, all non-system schemas - in the target database will be dumped. + Dump only schemas matching schema; this selects both the + schema itself, and all its contained objects. When this option is + not specified, all non-system schemas in the target database will be + dumped. Multiple schemas can be + selected by writing multiple - In this mode, pg_dump makes no - attempt to dump any other database objects that objects in the - selected schema may depend upon. Therefore, there is no - guarantee that the results of a single-schema dump can be - successfully restored by themselves into a clean database. + When + + + + + Non-schema objects such as blobs are not dumped when + + + + + + + + + + Do not dump any schemas matching the schema pattern. The pattern is + interpreted according to the same rules as for + + + When both @@ -340,7 +398,7 @@ PostgreSQL documentation Do not output commands to set ownership of objects to match the original database. By default, pg_dump issues - ALTER OWNER or + ALTER OWNER or SET SESSION AUTHORIZATION statements to set ownership of created database objects. These statements @@ -397,67 +455,47 @@ PostgreSQL documentation - Dump data for table - only. It is possible for there to be multiple tables with the same - name in different schemas; if that is the case, all matching tables - will be dumped. Also, if any POSIX regular expression character appears - in the table name (([{\.?+, the string will be interpreted - as a regular expression. Note that when in regular expression mode, the - string will not be anchored to the start/end unless ^ and - $ are used at the beginning/end of the string. + Dump only tables (or views or sequences) matching table. Multiple tables can be + selected by writing multiple - The options - For example, to dump a single table named pg_class: - - -$ pg_dump -t pg_class mydb > db.out - - - - To dump all tables starting with employee in the - detroit schema, except for the table named employee_log: - - -$ pg_dump -n detroit -t ^employee -T employee_log mydb > db.out - - - - To dump all schemas starting with east or west and ending in - gsm, but not schemas that contain the letters test, except for - one named east_alpha_test_five: - - -$ pg_dump -n "^(east|west).*gsm$" -N test -n east_alpha_test_five mydb > db.out - - - - - To dump all tables except for those beginning with ts_: - - -$ pg_dump -T "^ts_" mydb > db.out - - - - - In this mode, pg_dump makes no - attempt to dump any other database objects that the selected tables - may depend upon. Therefore, there is no guarantee + When + + + + The behavior of the + @@ -466,36 +504,20 @@ PostgreSQL documentation - Do not dump any matching tables. - More than one option can be used, and POSIX regular expressions are handled just - like -t. + Do not dump any tables matching the table pattern. The pattern is + interpreted according to the same rules as for - - - - - - - - - Dump only the matching schemas. - More than one option can be used, and POSIX regular expressions are handled just - like -t. - - - - - - - - - Do not dump the matching schemas. - More than one option can be used, and POSIX regular expressions are handled just - like -t. + When both - @@ -506,7 +528,7 @@ PostgreSQL documentation Specifies verbose mode. This will cause pg_dump to output detailed object - comments and start/stop times to the dump file, and progress + comments and start/stop times to the dump file, and progress messages to standard error. @@ -742,33 +764,80 @@ CREATE DATABASE foo WITH TEMPLATE template0; Examples - To dump a database: + To dump a database called mydb into a SQL-script file: + +$ pg_dump mydb > db.sql + + + + + To reload such a script into a (freshly created) database named + newdb: + -$ pg_dump mydb > db.out +$ psql -d newdb -f db.sql - To reload this database: + To dump a database into a custom-format archive file: + + +$ pg_dump -Fc mydb > db.dump + + + + + To reload an archive file into a (freshly created) database named + newdb: + + +$ pg_restore -d newdb db.dump + + + + + To dump a single table named mytab: + + +$ pg_dump -t mytab mydb > db.sql + + + + + To dump all tables whose names start with emp in the + detroit schema, except for the table named + employee_log: + + +$ pg_dump -t 'detroit.emp*' -T detroit.employee_log mydb > db.sql + + + + + To dump all schemas whose names start with east or + west and end in gsm, excluding any schemas whose + names contain the word test: + -$ psql -d database -f db.out +$ pg_dump -n 'east*gsm' -n 'west*gsm' -N '*test*' mydb > db.sql - To dump a database called mydb to a file in custom format: - file: + The same, using regular expression notation to consolidate the switches: -$ pg_dump -Fc mydb > db.out +$ pg_dump -n '(east|west)*gsm' -N '*test*' mydb > db.sql - To reload this dump into an existing database called newdb: + To dump all database objects except for tables whose names begin with + ts_: -$ pg_restore -d newdb db.out +$ pg_dump -T 'ts_*' mydb > db.sql diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index f4b4a1e5bb..44ccb2eab1 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.93 2006/09/27 15:41:23 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.94 2006/10/09 23:36:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -402,16 +402,14 @@ AssignDumpId(DumpableObject *dobj) { newAlloc = 256; dumpIdMap = (DumpableObject **) - malloc(newAlloc * sizeof(DumpableObject *)); + pg_malloc(newAlloc * sizeof(DumpableObject *)); } else { newAlloc = allocedDumpIds * 2; dumpIdMap = (DumpableObject **) - realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *)); + pg_realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *)); } - if (dumpIdMap == NULL) - exit_horribly(NULL, NULL, "out of memory\n"); memset(dumpIdMap + allocedDumpIds, 0, (newAlloc - allocedDumpIds) * sizeof(DumpableObject *)); allocedDumpIds = newAlloc; @@ -541,9 +539,7 @@ getDumpableObjects(DumpableObject ***objs, int *numObjs) j; *objs = (DumpableObject **) - malloc(allocedDumpIds * sizeof(DumpableObject *)); - if (*objs == NULL) - exit_horribly(NULL, NULL, "out of memory\n"); + pg_malloc(allocedDumpIds * sizeof(DumpableObject *)); j = 0; for (i = 1; i < allocedDumpIds; i++) { @@ -567,17 +563,15 @@ addObjectDependency(DumpableObject *dobj, DumpId refId) { dobj->allocDeps = 16; dobj->dependencies = (DumpId *) - malloc(dobj->allocDeps * sizeof(DumpId)); + pg_malloc(dobj->allocDeps * sizeof(DumpId)); } else { dobj->allocDeps *= 2; dobj->dependencies = (DumpId *) - realloc(dobj->dependencies, - dobj->allocDeps * sizeof(DumpId)); + pg_realloc(dobj->dependencies, + dobj->allocDeps * sizeof(DumpId)); } - if (dobj->dependencies == NULL) - exit_horribly(NULL, NULL, "out of memory\n"); } dobj->dependencies[dobj->nDeps++] = refId; } @@ -707,7 +701,8 @@ findParentsByOid(TableInfo *self, if (numParents > 0) { - self->parents = (TableInfo **) malloc(sizeof(TableInfo *) * numParents); + self->parents = (TableInfo **) + pg_malloc(sizeof(TableInfo *) * numParents); j = 0; for (i = 0; i < numInherits; i++) { @@ -806,3 +801,124 @@ strInArray(const char *pattern, char **arr, int arr_size) } return -1; } + + +/* + * Support for simple list operations + */ + +void +simple_oid_list_append(SimpleOidList *list, Oid val) +{ + SimpleOidListCell *cell; + + cell = (SimpleOidListCell *) pg_malloc(sizeof(SimpleOidListCell)); + cell->next = NULL; + cell->val = val; + + if (list->tail) + list->tail->next = cell; + else + list->head = cell; + list->tail = cell; +} + +void +simple_string_list_append(SimpleStringList *list, const char *val) +{ + SimpleStringListCell *cell; + + /* this calculation correctly accounts for the null trailing byte */ + cell = (SimpleStringListCell *) + pg_malloc(sizeof(SimpleStringListCell) + strlen(val)); + cell->next = NULL; + strcpy(cell->val, val); + + if (list->tail) + list->tail->next = cell; + else + list->head = cell; + list->tail = cell; +} + +bool +simple_oid_list_member(SimpleOidList *list, Oid val) +{ + SimpleOidListCell *cell; + + for (cell = list->head; cell; cell = cell->next) + { + if (cell->val == val) + return true; + } + return false; +} + +bool +simple_string_list_member(SimpleStringList *list, const char *val) +{ + SimpleStringListCell *cell; + + for (cell = list->head; cell; cell = cell->next) + { + if (strcmp(cell->val, val) == 0) + return true; + } + return false; +} + + +/* + * Safer versions of some standard C library functions. If an + * out-of-memory condition occurs, these functions will bail out + * safely; therefore, their return value is guaranteed to be non-NULL. + * + * XXX need to refactor things so that these can be in a file that can be + * shared by pg_dumpall and pg_restore as well as pg_dump. + */ + +char * +pg_strdup(const char *string) +{ + char *tmp; + + if (!string) + exit_horribly(NULL, NULL, "cannot duplicate null pointer\n"); + tmp = strdup(string); + if (!tmp) + exit_horribly(NULL, NULL, "out of memory\n"); + return tmp; +} + +void * +pg_malloc(size_t size) +{ + void *tmp; + + tmp = malloc(size); + if (!tmp) + exit_horribly(NULL, NULL, "out of memory\n"); + return tmp; +} + +void * +pg_calloc(size_t nmemb, size_t size) +{ + void *tmp; + + tmp = calloc(nmemb, size); + if (!tmp) + exit_horribly(NULL, NULL, "out of memory\n"); + return tmp; +} + +void * +pg_realloc(void *ptr, size_t size) +{ + void *tmp; + + tmp = realloc(ptr, size); + if (!tmp) + exit_horribly(NULL, NULL, "out of memory\n"); + return tmp; +} diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 5cf0b76b50..c4df03083e 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.452 2006/10/07 20:59:04 petere Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.453 2006/10/09 23:36:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,8 +40,6 @@ int optreset; #endif - - #include "access/htup.h" #include "catalog/pg_class.h" #include "catalog/pg_proc.h" @@ -87,20 +85,24 @@ static const char *username_subquery; /* obsolete as of 7.3: */ static Oid g_last_builtin_oid; /* value of the last builtin oid */ -/* select and exclude tables and schemas */ -typedef struct objnameArg -{ - struct objnameArg *next; - char *name; /* name of the relation */ - bool is_include; /* include/exclude? */ -} objnameArg; +/* + * Object inclusion/exclusion lists + * + * The string lists record the patterns given by command-line switches, + * which we then convert to lists of OIDs of matching objects. + */ +static SimpleStringList schema_include_patterns = { NULL, NULL }; +static SimpleOidList schema_include_oids = { NULL, NULL }; +static SimpleStringList schema_exclude_patterns = { NULL, NULL }; +static SimpleOidList schema_exclude_oids = { NULL, NULL }; -objnameArg *schemaList = NULL; /* List of schemas to include/exclude */ -objnameArg *tableList = NULL; /* List of tables to include/exclude */ +static SimpleStringList table_include_patterns = { NULL, NULL }; +static SimpleOidList table_include_oids = { NULL, NULL }; +static SimpleStringList table_exclude_patterns = { NULL, NULL }; +static SimpleOidList table_exclude_oids = { NULL, NULL }; -char *matchingSchemas = NULL; /* Final list of schemas to dump by - * oid */ -char *matchingTables = NULL; /* Final list of tables to dump by oid */ +/* default, if no "inclusion" switches appear, is to dump everything */ +static bool include_everything = true; char g_opaque_type[10]; /* name for the opaque type */ @@ -119,6 +121,10 @@ static int disable_dollar_quoting = 0; static void help(const char *progname); +static void expand_schema_name_patterns(SimpleStringList *patterns, + SimpleOidList *oids); +static void expand_table_name_patterns(SimpleStringList *patterns, + SimpleOidList *oids); static NamespaceInfo *findNamespace(Oid nsoid, Oid objoid); static void dumpTableData(Archive *fout, TableDataInfo *tdinfo); static void dumpComment(Archive *fout, const char *target, @@ -188,11 +194,6 @@ static void check_sql_result(PGresult *res, PGconn *conn, const char *query, int main(int argc, char **argv) { - PQExpBuffer query = createPQExpBuffer(); - PGresult *res; - objnameArg *this_obj_name, - *schemaList_tail = NULL, - *tableList_tail = NULL; int c; const char *filename = NULL; const char *format = "p"; @@ -208,14 +209,13 @@ main(int argc, char **argv) DumpableObject **dobjs; int numObjs; int i; - bool switch_include_exclude; bool force_password = false; int compressLevel = -1; bool ignore_version = false; int plainText = 0; int outputClean = 0; int outputCreate = 0; - bool outputBlobs = true; + bool outputBlobs = false; int outputNoOwner = 0; static int use_setsessauth = 0; static int disable_triggers = 0; @@ -306,7 +306,7 @@ main(int argc, char **argv) break; case 'b': /* Dump blobs */ - /* this is now default, so just ignore the switch */ + outputBlobs = true; break; case 'c': /* clean (i.e., drop) schema prior to create */ @@ -347,42 +347,13 @@ main(int argc, char **argv) ignore_version = true; break; - case 'n': /* Include schemas */ - case 'N': /* Exclude schemas */ - case 't': /* Include tables */ - case 'T': /* Exclude tables */ - - if (strlen(optarg) < 1) - { - fprintf(stderr, _("%s: invalid -%c option\n"), progname, c); - exit(1); - } - - { - /* Create a struct for this name */ - objnameArg *new_obj_name = (objnameArg *) - malloc(sizeof(objnameArg)); - - new_obj_name->next = NULL; - new_obj_name->name = strdup(optarg); - new_obj_name->is_include = islower((unsigned char) c) ? true : false; + case 'n': /* include schema(s) */ + simple_string_list_append(&schema_include_patterns, optarg); + include_everything = false; + break; - /* add new entry to the proper list */ - if (tolower((unsigned char) c) == 'n') - { - if (!schemaList_tail) - schemaList_tail = schemaList = new_obj_name; - else - schemaList_tail = schemaList_tail->next = new_obj_name; - } - else - { - if (!tableList_tail) - tableList_tail = tableList = new_obj_name; - else - tableList_tail = tableList_tail->next = new_obj_name; - } - } + case 'N': /* exclude schema(s) */ + simple_string_list_append(&schema_exclude_patterns, optarg); break; case 'o': /* Dump oids */ @@ -403,13 +374,21 @@ main(int argc, char **argv) case 's': /* dump schema only */ schemaOnly = true; - outputBlobs = false; break; case 'S': /* Username for superuser in plain text output */ outputSuperuser = strdup(optarg); break; + case 't': /* include table(s) */ + simple_string_list_append(&table_include_patterns, optarg); + include_everything = false; + break; + + case 'T': /* exclude table(s) */ + simple_string_list_append(&table_exclude_patterns, optarg); + break; + case 'u': force_password = true; username = simple_prompt("User name: ", 100, true); @@ -488,9 +467,6 @@ main(int argc, char **argv) exit(1); } - if (matchingTables != NULL || matchingSchemas != NULL) - outputBlobs = false; - if (dumpInserts == true && oids == true) { write_msg(NULL, "INSERT (-d, -D) and OID (-o) options cannot be used together\n"); @@ -607,162 +583,42 @@ main(int argc, char **argv) write_msg(NULL, "last built-in OID is %u\n", g_last_builtin_oid); } - - if (schemaList != NULL && g_fout->remoteVersion < 70300) + /* Expand schema selection patterns into OID lists */ + if (schema_include_patterns.head != NULL) { - write_msg(NULL, "server version must be at least 7.3 to use schema switches\n"); - exit_nicely(); - } - - /* Check schema selection flags */ - resetPQExpBuffer(query); - switch_include_exclude = true; - - for (this_obj_name = schemaList; this_obj_name; this_obj_name = this_obj_name->next) - { - if (switch_include_exclude) - { - /* Special case for when -N is the first argument */ - if (this_obj_name == schemaList && !this_obj_name->is_include) - appendPQExpBuffer(query, - "SELECT oid FROM pg_catalog.pg_namespace " - "WHERE nspname NOT LIKE 'pg_%%' AND " - " nspname != 'information_schema' EXCEPT\n"); - - appendPQExpBuffer(query, "SELECT oid FROM pg_catalog.pg_namespace WHERE"); - } - - appendPQExpBuffer(query, "%s nspname %c ", switch_include_exclude ? "" : " OR", - /* any meta-characters? */ - strpbrk(this_obj_name->name, "([{\\.?+") == NULL ? '=' : '~'); - appendStringLiteralAH(query, this_obj_name->name, g_fout); - - if (this_obj_name->next && this_obj_name->next->is_include == this_obj_name->is_include) - switch_include_exclude = false; - else - { - switch_include_exclude = true; - - /* Add the joiner if needed */ - if (this_obj_name->next) - appendPQExpBuffer(query, "\n%s\n", - this_obj_name->next->is_include ? "UNION" : "EXCEPT"); - } - } - - /* Construct OID list of matching schemas */ - if (schemaList) - { - int len; - - res = PQexec(g_conn, query->data); - check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); - if (PQntuples(res) == 0) + expand_schema_name_patterns(&schema_include_patterns, + &schema_include_oids); + if (schema_include_oids.head == NULL) { write_msg(NULL, "No matching schemas were found\n"); exit_nicely(); } - - for (i = 0, len = strlen(" "); i < PQntuples(res); i++) - len += strlen(PQgetvalue(res, i, 0)) + 1; - - /* - * Need to use comma separators so it can be used by IN. zero is a - * dummy placeholder. Format is " oid oid oid ". - */ - matchingSchemas = malloc(len + 1); - strcpy(matchingSchemas, " "); - for (i = 0; i < PQntuples(res); i++) - { - strcat(matchingSchemas, PQgetvalue(res, i, 0)); - strcat(matchingSchemas, " "); - } } + expand_schema_name_patterns(&schema_exclude_patterns, + &schema_exclude_oids); + /* non-matching exclusion patterns aren't an error */ - /* Check table selection flags */ - resetPQExpBuffer(query); - switch_include_exclude = true; - - for (this_obj_name = tableList; this_obj_name; this_obj_name = this_obj_name->next) + /* Expand table selection patterns into OID lists */ + if (table_include_patterns.head != NULL) { - if (switch_include_exclude) - { - /* Special case for when -T is the first argument */ - if (this_obj_name == tableList && !this_obj_name->is_include && !strlen(query->data)) - appendPQExpBuffer(query, - "SELECT pg_class.oid FROM pg_catalog.pg_class, pg_catalog.pg_namespace " - "WHERE relkind='r' AND " - " relnamespace = pg_namespace.oid AND " - " nspname NOT LIKE 'pg_%%' AND " - " nspname != 'information_schema' EXCEPT\n"); - - appendPQExpBuffer(query, "SELECT oid FROM pg_catalog.pg_class WHERE relkind='r' AND ("); - } - - appendPQExpBuffer(query, "%srelname %c ", switch_include_exclude ? "" : " OR ", - /* any meta-characters? */ - strpbrk(this_obj_name->name, "([{\\.?+") == NULL ? '=' : '~'); - appendStringLiteralAH(query, this_obj_name->name, g_fout); - - if (this_obj_name->next && this_obj_name->next->is_include == this_obj_name->is_include) - switch_include_exclude = false; - else - { - switch_include_exclude = true; - appendPQExpBuffer(query, ")"); - - /* Add the joiner if needed */ - if (this_obj_name->next) - appendPQExpBuffer(query, "\n%s\n", this_obj_name->next->is_include ? - "UNION" : "EXCEPT"); - } - } - - /* Construct OID list of matching tables */ - if (tableList) - { - int len; - - /* Restrict by schema? */ - if (matchingSchemas != NULL) - { - char *matchingSchemas_commas = strdup(matchingSchemas), - *p; - - /* Construct "IN" SQL string by adding commas, " oid, oid, oid " */ - for (p = matchingSchemas_commas; *p; p++) - { - /* No commas for first/last characters */ - if (*p == ' ' && p != matchingSchemas_commas && *(p + 1)) - *p = ','; - } - - appendPQExpBuffer(query, - "\nINTERSECT\nSELECT oid FROM pg_catalog.pg_class WHERE relkind='r' AND relnamespace IN (%s)\n", - matchingSchemas_commas); - } - - res = PQexec(g_conn, query->data); - check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); - if (PQntuples(res) == 0) + expand_table_name_patterns(&table_include_patterns, + &table_include_oids); + if (table_include_oids.head == NULL) { write_msg(NULL, "No matching tables were found\n"); exit_nicely(); } - - for (i = 0, len = strlen(" "); i < PQntuples(res); i++) - len += strlen(PQgetvalue(res, i, 0)) + 1; - - matchingTables = malloc(len + 1); - strcpy(matchingTables, " "); - for (i = 0; i < PQntuples(res); i++) - { - strcat(matchingTables, PQgetvalue(res, i, 0)); - strcat(matchingTables, " "); - } } + expand_table_name_patterns(&table_exclude_patterns, + &table_exclude_oids); + /* non-matching exclusion patterns aren't an error */ - destroyPQExpBuffer(query); + /* + * Dumping blobs is now default unless we saw an inclusion switch or -s + * ... but even if we did see one of these, -b turns it back on. + */ + if (include_everything && !schemaOnly) + outputBlobs = true; /* * Now scan the database and create DumpableObject structs for all the @@ -824,7 +680,7 @@ main(int argc, char **argv) dumpStdStrings(g_fout); /* The database item is always next, unless we don't want it at all */ - if (!dataOnly && matchingTables == NULL && matchingSchemas == NULL) + if (include_everything && !dataOnly) dumpDatabase(g_fout); /* Now the rearrangeable objects. */ @@ -884,28 +740,28 @@ help(const char *progname) printf(_("\nOptions controlling the output content:\n")); printf(_(" -a, --data-only dump only the data, not the schema\n")); + printf(_(" -b, --blobs include large objects in dump\n")); printf(_(" -c, --clean clean (drop) schema prior to create\n")); printf(_(" -C, --create include commands to create database in dump\n")); printf(_(" -d, --inserts dump data as INSERT commands, rather than COPY\n")); printf(_(" -D, --column-inserts dump data as INSERT commands with column names\n")); printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n")); - printf(_(" -n, --schema=SCHEMA dump the named schema only\n")); - printf(_(" -N, --exclude-schema=SCHEMA\n" - " do NOT dump the named schema\n")); + printf(_(" -n, --schema=SCHEMA dump the named schema(s) only\n")); + printf(_(" -N, --exclude-schema=SCHEMA do NOT dump the named schema(s)\n")); printf(_(" -o, --oids include OIDs in dump\n")); printf(_(" -O, --no-owner skip restoration of object ownership\n" " in plain text format\n")); printf(_(" -s, --schema-only dump only the schema, no data\n")); printf(_(" -S, --superuser=NAME specify the superuser user name to use in\n" " plain text format\n")); - printf(_(" -t, --table=TABLE dump the named table only\n")); - printf(_(" -T, --exclude-table=TABLE do NOT dump the named table\n")); + printf(_(" -t, --table=TABLE dump the named table(s) only\n")); + printf(_(" -T, --exclude-table=TABLE do NOT dump the named table(s)\n")); printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n")); printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n")); printf(_(" --disable-triggers disable triggers during data-only restore\n")); printf(_(" --use-set-session-authorization\n" " use SESSION AUTHORIZATION commands instead of\n" - " OWNER TO commands\n")); + " ALTER OWNER commands to set ownership\n")); printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); @@ -928,66 +784,159 @@ exit_nicely(void) } /* - * selectDumpableNamespace: policy-setting subroutine - * Mark a namespace as to be dumped or not + * Find the OIDs of all schemas matching the given list of patterns, + * and append them to the given OID list. */ static void -selectDumpableNamespace(NamespaceInfo *nsinfo) +expand_schema_name_patterns(SimpleStringList *patterns, SimpleOidList *oids) { + PQExpBuffer query; + PGresult *res; + SimpleStringListCell *cell; + int i; + + if (patterns->head == NULL) + return; /* nothing to do */ + + if (g_fout->remoteVersion < 70300) + { + write_msg(NULL, "server version must be at least 7.3 to use schema selection switches\n"); + exit_nicely(); + } + + query = createPQExpBuffer(); + /* - * If specific tables are being dumped, do not dump any complete - * namespaces. If specific namespaces are being dumped, dump just those - * namespaces. Otherwise, dump all non-system namespaces. + * We use UNION ALL rather than UNION; this might sometimes result in + * duplicate entries in the OID list, but we don't care. */ - nsinfo->dobj.dump = false; - if (matchingTables != NULL) - /* false */ ; - else if (matchingSchemas != NULL) + for (cell = patterns->head; cell; cell = cell->next) { - char *search_oid = malloc(20); + if (cell != patterns->head) + appendPQExpBuffer(query, "UNION ALL\n"); + appendPQExpBuffer(query, + "SELECT oid FROM pg_catalog.pg_namespace n\n"); + processSQLNamePattern(g_conn, query, cell->val, false, false, + NULL, "n.nspname", NULL, + NULL); + } - sprintf(search_oid, " %d ", nsinfo->dobj.catId.oid); - if (strstr(matchingSchemas, search_oid) != NULL) - nsinfo->dobj.dump = true; + res = PQexec(g_conn, query->data); + check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); - free(search_oid); + for (i = 0; i < PQntuples(res); i++) + { + simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0))); } - /* The server prevents users from creating pg_ schemas */ - else if (strncmp(nsinfo->dobj.name, "pg_", 3) != 0 && - strcmp(nsinfo->dobj.name, "information_schema") != 0) - nsinfo->dobj.dump = true; + + PQclear(res); + destroyPQExpBuffer(query); } /* - * selectDumpableTable: policy-setting subroutine - * Mark a table as to be dumped or not + * Find the OIDs of all tables matching the given list of patterns, + * and append them to the given OID list. */ static void -selectDumpableTable(TableInfo *tbinfo) +expand_table_name_patterns(SimpleStringList *patterns, SimpleOidList *oids) { + PQExpBuffer query; + PGresult *res; + SimpleStringListCell *cell; + int i; + + if (patterns->head == NULL) + return; /* nothing to do */ + + query = createPQExpBuffer(); + /* - * Always dump if dumping parent namespace; else, if a particular - * tablename has been specified, dump matching table name; else, do not - * dump. + * We use UNION ALL rather than UNION; this might sometimes result in + * duplicate entries in the OID list, but we don't care. */ - tbinfo->dobj.dump = false; - if (matchingTables == NULL) + for (cell = patterns->head; cell; cell = cell->next) { - if (tbinfo->dobj.namespace->dobj.dump) - tbinfo->dobj.dump = true; + if (cell != patterns->head) + appendPQExpBuffer(query, "UNION ALL\n"); + appendPQExpBuffer(query, + "SELECT c.oid" + "\nFROM pg_catalog.pg_class c" + "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" + "\nWHERE c.relkind in ('%c', '%c', '%c')\n", + RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW); + processSQLNamePattern(g_conn, query, cell->val, true, false, + "n.nspname", "c.relname", NULL, + "pg_catalog.pg_table_is_visible(c.oid)"); } - else - { - char *search_oid = malloc(20); - sprintf(search_oid, " %d ", tbinfo->dobj.catId.oid); - if (strstr(matchingTables, search_oid) != NULL) - tbinfo->dobj.dump = true; + res = PQexec(g_conn, query->data); + check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); - free(search_oid); + for (i = 0; i < PQntuples(res); i++) + { + simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0))); } + + PQclear(res); + destroyPQExpBuffer(query); +} + +/* + * selectDumpableNamespace: policy-setting subroutine + * Mark a namespace as to be dumped or not + */ +static void +selectDumpableNamespace(NamespaceInfo *nsinfo) +{ + /* + * If specific tables are being dumped, do not dump any complete + * namespaces. If specific namespaces are being dumped, dump just those + * namespaces. Otherwise, dump all non-system namespaces. + */ + if (table_include_oids.head != NULL) + nsinfo->dobj.dump = false; + else if (schema_include_oids.head != NULL) + nsinfo->dobj.dump = simple_oid_list_member(&schema_include_oids, + nsinfo->dobj.catId.oid); + else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 || + strcmp(nsinfo->dobj.name, "information_schema") == 0) + nsinfo->dobj.dump = false; + else + nsinfo->dobj.dump = true; + /* + * In any case, a namespace can be excluded by an exclusion switch + */ + if (nsinfo->dobj.dump && + simple_oid_list_member(&schema_exclude_oids, + nsinfo->dobj.catId.oid)) + nsinfo->dobj.dump = false; +} + +/* + * selectDumpableTable: policy-setting subroutine + * Mark a table as to be dumped or not + */ +static void +selectDumpableTable(TableInfo *tbinfo) +{ + /* + * If specific tables are being dumped, dump just those tables; + * else, dump according to the parent namespace's dump flag. + */ + if (table_include_oids.head != NULL) + tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids, + tbinfo->dobj.catId.oid); + else + tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump; + /* + * In any case, a table can be excluded by an exclusion switch + */ + if (tbinfo->dobj.dump && + simple_oid_list_member(&table_exclude_oids, + tbinfo->dobj.catId.oid)) + tbinfo->dobj.dump = false; } /* @@ -5596,7 +5545,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo) static bool shouldDumpProcLangs(void) { - if (matchingTables != NULL || matchingSchemas != NULL) + if (!include_everything) return false; /* And they're schema not data */ if (dataOnly) diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 65f5e84c41..dad62f9606 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.129 2006/08/21 00:57:25 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.130 2006/10/09 23:36:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -41,6 +41,35 @@ typedef struct typedef int DumpId; +/* + * Data structures for simple lists of OIDs and strings. The support for + * these is very primitive compared to the backend's List facilities, but + * it's all we need in pg_dump. + */ + +typedef struct SimpleOidListCell +{ + struct SimpleOidListCell *next; + Oid val; +} SimpleOidListCell; + +typedef struct SimpleOidList +{ + SimpleOidListCell *head; + SimpleOidListCell *tail; +} SimpleOidList; + +typedef struct SimpleStringListCell +{ + struct SimpleStringListCell *next; + char val[1]; /* VARIABLE LENGTH FIELD */ +} SimpleStringListCell; + +typedef struct SimpleStringList +{ + SimpleStringListCell *head; + SimpleStringListCell *tail; +} SimpleStringList; /* * The data structures used to store system catalog information. Every @@ -364,6 +393,16 @@ extern TypeInfo *findTypeByOid(Oid oid); extern FuncInfo *findFuncByOid(Oid oid); extern OprInfo *findOprByOid(Oid oid); +extern void simple_oid_list_append(SimpleOidList *list, Oid val); +extern void simple_string_list_append(SimpleStringList *list, const char *val); +extern bool simple_oid_list_member(SimpleOidList *list, Oid val); +extern bool simple_string_list_member(SimpleStringList *list, const char *val); + +extern char *pg_strdup(const char *string); +extern void *pg_malloc(size_t size); +extern void *pg_calloc(size_t nmemb, size_t size); +extern void *pg_realloc(void *ptr, size_t size); + extern void check_conn_and_db(void); extern void exit_nicely(void); -- 2.40.0