Hello

2014-02-17 22:14 GMT+01:00 Pavel Stehule <pavel.steh...@gmail.com>:

> Hello
>
>
> 2014-02-17 18:10 GMT+01:00 Alvaro Herrera <alvhe...@2ndquadrant.com>:
>
> Jeevan Chalke escribió:
>>
>> I don't understand this code.  (Well, it's pg_dump.)  Or maybe I do
>> understand it, and it's not doing what you think it's doing.  I mean, in
>> this part:
>>
>> > diff --git a/src/bin/pg_dump/pg_backup_archiver.c
>> b/src/bin/pg_dump/pg_backup_archiver.c
>> > index 7fc0288..c08a0d3 100644
>> > --- a/src/bin/pg_dump/pg_backup_archiver.c
>> > +++ b/src/bin/pg_dump/pg_backup_archiver.c
>> > @@ -413,8 +413,84 @@ RestoreArchive(Archive *AHX)
>> >                               /* Select owner and schema as necessary */
>> >                               _becomeOwner(AH, te);
>> >                               _selectOutputSchema(AH, te->namespace);
>> > -                             /* Drop it */
>> > -                             ahprintf(AH, "%s", te->dropStmt);
>> > +
>> > +                             if (*te->dropStmt != '\0')
>> > +                             {
>> > +                                     /* Inject IF EXISTS clause to
>> DROP part when required. */
>> > +                                     if (ropt->if_exists)
>>
>> It does *not* modify te->dropStmt, it only sends ahprint() a different
>> version of what was stored (injected the wanted IF EXISTS clause).  If
>> that is correct, then why are we, in this other part, trying to remove
>> the IF EXISTS clause?
>>
>
> we should not to modify te->dropStmt, because only in this fragment a DROP
> STATEMENT is produced. This additional logic ensures correct syntax for all
> variation of DROP.
>
> When I wrote this patch I had a initial problem with understanding
> relation between pg_dump and pg_restore. And I pushed IF EXISTS to all
> related DROP statements producers. But I was wrong. All the drop statements
> are reparsed and transformed and serialized in this fragment. So only this
> fragment should be modified. IF EXISTS clause can be injected before, when
> you read plain text dump (produced by pg_dump --if-exists) in pg_restore.
>

I was wrong - "IF EXISTS" was there, because I generated DROP IF EXISTS
elsewhere in some very old stages of this patch. It is useless to check it
there now.


>
>
>>
>> > @@ -2942,9 +3018,39 @@ _getObjectDescription(PQExpBuffer buf, TocEntry
>> *te, ArchiveHandle *AH)
>> >               strcmp(type, "OPERATOR CLASS") == 0 ||
>> >               strcmp(type, "OPERATOR FAMILY") == 0)
>> >       {
>> > -             /* Chop "DROP " off the front and make a modifiable copy
>> */
>> > -             char       *first = pg_strdup(te->dropStmt + 5);
>> > -             char       *last;
>> > +             char        *first;
>> > +             char        *last;
>> > +
>> > +             /*
>> > +              * Object description is based on dropStmt statement
>> which may have
>> > +              * IF EXISTS clause.  Thus we need to update an offset
>> such that it
>> > +              * won't be included in the object description.
>> > +              */
>>
>> Maybe I am mistaken and the te->dropStmt already contains the IF EXISTS
>> bit for some reason; but if so I don't know why that is.  Care to
>> explain?
>>
>
> pg_restore is available to read plain dump produced by pg_dump
> --if-exists. It is way how IF EXISTS can infect te->dropStmt
>
>
>>
>> I also think that _getObjectDescription() becomes overworked after this
>> patch.  I wonder if we should be storing te->objIdentity so that we can
>> construct the ALTER OWNER command without going to as much trouble as
>> parsing the DROP command.  Is there a way to do that? Maybe we can ask
>> the server for the object identity, for example.  There is a new
>> function to do that in 9.3 which perhaps we can now use.
>>
>>
> do you think a pg_describe_object function?
>
> Probably it is possible, but its significantly much more invasive change,
> you should to get objidentity, that is not trivial
>
> Regards
>

It is irony, so this is death code - it is not used now. So I removed it
from patch.

Reduced, fixed patch attached + used tests

Regards

Pavel




>
> Pavel
>
>
>> --
>> Álvaro Herrera                http://www.2ndQuadrant.com/
>> PostgreSQL Development, 24x7 Support, Training & Services
>>
>
>
commit 937830b60920a8fac84cd2adb24c3d1b5280b036
Author: Pavel Stehule <pavel.steh...@gooddata.com>
Date:   Tue Feb 18 14:25:40 2014 +0100

    reduced2

diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 8d45f24..c39767b 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -650,6 +650,16 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>--if-exists</option></term>
+      <listitem>
+       <para>
+        It uses conditional commands (with <literal>IF EXISTS</literal>
+        clause) for cleaning database schema.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>--disable-dollar-quoting</></term>
       <listitem>
        <para>
diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index 5c6a101..ba6583d 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -301,6 +301,16 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>--if-exists</option></term>
+      <listitem>
+       <para>
+        It uses conditional commands (with <literal>IF EXISTS</literal>
+        clause) for cleaning database schema.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>--inserts</option></term>
       <listitem>
        <para>
diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml
index 717da42..55326d5 100644
--- a/doc/src/sgml/ref/pg_restore.sgml
+++ b/doc/src/sgml/ref/pg_restore.sgml
@@ -490,6 +490,16 @@
      </varlistentry>
 
      <varlistentry>
+      <term><option>--if-exists</option></term>
+      <listitem>
+       <para>
+        It uses conditional commands (with <literal>IF EXISTS</literal>
+        clause) for cleaning database schema.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>--no-data-for-failed-tables</option></term>
       <listitem>
        <para>
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 6927968..83f7216 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -113,6 +113,7 @@ typedef struct _restoreOptions
 	char	   *superuser;		/* Username to use as superuser */
 	char	   *use_role;		/* Issue SET ROLE to this */
 	int			dropSchema;
+	int			if_exists;
 	const char *filename;
 	int			dataOnly;
 	int			schemaOnly;
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 2b36e45..f36caf2 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -413,8 +413,64 @@ RestoreArchive(Archive *AHX)
 				/* Select owner and schema as necessary */
 				_becomeOwner(AH, te);
 				_selectOutputSchema(AH, te->namespace);
-				/* Drop it */
-				ahprintf(AH, "%s", te->dropStmt);
+
+				if (*te->dropStmt != '\0')
+				{
+					/* Inject IF EXISTS clause to DROP part when required. */
+					if (ropt->if_exists)
+					{
+						char buffer[40];
+						char *mark;
+						char *dropStmt = te->dropStmt;
+						PQExpBuffer ftStmt = createPQExpBuffer();
+
+						/*
+						 * Need to inject IF EXISTS clause after ALTER TABLE
+						 * part in ALTER TABLE .. DROP statement
+						 */
+						if (strncmp(dropStmt, "ALTER TABLE", 11) == 0)
+						{
+							appendPQExpBuffer(ftStmt,
+											  "ALTER TABLE IF EXISTS");
+							dropStmt = dropStmt + 11;
+						}
+
+						/*
+						 * A content of te->desc is usually substring of a DROP
+						 * statement with one related exception - CONSTRAINTs.
+						 *
+						 * Independent to previous sentence, ALTER TABLE ..
+						 * ALTER COLUMN .. DROP DEFAULT statement does not
+						 * support IF EXISTS clause and therefore it should not
+						 * be injected.
+						 */
+						if (strcmp(te->desc, "DEFAULT") != 0)
+						{
+							if (strcmp(te->desc, "CONSTRAINT") == 0 ||
+								strcmp(te->desc, "CHECK CONSTRAINT") == 0 ||
+								strcmp(te->desc, "FK CONSTRAINT") == 0)
+								strcpy(buffer, "DROP CONSTRAINT");
+							else
+								snprintf(buffer, sizeof(buffer), "DROP %s",
+													 te->desc);
+
+							mark = strstr(dropStmt, buffer);
+							Assert(mark != NULL);
+
+							*mark = '\0';
+							appendPQExpBuffer(ftStmt, "%s%s IF EXISTS%s",
+										  dropStmt, buffer, mark + strlen(buffer));
+						}
+						else
+							appendPQExpBuffer(ftStmt, "%s", dropStmt);
+
+						ahprintf(AH, "%s", ftStmt->data);
+
+						destroyPQExpBuffer(ftStmt);
+					}
+					else
+						ahprintf(AH, "%s", te->dropStmt);
+				}
 			}
 		}
 
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 458a118..88ea556 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -136,6 +136,7 @@ static int	binary_upgrade = 0;
 static int	disable_dollar_quoting = 0;
 static int	dump_inserts = 0;
 static int	column_inserts = 0;
