Hi

2014-08-14 9:03 GMT+02:00 Heikki Linnakangas <hlinnakan...@vmware.com>:

> On 08/14/2014 06:53 AM, Joachim Wieland wrote:
>
>> I'm seeing an assertion failure with "pg_dump -c --if-exists" which is
>> not ready to handle BLOBs it seems:
>>
>> pg_dump: pg_backup_archiver.c:472: RestoreArchive: Assertion `mark !=
>> ((void *)0)' failed.
>>
>> To reproduce:
>>
>> $ createdb test
>> $ pg_dump -c --if-exists test  (works, dumps empty database)
>> $ psql test -c "select lo_create(1);"
>> $ pg_dump -c --if-exists test  (fails, with the above mentioned assertion)
>>
>
> The code tries to inject an "IF EXISTS" into the already-construct DROP
> command, but it doesn't work for large objects, because the deletion
> command looks like "SELECT pg_catalog.lo_unlink(xxx)". There is no DROP
> there.
>
> I believe we could use "SELECT pg_catalog.lo_unlink(loid) FROM
> pg_catalog.pg_largeobject_metadata WHERE loid = xxx".
> pg_largeobject_metadata table didn't exist before version 9.0, but we don't
> guarantee pg_dump's output to be compatible in that direction anyway, so I
> think that's fine.
>
> The quick fix would be to add an exception for blobs, close to where
> Assert is. There are a few exceptions there already. A cleaner solution
> would be to add a new argument to ArchiveEntry and make the callers
> responsible for providing an "DROP IF EXISTS" query, but that's not too
> appetizing because for most callers it would be boring boilerplate code.
> Perhaps add an argument, but if it's NULL, ArchiveEntry would form the
> if-exists query automatically from the DROP query.
>

I am sending two patches

first is fast fix

second fix is implementation of Heikki' idea.




> - Heikki
>
>
>
> --
> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers
>
commit 68ba9d3b682c6e56b08d623ebc58e351ce1a6c55
Author: Pavel Stehule <pavel.steh...@gooddata.com>
Date:   Fri Aug 15 13:41:24 2014 +0200

    heikki-lo-if-exists-fix

diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 3aebac8..0049be7 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -429,60 +429,100 @@ RestoreArchive(Archive *AHX)
 					}
 					else
 					{
-						char		buffer[40];
-						char	   *mark;
-						char	   *dropStmt = pg_strdup(te->dropStmt);
-						char	   *dropStmtPtr = dropStmt;
-						PQExpBuffer ftStmt = createPQExpBuffer();
-
 						/*
-						 * Need to inject IF EXISTS clause after ALTER TABLE
-						 * part in ALTER TABLE .. DROP statement
+						 * LO doesn't use DDL stmts, instead it uses lo_unlink
+						 * functions. But it should be injected too.
 						 */
-						if (strncmp(dropStmt, "ALTER TABLE", 11) == 0)
+						if (strncmp(te->desc, "BLOB", 4) == 0)
 						{
-							appendPQExpBuffer(ftStmt,
-											  "ALTER TABLE IF EXISTS");
-							dropStmt = dropStmt + 11;
+							Oid	   loid = InvalidOid;
+							char	   *mark;
+
+							/* find and take foid */
+							if (strncmp(te->dropStmt, "SELECT pg_catalog.lo_unlink('", 29) == 0)
+							{
+								unsigned long cvt;
+								char	   *endptr;
+							
+								mark = te->dropStmt + 29;
+
+								if (*mark != '\0')
+								{
+									errno = 0;
+									cvt = strtoul(mark, &endptr, 10);
+
+									/* Ensure we have a valid Oid */
+									if (errno == 0 && mark != endptr && *endptr == '\'')
+										loid = (Oid) cvt;
+								}
+							}
+
+							if (loid == InvalidOid)
+								exit_horribly(modulename, "cannot to identify oid of large object\n");
+
+							/*
+							 * Now we have valid foid and we can generate
+							 * fault tolerant (not existency) SQL statement.
+							 */
+							DropBlobIfExists(AH, loid);
 						}
-
-						/*
-						 * ALTER TABLE..ALTER COLUMN..DROP DEFAULT does not
-						 * support the IF EXISTS clause, and therefore we
-						 * simply emit the original command for such objects.
-						 * For other objects, we need to extract the first
-						 * part of the DROP which includes the object type.
-						 * Most of the time this matches te->desc, so search
-						 * for that; however for the different kinds of
-						 * CONSTRAINTs, we know to search for hardcoded "DROP
-						 * CONSTRAINT" instead.
-						 */
-						if (strcmp(te->desc, "DEFAULT") == 0)
-							appendPQExpBuffer(ftStmt, "%s", dropStmt);
 						else
 						{
-							if (strcmp(te->desc, "CONSTRAINT") == 0 ||
-								strcmp(te->desc, "CHECK CONSTRAINT") == 0 ||
-								strcmp(te->desc, "FK CONSTRAINT") == 0)
-								strcpy(buffer, "DROP CONSTRAINT");
+							char		buffer[40];
+							char	   *mark;
+							char	   *dropStmt = pg_strdup(te->dropStmt);
+							char	   *dropStmtPtr = 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;
+							}
+
+							/*
+							 * ALTER TABLE..ALTER COLUMN..DROP DEFAULT does not
+							 * support the IF EXISTS clause, and therefore we
+							 * simply emit the original command for such objects.
+							 * For other objects, we need to extract the first
+							 * part of the DROP which includes the object type.
+							 * Most of the time this matches te->desc, so search
+							 * for that; however for the different kinds of
+							 * CONSTRAINTs, we know to search for hardcoded "DROP
+							 * CONSTRAINT" instead.
+							 */
+							if (strcmp(te->desc, "DEFAULT") == 0)
+								appendPQExpBuffer(ftStmt, "%s", dropStmt);
 							else
-								snprintf(buffer, sizeof(buffer), "DROP %s",
-										 te->desc);
+							{
+								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 = strstr(dropStmt, buffer);
+								Assert(mark != NULL);
 
-							*mark = '\0';
-							appendPQExpBuffer(ftStmt, "%s%s IF EXISTS%s",
-											  dropStmt, buffer,
-											  mark + strlen(buffer));
-						}
+								*mark = '\0';
+								appendPQExpBuffer(ftStmt, "%s%s IF EXISTS%s",
+												  dropStmt, buffer,
+												  mark + strlen(buffer));
+							}
 
-						ahprintf(AH, "%s", ftStmt->data);
+							ahprintf(AH, "%s", ftStmt->data);
 
-						destroyPQExpBuffer(ftStmt);
+							destroyPQExpBuffer(ftStmt);
 
-						pg_free(dropStmtPtr);
+							pg_free(dropStmtPtr);
+						}
 					}
 				}
 			}
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
new file mode 100644
index 3aebac8..3c2ee84
*** a/src/bin/pg_dump/pg_backup_archiver.c
--- b/src/bin/pg_dump/pg_backup_archiver.c
*************** RestoreArchive(Archive *AHX)
*** 435,440 ****
--- 435,443 ----
  						char	   *dropStmtPtr = dropStmt;
  						PQExpBuffer ftStmt = createPQExpBuffer();
  
+ 						if (strncmp(te->desc, "BLOB", 4) == 0)
+ 							exit_horribly(modulename, "large objects doesn't support --if-exists option\n");
+ 
  						/*
  						 * Need to inject IF EXISTS clause after ALTER TABLE
  						 * part in ALTER TABLE .. DROP statement
-- 
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