@@ -138,6 +138,12 @@ static const CatalogId nilCatalogId = {0, 0};
138
138
static bool have_extra_float_digits = false;
139
139
static int extra_float_digits;
140
140
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
+
141
147
/*
142
148
* Macro for producing quoted, schema-qualified name of a dumpable object.
143
149
*/
@@ -306,11 +312,13 @@ main(int argc, char **argv)
306
312
DumpableObject *boundaryObjs;
307
313
int i;
308
314
int optindex;
315
+ char *endptr;
309
316
RestoreOptions *ropt;
310
317
Archive *fout; /* the script file */
311
318
const char *dumpencoding = NULL;
312
319
const char *dumpsnapshot = NULL;
313
320
char *use_role = NULL;
321
+ long rowsPerInsert;
314
322
int numWorkers = 1;
315
323
trivalue prompt_password = TRI_DEFAULT;
316
324
int compressLevel = -1;
@@ -363,7 +371,7 @@ main(int argc, char **argv)
363
371
{"exclude-table-data", required_argument, NULL, 4},
364
372
{"extra-float-digits", required_argument, NULL, 8},
365
373
{"if-exists", no_argument, &dopt.if_exists, 1},
366
- {"inserts", no_argument, &dopt.dump_inserts, 1 },
374
+ {"inserts", no_argument, NULL, 9 },
367
375
{"lock-wait-timeout", required_argument, NULL, 2},
368
376
{"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
369
377
{"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
@@ -382,6 +390,7 @@ main(int argc, char **argv)
382
390
{"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
383
391
{"no-sync", no_argument, NULL, 7},
384
392
{"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
393
+ {"rows-per-insert", required_argument, NULL, 10},
385
394
386
395
{NULL, 0, NULL, 0}
387
396
};
@@ -572,6 +581,31 @@ main(int argc, char **argv)
572
581
}
573
582
break;
574
583
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
+
575
609
default:
576
610
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
577
611
exit_nicely(1);
@@ -596,8 +630,8 @@ main(int argc, char **argv)
596
630
}
597
631
598
632
/* --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 ;
601
635
602
636
/*
603
637
* Binary upgrade mode implies dumping sequence data even in schema-only
@@ -622,8 +656,12 @@ main(int argc, char **argv)
622
656
if (dopt.if_exists && !dopt.outputClean)
623
657
exit_horribly(NULL, "option --if-exists requires option -c/--clean\n");
624
658
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");
627
665
628
666
/* Identify archive format to emit */
629
667
archiveFormat = parseArchiveFormat(format, &archiveMode);
@@ -993,6 +1031,7 @@ help(const char *progname)
993
1031
printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
994
1032
printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
995
1033
printf(_(" --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"));
996
1035
printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
997
1036
printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
998
1037
printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
@@ -1909,9 +1948,9 @@ dumpTableData_insert(Archive *fout, void *dcontext)
1909
1948
PQExpBuffer q = createPQExpBuffer();
1910
1949
PQExpBuffer insertStmt = NULL;
1911
1950
PGresult *res;
1912
- int tuple;
1913
1951
int nfields;
1914
- int field;
1952
+ int rows_per_statement = dopt->dump_inserts;
1953
+ int rows_this_statement = 0;
1915
1954
1916
1955
appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
1917
1956
"SELECT * FROM ONLY %s",
@@ -1926,69 +1965,88 @@ dumpTableData_insert(Archive *fout, void *dcontext)
1926
1965
res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
1927
1966
PGRES_TUPLES_OK);
1928
1967
nfields = 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)
1930
1977
{
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;
1941
1979
1942
- insertStmt = createPQExpBuffer();
1980
+ insertStmt = createPQExpBuffer();
1943
1981
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;
1953
1991
1954
- appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
1955
- fmtQualifiedDumpable(targettab));
1992
+ appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
1993
+ fmtQualifiedDumpable(targettab));
1956
1994
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)
1963
2004
{
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++ )
1966
2007
{
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)));
1976
2012
}
2013
+ appendPQExpBufferStr(insertStmt, ") ");
2014
+ }
1977
2015
1978
- if (tbinfo->needs_override)
1979
- appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2016
+ if (tbinfo->needs_override)
2017
+ appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
1980
2018
1981
- appendPQExpBufferStr(insertStmt, "VALUES (");
1982
- }
2019
+ appendPQExpBufferStr(insertStmt, "VALUES");
1983
2020
}
2021
+ }
1984
2022
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);
1986
2028
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
+ */
1988
2038
if (nfields == 0)
1989
2039
continue;
1990
2040
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++)
1992
2050
{
1993
2051
if (field > 0)
1994
2052
archputs(", ", fout);
@@ -2053,10 +2111,19 @@ dumpTableData_insert(Archive *fout, void *dcontext)
2053
2111
}
2054
2112
}
2055
2113
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
+ }
2060
2127
}
2061
2128
2062
2129
if (PQntuples(res) <= 0)
@@ -2067,6 +2134,15 @@ dumpTableData_insert(Archive *fout, void *dcontext)
2067
2134
PQclear(res);
2068
2135
}
2069
2136
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
+
2070
2146
archputs("\n\n", fout);
2071
2147
2072
2148
ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
0 commit comments