@@ -138,6 +138,12 @@ static const CatalogId nilCatalogId = {0, 0};
138138static bool have_extra_float_digits = false;
139139static int extra_float_digits;
140140
141+ /*
142+ * The default number of rows per INSERT when
143+ * --inserts is specified without --rows-per-insert
144+ */
145+ #define DUMP_DEFAULT_ROWS_PER_INSERT 1
146+
141147/*
142148 * Macro for producing quoted, schema-qualified name of a dumpable object.
143149 */
@@ -306,11 +312,13 @@ main(int argc, char **argv)
306312DumpableObject *boundaryObjs;
307313inti;
308314intoptindex;
315+ char *endptr;
309316RestoreOptions *ropt;
310317Archive *fout;/* the script file */
311318const char *dumpencoding = NULL;
312319const char *dumpsnapshot = NULL;
313320char *use_role = NULL;
321+ longrowsPerInsert;
314322intnumWorkers = 1;
315323trivalueprompt_password = TRI_DEFAULT;
316324intcompressLevel = -1;
@@ -363,7 +371,7 @@ main(int argc, char **argv)
363371{"exclude-table-data", required_argument, NULL, 4},
364372{"extra-float-digits", required_argument, NULL, 8},
365373{"if-exists", no_argument, &dopt.if_exists, 1},
366- {"inserts", no_argument,&dopt.dump_inserts, 1 },
374+ {"inserts", no_argument,NULL, 9 },
367375{"lock-wait-timeout", required_argument, NULL, 2},
368376{"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
369377{"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
@@ -382,6 +390,7 @@ main(int argc, char **argv)
382390{"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
383391{"no-sync", no_argument, NULL, 7},
384392{"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
393+ {"rows-per-insert", required_argument, NULL, 10},
385394
386395{NULL, 0, NULL, 0}
387396};
@@ -572,6 +581,31 @@ main(int argc, char **argv)
572581}
573582break;
574583
584+ case 9:/* inserts */
585+
586+ /*
587+ * dump_inserts also stores --rows-per-insert, careful not to
588+ * overwrite that.
589+ */
590+ if (dopt.dump_inserts == 0)
591+ dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
592+ break;
593+
594+ case 10:/* rows per insert */
595+ errno = 0;
596+ rowsPerInsert = strtol(optarg, &endptr, 10);
597+
598+ if (endptr == optarg || *endptr != '\0' ||
599+ rowsPerInsert <= 0 || rowsPerInsert > INT_MAX ||
600+ errno == ERANGE)
601+ {
602+ write_msg(NULL, "rows-per-insert must be in range %d..%d\n",
603+ 1, INT_MAX);
604+ exit_nicely(1);
605+ }
606+ dopt.dump_inserts = (int) rowsPerInsert;
607+ break;
608+
575609default:
576610fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
577611exit_nicely(1);
@@ -596,8 +630,8 @@ main(int argc, char **argv)
596630}
597631
598632/* --column-inserts implies --inserts */
599- if (dopt.column_inserts)
600- dopt.dump_inserts =1 ;
633+ if (dopt.column_inserts && dopt.dump_inserts == 0 )
634+ dopt.dump_inserts =DUMP_DEFAULT_ROWS_PER_INSERT ;
601635
602636/*
603637 * Binary upgrade mode implies dumping sequence data even in schema-only
@@ -622,8 +656,12 @@ main(int argc, char **argv)
622656if (dopt.if_exists && !dopt.outputClean)
623657exit_horribly(NULL, "option --if-exists requires option -c/--clean\n");
624658
625- if (dopt.do_nothing && !(dopt.dump_inserts || dopt.column_inserts))
626- exit_horribly(NULL, "option --on-conflict-do-nothing requires option --inserts or --column-inserts\n");
659+ /*
660+ * --inserts are already implied above if --column-inserts or
661+ * --rows-per-insert were specified.
662+ */
663+ if (dopt.do_nothing && dopt.dump_inserts == 0)
664+ exit_horribly(NULL, "option --on-conflict-do-nothing requires option --inserts, --rows-per-insert or --column-inserts\n");
627665
628666/* Identify archive format to emit */
629667archiveFormat = parseArchiveFormat(format, &archiveMode);
@@ -993,6 +1031,7 @@ help(const char *progname)
9931031printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
9941032printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
9951033printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1034+ printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
9961035printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
9971036printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
9981037printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
@@ -1909,9 +1948,9 @@ dumpTableData_insert(Archive *fout, void *dcontext)
19091948PQExpBuffer q = createPQExpBuffer();
19101949PQExpBuffer insertStmt = NULL;
19111950PGresult *res;
1912- inttuple;
19131951intnfields;
1914- intfield;
1952+ introws_per_statement = dopt->dump_inserts;
1953+ introws_this_statement = 0;
19151954
19161955appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
19171956 "SELECT * FROM ONLY %s",
@@ -1926,69 +1965,88 @@ dumpTableData_insert(Archive *fout, void *dcontext)
19261965res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
19271966 PGRES_TUPLES_OK);
19281967nfields = PQnfields(res);
1929- for (tuple = 0; tuple < PQntuples(res); tuple++)
1968+
1969+ /*
1970+ * First time through, we build as much of the INSERT statement as
1971+ * possible in "insertStmt", which we can then just print for each
1972+ * statement. If the table happens to have zero columns then this will
1973+ * be a complete statement, otherwise it will end in "VALUES" and be
1974+ * ready to have the row's column values printed.
1975+ */
1976+ if (insertStmt == NULL)
19301977{
1931- /*
1932- * First time through, we build as much of the INSERT statement as
1933- * possible in "insertStmt", which we can then just print for each
1934- * line. If the table happens to have zero columns then this will
1935- * be a complete statement, otherwise it will end in "VALUES(" and
1936- * be ready to have the row's column values appended.
1937- */
1938- if (insertStmt == NULL)
1939- {
1940- TableInfo *targettab;
1978+ TableInfo *targettab;
19411979
1942- insertStmt = createPQExpBuffer();
1980+ insertStmt = createPQExpBuffer();
19431981
1944- /*
1945- * When load-via-partition-root is set, get the root table
1946- *name for the partition table, so that we can reload data
1947- * through the root table.
1948- */
1949- if (dopt->load_via_partition_root && tbinfo->ispartition)
1950- targettab = getRootTableInfo(tbinfo);
1951- else
1952- targettab = tbinfo;
1982+ /*
1983+ * When load-via-partition-root is set, get the root table name
1984+ * for the partition table, so that we can reload data through the
1985+ * root table.
1986+ */
1987+ if (dopt->load_via_partition_root && tbinfo->ispartition)
1988+ targettab = getRootTableInfo(tbinfo);
1989+ else
1990+ targettab = tbinfo;
19531991
1954- appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
1955- fmtQualifiedDumpable(targettab));
1992+ appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
1993+ fmtQualifiedDumpable(targettab));
19561994
1957- /* corner case for zero-column table */
1958- if (nfields == 0)
1959- {
1960- appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
1961- }
1962- else
1995+ /* corner case for zero-column table */
1996+ if (nfields == 0)
1997+ {
1998+ appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
1999+ }
2000+ else
2001+ {
2002+ /* append the list of column names if required */
2003+ if (dopt->column_inserts)
19632004{
1964- /* append the list of column names if required */
1965- if (dopt->column_inserts )
2005+ appendPQExpBufferChar(insertStmt, '(');
2006+ for (int field = 0; field < nfields; field++ )
19662007{
1967- appendPQExpBufferChar(insertStmt, '(');
1968- for (field = 0; field < nfields; field++)
1969- {
1970- if (field > 0)
1971- appendPQExpBufferStr(insertStmt, ", ");
1972- appendPQExpBufferStr(insertStmt,
1973- fmtId(PQfname(res, field)));
1974- }
1975- appendPQExpBufferStr(insertStmt, ") ");
2008+ if (field > 0)
2009+ appendPQExpBufferStr(insertStmt, ", ");
2010+ appendPQExpBufferStr(insertStmt,
2011+ fmtId(PQfname(res, field)));
19762012}
2013+ appendPQExpBufferStr(insertStmt, ") ");
2014+ }
19772015
1978- if (tbinfo->needs_override)
1979- appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2016+ if (tbinfo->needs_override)
2017+ appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
19802018
1981- appendPQExpBufferStr(insertStmt, "VALUES (");
1982- }
2019+ appendPQExpBufferStr(insertStmt, "VALUES");
19832020}
2021+ }
19842022
1985- archputs(insertStmt->data, fout);
2023+ for (int tuple = 0; tuple < PQntuples(res); tuple++)
2024+ {
2025+ /* Write the INSERT if not in the middle of a multi-row INSERT. */
2026+ if (rows_this_statement == 0)
2027+ archputs(insertStmt->data, fout);
19862028
1987- /* if it is zero-column table then we're done */
2029+ /*
2030+ * If it is zero-column table then we've aleady written the
2031+ * complete statement, which will mean we've disobeyed
2032+ * --rows-per-insert when it's set greater than 1. We do support
2033+ * a way to make this multi-row with: SELECT UNION ALL SELECT
2034+ * UNION ALL ... but that's non-standard so we should avoid it
2035+ * given that using INSERTs is mostly only ever needed for
2036+ * cross-database exports.
2037+ */
19882038if (nfields == 0)
19892039continue;
19902040
1991- for (field = 0; field < nfields; field++)
2041+ /* Emit a row heading */
2042+ if (rows_per_statement == 1)
2043+ archputs(" (", fout);
2044+ else if (rows_this_statement > 0)
2045+ archputs(",\n\t(", fout);
2046+ else
2047+ archputs("\n\t(", fout);
2048+
2049+ for (int field = 0; field < nfields; field++)
19922050{
19932051if (field > 0)
19942052archputs(", ", fout);
@@ -2053,10 +2111,19 @@ dumpTableData_insert(Archive *fout, void *dcontext)
20532111}
20542112}
20552113
2056- if (!dopt->do_nothing)
2057- archputs(");\n", fout);
2058- else
2059- archputs(") ON CONFLICT DO NOTHING;\n", fout);
2114+ /* Terminate the row ... */
2115+ archputs(")", fout);
2116+
2117+ /* ... and the statement, if the target no. of rows is reached */
2118+ if (++rows_this_statement >= rows_per_statement)
2119+ {
2120+ if (dopt->do_nothing)
2121+ archputs(" ON CONFLICT DO NOTHING;\n", fout);
2122+ else
2123+ archputs(";\n", fout);
2124+ /* Reset the row counter */
2125+ rows_this_statement = 0;
2126+ }
20602127}
20612128
20622129if (PQntuples(res) <= 0)
@@ -2067,6 +2134,15 @@ dumpTableData_insert(Archive *fout, void *dcontext)
20672134PQclear(res);
20682135}
20692136
2137+ /* Terminate any statements that didn't make the row count. */
2138+ if (rows_this_statement > 0)
2139+ {
2140+ if (dopt->do_nothing)
2141+ archputs(" ON CONFLICT DO NOTHING;\n", fout);
2142+ else
2143+ archputs(";\n", fout);
2144+ }
2145+
20702146archputs("\n\n", fout);
20712147
20722148ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");