+static int	if_exists = 0;
 static int	no_security_labels = 0;
 static int	no_synchronized_snapshots = 0;
 static int	no_unlogged_table_data = 0;
@@ -349,6 +350,7 @@ main(int argc, char **argv)
 		{"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1},
 		{"disable-triggers", no_argument, &disable_triggers, 1},
 		{"exclude-table-data", required_argument, NULL, 4},
+		{"if-exists", no_argument, &if_exists, 1},
 		{"inserts", no_argument, &dump_inserts, 1},
 		{"lock-wait-timeout", required_argument, NULL, 2},
 		{"no-tablespaces", no_argument, &outputNoTablespaces, 1},
@@ -577,6 +579,9 @@ main(int argc, char **argv)
 		exit_nicely(1);
 	}
 
+	if (if_exists && !outputClean)
+		exit_horribly(NULL, "option --if-exists requires -c/--clean option\n");
+
 	/* Identify archive format to emit */
 	archiveFormat = parseArchiveFormat(format, &archiveMode);
 
@@ -809,6 +814,7 @@ main(int argc, char **argv)
 	ropt->dropSchema = outputClean;
 	ropt->dataOnly = dataOnly;
 	ropt->schemaOnly = schemaOnly;
+	ropt->if_exists = if_exists;
 	ropt->dumpSections = dumpSections;
 	ropt->aclsSkip = aclsSkip;
 	ropt->superuser = outputSuperuser;
