diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 0e129f9654..ed0076573e 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1866,6 +1866,91 @@ dumpTableData_copy(Archive *fout, void *dcontext)
 	return 1;
 }
 
+/*
+ * putArchiveRow
+ *		Write the 'tuple'th row from 'res' to 'fout'.
+ *		'nfields' must match PQnfields(res) and not be 0.
+ *
+ * 'buf' must be a pre-allocated PQExpBuffer which is used as a temporary
+ * buffer in the function. The contents of which should not be expected to
+ * remain after this function returns.
+ */
+static void
+putArchiveRow(Archive *fout, PQExpBuffer buf, PGresult *res, int tuple,
+			  int nfields)
+{
+	int			field;
+
+	Assert(nfields > 0);
+
+	archputs("(", fout);
+
+	for (field = 0; field < nfields; field++)
+	{
+		if (field > 0)
+			archputs(", ", fout);
+		if (PQgetisnull(res, tuple, field))
+		{
+			archputs("NULL", fout);
+			continue;
+		}
+
+		/* XXX This code is partially duplicated in ruleutils.c */
+		switch (PQftype(res, field))
+		{
+			case INT2OID:
+			case INT4OID:
+			case INT8OID:
+			case OIDOID:
+			case FLOAT4OID:
+			case FLOAT8OID:
+			case NUMERICOID:
+				{
+					/*
+					 * These types are printed without quotes unless they
+					 * contain values that aren't accepted by the scanner
+					 * unquoted (e.g., 'NaN').  Note that strtod() and friends
+					 * might accept NaN, so we can't use that to test.
+					 *
+					 * In reality we only need to defend against infinity and
+					 * NaN, so we need not get too crazy about pattern
+					 * matching here.
+					 */
+					const char *s = PQgetvalue(res, tuple, field);
+
+					if (strspn(s, "0123456789 +-eE.") == strlen(s))
+						archputs(s, fout);
+					else
+						archprintf(fout, "'%s'", s);
+				}
+				break;
+
+			case BITOID:
+			case VARBITOID:
+				archprintf(fout, "B'%s'",
+						   PQgetvalue(res, tuple, field));
+				break;
+
+			case BOOLOID:
+				if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
+					archputs("true", fout);
+				else
+					archputs("false", fout);
+				break;
+
+			default:
+				/* All other types are printed as string literals. */
+				resetPQExpBuffer(buf);
+				appendStringLiteralAH(buf,
+									  PQgetvalue(res, tuple, field),
+									  fout);
+				archputs(buf->data, fout);
+				break;
+		}
+	}
+	archputs(")", fout);
+}
+
 /*
  * Dump table data using INSERT commands.
  *
@@ -1886,6 +1971,8 @@ dumpTableData_insert(Archive *fout, void *dcontext)
 	int			tuple;
 	int			nfields;
 	int			field;
+	int			rows_per_statement = 3;
+	int			rows_this_statement = 0;
 
 	appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
 					  "SELECT * FROM ONLY %s",
@@ -1900,137 +1987,97 @@ dumpTableData_insert(Archive *fout, void *dcontext)
 		res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
 							  PGRES_TUPLES_OK);
 		nfields = PQnfields(res);
-		for (tuple = 0; tuple < PQntuples(res); tuple++)
+
+		/*
+		 * First time through, we build as much of the INSERT statement as
+		 * possible in "insertStmt", which we can then just print for each
+		 * line. If the table happens to have zero columns then this will be a
+		 * complete statement, otherwise it will end in "VALUES " and be ready
+		 * to have the row's column values appended.
+		 */
+		if (insertStmt == NULL)
 		{
-			/*
-			 * First time through, we build as much of the INSERT statement as
-			 * possible in "insertStmt", which we can then just print for each
-			 * line. If the table happens to have zero columns then this will
-			 * be a complete statement, otherwise it will end in "VALUES(" and
-			 * be ready to have the row's column values appended.
-			 */
-			if (insertStmt == NULL)
-			{
-				TableInfo  *targettab;
+			TableInfo  *targettab;
 
-				insertStmt = createPQExpBuffer();
+			insertStmt = createPQExpBuffer();
 
-				/*
-				 * When load-via-partition-root is set, get the root table
-				 * name for the partition table, so that we can reload data
-				 * through the root table.
-				 */
-				if (dopt->load_via_partition_root && tbinfo->ispartition)
-					targettab = getRootTableInfo(tbinfo);
-				else
-					targettab = tbinfo;
+			/*
+			 * When load-via-partition-root is set, get the root table name
+			 * for the partition table, so that we can reload data through the
+			 * root table.
+			 */
+			if (dopt->load_via_partition_root && tbinfo->ispartition)
+				targettab = getRootTableInfo(tbinfo);
+			else
+				targettab = tbinfo;
 
-				appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
-								  fmtQualifiedDumpable(targettab));
+			appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
+							  fmtQualifiedDumpable(targettab));
 
