 Make pg_restore apply TOC entries statement by statement.
 Previously, the whole TOC entry was restored with a single PQexec, which caused all statements to fail if one of them failed.
 This behaviour was different from restoring a plain format dump, where the statements were restored individually.

---
 src/bin/pg_dump/pg_backup_archiver.c |  131 +++++++++++++++++++++++++++++++++-
 1 files changed, 130 insertions(+), 1 deletions(-)

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..dd5530f
*** a/src/bin/pg_dump/pg_backup_archiver.c
--- b/src/bin/pg_dump/pg_backup_archiver.c
*************** static ArchiveHandle *_allocAH(const cha
*** 55,60 ****
--- 55,61 ----
  	 const int compression, ArchiveMode mode, SetupWorkerPtr setupWorkerPtr);
  static void _getObjectDescription(PQExpBuffer buf, TocEntry *te,
  					  ArchiveHandle *AH);
+ static char *_nextStatement(char **p);
  static void _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData, bool acl_pass);
  static char *replace_line_endings(const char *str);
  static void _doSetFixedOutputState(ArchiveHandle *AH);
*************** _printTocEntry(ArchiveHandle *AH, TocEnt
*** 3177,3183 ****
  	else
  	{
  		if (strlen(te->defn) > 0)
! 			ahprintf(AH, "%s\n\n", te->defn);
  	}
  
  	/*
--- 3178,3205 ----
  	else
  	{
  		if (strlen(te->defn) > 0)
! 		{
! 			/*
! 			 * Break up the definition into individual statements and issue
! 			 * them one by one.  If we didn't do that, plain format dumps
! 			 * would behave differently, because there the dump is restored
! 			 * statement by statement.  This can cause different behaviour
! 			 * if one of the statements fails.
! 			 */
! 			char *p = te->defn;
! 
! 			while (*p != '\0')
! 			{
! 				char *stmt = _nextStatement(&p);
! 
! 				/* append newlines only to the last statement */
! 				if (*p == '\0')
! 					ahprintf(AH, "%s\n\n", stmt);
! 				else
! 					ahprintf(AH, "%s", stmt);
! 				free(stmt);
! 			}
! 		}
  	}
  
  	/*
*************** _printTocEntry(ArchiveHandle *AH, TocEnt
*** 3252,3257 ****
--- 3274,3386 ----
  }
  
  /*
+  * Parse *command and extract the next SQL statement.
+  * The next statement is returned in a newly allocated string,
+  * and *command is changed to point to the beginning of the next statement.
+  */
+ static char *
+ _nextStatement(char **command)
+ {
+ 	char *p, *q, *ret, h, *message;
+ 	int status = 0;  /* 0 normal, 1 single quotes, 2 double quotes, 3 problem */
+ 
+ 	for (p = *command; *p != '\0' && status != 3 && (*p != ';' || status != 0); ++p)
+ 	{
+ 		switch (*p)
+ 		{
+ 			case '\'':
+ 				if (status == 0)
+ 					status = 1;
+ 				else if (status == 1)
+ 					status = 0;
+ 				break;
+ 			case '"':
+ 				if (status == 0)
+ 					status = 2;
+ 				else if (status == 2)
+ 					status = 0;
+ 				break;
+ 			case '$':
+ 				if (status != 0)
+ 					continue;
+ 
+ 				/*
+ 				 * We don't need a full-fledged parser for dollar quotes
+ 				 * here because we can assume that the dump file was written
+ 				 * by pg_dump.
+ 				 * We assume that all names containing a dollar sign will be double
+ 				 * quoted and that no parameters like $1 will be in SQL statements.
+ 				 * Then every occurrence of a dollar sign outside quotes would be
+ 				 * part of a dollar quote.
+ 				 */
+ 				for (q = p + 1; *q != '\0' && *q != '$'; ++q)
+ 					;
+ 				if (*q == '$')
+ 				{
+ 					char *limit;
+ 
+ 					/* copy delimiter including dollars */
+ 					h = *(q + 1);
+ 					*(q + 1) = '\0';
+ 					limit = pg_strdup(p);
+ 					*(q + 1) = h;
+ 
+ 					/* find next occurrence of delimiter */
+ 					q = strstr(q + 1, limit);
+ 
+ 					/* skip quoted string if found */
+ 					if (q)
+ 						p = q + (strlen(limit) - 1);
+ 					else
+ 					{
+ 						message = "unfinished dollar quote";
+ 						status = 3;
+ 					}
+ 
+ 					free(limit);
+ 				}
+ 				else
+ 				{
+ 					message = "dollar sign is not part of a dollar quote";
+ 					status = 3;
+ 				}
+ 				break;
+ 			default:
+ 				continue;
+ 		}
+ 	}
+ 
+ 	/* skip to the beginning of the next non-whitespace */
+ 	while (*p == ';' || *p == '\n' || *p == '\r' || *p == ' ')
+ 		++p;
+ 
+ 	if (*p == '\0' && status != 0)
+ 	{
+ 		message = "unfinished quote";
+ 		status = 3;
+ 	}
+ 
+ 	if (status == 3)
+ 	{
+ 		write_msg(modulename, "WARNING: %s", message);
+ 
+ 		/* don't split the command string if there was a parsing problem */
+ 		ret = pg_strdup(*command);
+ 		*command += strlen(*command);
+ 	}
+ 	else
+ 	{
+ 		h = *p;
+ 		*p = '\0';
+ 		ret = pg_strdup(*command);
+ 		*p = h;
+ 		*command = p;
+ 	}
+ 
+ 	return ret;
+ }
+ 
+ /*
   * Sanitize a string to be included in an SQL comment, by replacing any
   * newlines with spaces.
   */
-- 
1.7.1