@@ -890,6 +896,7 @@ help(const char *progname)
 	printf(_("  --disable-dollar-quoting     disable dollar quoting, use SQL standard quoting\n"));
 	printf(_("  --disable-triggers           disable triggers during data-only restore\n"));
 	printf(_("  --exclude-table-data=TABLE   do NOT dump data for the named table(s)\n"));
+	printf(_("  --if-exists                  use IF EXISTS when dropping objects\n"));
 	printf(_("  --inserts                    dump data as INSERT commands, rather than COPY\n"));
 	printf(_("  --no-security-labels         do not dump security label assignments\n"));
 	printf(_("  --no-synchronized-snapshots  do not use synchronized snapshots in parallel jobs\n"));
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 193c1a0..335fdde 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -73,6 +73,7 @@ static int	binary_upgrade = 0;
 static int	column_inserts = 0;
 static int	disable_dollar_quoting = 0;
 static int	disable_triggers = 0;
+static int	if_exists = 0;
 static int	inserts = 0;
 static int	no_tablespaces = 0;
 static int	use_setsessauth = 0;
@@ -119,6 +120,7 @@ main(int argc, char *argv[])
 		{"column-inserts", no_argument, &column_inserts, 1},
 		{"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1},
 		{"disable-triggers", no_argument, &disable_triggers, 1},
+		{"if-exists", no_argument, &if_exists, 1},
 		{"inserts", no_argument, &inserts, 1},
 		{"lock-wait-timeout", required_argument, NULL, 2},
 		{"no-tablespaces", no_argument, &no_tablespaces, 1},
@@ -352,6 +354,8 @@ main(int argc, char *argv[])
 		appendPQExpBufferStr(pgdumpopts, " --disable-dollar-quoting");
 	if (disable_triggers)
 		appendPQExpBufferStr(pgdumpopts, " --disable-triggers");
+	if (if_exists)
+		appendPQExpBufferStr(pgdumpopts, " --if-exists");
 	if (inserts)
 		appendPQExpBufferStr(pgdumpopts, " --inserts");
 	if (no_tablespaces)
@@ -564,6 +568,7 @@ help(void)
 	printf(_("  --column-inserts             dump data as INSERT commands with column names\n"));
 	printf(_("  --disable-dollar-quoting     disable dollar quoting, use SQL standard quoting\n"));
 	printf(_("  --disable-triggers           disable triggers during data-only restore\n"));
+	printf(_("  --if-exists                  use IF EXISTS when dropping objects\n"));
 	printf(_("  --inserts                    dump data as INSERT commands, rather than COPY\n"));
 	printf(_("  --no-security-labels         do not dump security label assignments\n"));
 	printf(_("  --no-tablespaces             do not dump tablespace assignments\n"));
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index bb65253..f732027 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -76,6 +76,7 @@ main(int argc, char **argv)
 	Archive    *AH;
 	char	   *inputFileSpec;
 	static int	disable_triggers = 0;
+	static int	if_exists = 0;
 	static int	no_data_for_failed_tables = 0;
 	static int	outputNoTablespaces = 0;
 	static int	use_setsessauth = 0;
@@ -116,6 +117,7 @@ main(int argc, char **argv)
 		 * the following options don't have an equivalent short option letter
 		 */
 		{"disable-triggers", no_argument, &disable_triggers, 1},
+		{"if-exists", no_argument, &if_exists, 1},
 		{"no-data-for-failed-tables", no_argument, &no_data_for_failed_tables, 1},
 		{"no-tablespaces", no_argument, &outputNoTablespaces, 1},
 		{"role", required_argument, NULL, 2},
@@ -342,6 +344,14 @@ main(int argc, char **argv)
 	opts->use_setsessauth = use_setsessauth;
 	opts->no_security_labels = no_security_labels;
 
+	if (if_exists && !opts->dropSchema)
+	{
+		fprintf(stderr, _("%s: option --if-exists requires -c/--clean option\n"),
+				progname);
+		exit_nicely(1);
+	}
+	opts->if_exists = if_exists;
+
 	if (opts->formatName)
 	{
 		switch (opts->formatName[0])
@@ -456,6 +466,7 @@ usage(const char *progname)
 	printf(_("  -x, --no-privileges          skip restoration of access privileges (grant/revoke)\n"));
 	printf(_("  -1, --single-transaction     restore as a single transaction\n"));
 	printf(_("  --disable-triggers           disable triggers during data-only restore\n"));
+	printf(_("  --if-exists                  use IF EXISTS when dropping objects\n"));
 	printf(_("  --no-data-for-failed-tables  do not restore data of tables that could not be\n"
 			 "                               created\n"));
 	printf(_("  --no-security-labels         do not restore security labels\n"));

Attachment: dumptest.sql
Description: application/sql

Attachment: Makefile
Description: Binary data

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to