-				/* corner case for zero-column table */
-				if (nfields == 0)
-				{
-					appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
-				}
-				else
+			/* corner case for zero-column table */
+			if (nfields == 0)
+			{
+				appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
+			}
+			else
+			{
+				/* append the list of column names if required */
+				if (dopt->column_inserts)
 				{
-					/* append the list of column names if required */
-					if (dopt->column_inserts)
+					appendPQExpBufferChar(insertStmt, '(');
+					for (field = 0; field < nfields; field++)
 					{
-						appendPQExpBufferChar(insertStmt, '(');
-						for (field = 0; field < nfields; field++)
-						{
-							if (field > 0)
-								appendPQExpBufferStr(insertStmt, ", ");
-							appendPQExpBufferStr(insertStmt,
-												 fmtId(PQfname(res, field)));
-						}
-						appendPQExpBufferStr(insertStmt, ") ");
+						if (field > 0)
+							appendPQExpBufferStr(insertStmt, ", ");
+						appendPQExpBufferStr(insertStmt,
+											 fmtId(PQfname(res, field)));
 					}
+					appendPQExpBufferStr(insertStmt, ") ");
+				}
 
-					if (tbinfo->needs_override)
-						appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
+				if (tbinfo->needs_override)
+					appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
 
-					appendPQExpBufferStr(insertStmt, "VALUES (");
-				}
+				appendPQExpBufferStr(insertStmt, "VALUES ");
 			}
+		}
 
-			archputs(insertStmt->data, fout);
+		for (tuple = 0; tuple < PQntuples(res); tuple++)
+		{
+			/*
+			 * If we've not written the initial part of the statement yet then
+			 * do so now.
+			 */
+			if (rows_this_statement == 0)
+				archputs(insertStmt->data, fout);
 
-			/* if it is zero-column table then we're done */
+			/*
+			 * If it is zero-column table then we've written all we need to.
+			 * We're unable to do multi-inserts for this case due to lack of a
+			 * valid syntax, so continue to use single row statements
+			 */
 			if (nfields == 0)
 				continue;
 
-			for (field = 0; field < nfields; field++)
-			{
-				if (field > 0)
-					archputs(", ", fout);
-				if (PQgetisnull(res, tuple, field))
-				{
-					archputs("NULL", fout);
-					continue;
-				}
-
-				/* XXX This code is partially duplicated in ruleutils.c */
-				switch (PQftype(res, field))
-				{
-					case INT2OID:
-					case INT4OID:
-					case INT8OID:
-					case OIDOID:
-					case FLOAT4OID:
-					case FLOAT8OID:
-					case NUMERICOID:
-						{
-							/*
-							 * These types are printed without quotes unless
-							 * they contain values that aren't accepted by the
-							 * scanner unquoted (e.g., 'NaN').  Note that
-							 * strtod() and friends might accept NaN, so we
-							 * can't use that to test.
-							 *
-							 * In reality we only need to defend against
-							 * infinity and NaN, so we need not get too crazy
-							 * about pattern matching here.
-							 */
-							const char *s = PQgetvalue(res, tuple, field);
-
-							if (strspn(s, "0123456789 +-eE.") == strlen(s))
-								archputs(s, fout);
-							else
-								archprintf(fout, "'%s'", s);
-						}
-						break;
+			if (rows_this_statement > 0)
+				archputs(", ", fout);
 
-					case BITOID:
-					case VARBITOID:
-						archprintf(fout, "B'%s'",
-								   PQgetvalue(res, tuple, field));
-						break;
+			putArchiveRow(fout, q, res, tuple, nfields);
+			rows_this_statement++;
 
-					case BOOLOID:
-						if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
-							archputs("true", fout);
-						else
-							archputs("false", fout);
-						break;
-
-					default:
-						/* All other types are printed as string literals. */
-						resetPQExpBuffer(q);
-						appendStringLiteralAH(q,
-											  PQgetvalue(res, tuple, field),
-											  fout);
-						archputs(q->data, fout);
-						break;
-				}
+			/*
+			 * If we've put the target number of rows onto this statement then
+			 * we can terminate it now.
+			 */
+			if (rows_this_statement == rows_per_statement)
+			{
+				/* reset the row counter */
+				rows_this_statement = 0;
+				if (!dopt->do_nothing)
+					archputs(";\n", fout);
+				else
+					archputs(" ON CONFLICT DO NOTHING;\n", fout);
 			}
-
-			if (!dopt->do_nothing)
-				archputs(");\n", fout);
-			else
-				archputs(") ON CONFLICT DO NOTHING;\n", fout);
 		}
 
 		if (PQntuples(res) <= 0)
@@ -2041,6 +2088,16 @@ dumpTableData_insert(Archive *fout, void *dcontext)
 		PQclear(res);
 	}
 
+	/* terminate any partially written statement */
+	if (rows_this_statement > 0)
+	{
+		if (!dopt->do_nothing)
+			archputs(";\n", fout);
+		else
+			archputs(" ON CONFLICT DO NOTHING;\n", fout);
+	}
+
+
 	archputs("\n\n", fout);
 
 	ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
