1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

pg_dump: Include column statistics for index-backed constraints

dumpIndex() emits ALTER INDEX .. SET STATISTICS for per-column
statistics targets, but dumpConstraint() never did.  Targets set on the
expression columns of an exclusion constraint's index were silently
lost on dump/restore and pg_upgrade.

The same code can be used in the index and constraint dump code to
generate these ALTER INDEX commands.  The logic has been refactored so
that both share a new subroutine in charge of generating all these
custom SET STATISTICS, if any.

Oversight in e4fca461ab.

Author: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Andrew Krylosov <krylosov.andrew@gmail.com>
Discussion: https://postgr.es/m/CAN4CZFMd9riOuV5LkM+uV=uF-HYp49C8Fh22xWyMxptGLBCZ3A@mail.gmail.com
Backpatch-through: 14
This commit is contained in:
Michael Paquier
2026-10-02 17:02:48 +09:00
parent fc56f1663e
commit 3693e2bc00
2 changed files with 72 additions and 41 deletions

View File

@@ -16325,6 +16325,48 @@ getAttrName(int attrnum, const TableInfo *tblInfo)
return NULL; /* keep compiler quiet */
}
/*
* appendIndexStatTargets
* append ALTER INDEX ... SET STATISTICS commands for the per-column
* statistics targets of an index (attstattarget), if any.
*/
static void
appendIndexStatTargets(PQExpBuffer q, const IndxInfo *indxinfo)
{
char **indstatcolsarray = NULL;
char **indstatvalsarray = NULL;
int nstatcols = 0;
int nstatvals = 0;
if (strlen(indxinfo->indstatcols) == 0 &&
strlen(indxinfo->indstatvals) == 0)
return;
if (!parsePGArray(indxinfo->indstatcols, &indstatcolsarray, &nstatcols))
pg_fatal("could not parse index statistic columns");
if (!parsePGArray(indxinfo->indstatvals, &indstatvalsarray, &nstatvals))
pg_fatal("could not parse index statistic values");
if (nstatcols != nstatvals)
pg_fatal("mismatched number of columns and values for index statistics");
for (int j = 0; j < nstatcols; j++)
{
appendPQExpBuffer(q, "ALTER INDEX %s ",
fmtQualifiedDumpable(indxinfo));
/*
* Note that this is a column number, so no quotes should be used.
*/
appendPQExpBuffer(q, "ALTER COLUMN %s ",
indstatcolsarray[j]);
appendPQExpBuffer(q, "SET STATISTICS %s;\n",
indstatvalsarray[j]);
}
free(indstatcolsarray);
free(indstatvalsarray);
}
/*
* dumpIndex
* write out to fout a user-defined index
@@ -16359,13 +16401,6 @@ dumpIndex(Archive *fout, const IndxInfo *indxinfo)
*/
if (!is_constraint)
{
char *indstatcols = indxinfo->indstatcols;
char *indstatvals = indxinfo->indstatvals;
char **indstatcolsarray = NULL;
char **indstatvalsarray = NULL;
int nstatcols = 0;
int nstatvals = 0;
if (dopt->binary_upgrade)
binary_upgrade_set_pg_class_oids(fout, q,
indxinfo->dobj.catId.oid, true);
@@ -16389,35 +16424,8 @@ dumpIndex(Archive *fout, const IndxInfo *indxinfo)
qindxname);
}
/*
* If the index has any statistics on some of its columns, generate
* the associated ALTER INDEX queries.
*/
if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
{
int j;
if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
pg_fatal("could not parse index statistic columns");
if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
pg_fatal("could not parse index statistic values");
if (nstatcols != nstatvals)
pg_fatal("mismatched number of columns and values for index statistics");
for (j = 0; j < nstatcols; j++)
{
appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
/*
* Note that this is a column number, so no quotes should be
* used.
*/
appendPQExpBuffer(q, "ALTER COLUMN %s ",
indstatcolsarray[j]);
appendPQExpBuffer(q, "SET STATISTICS %s;\n",
indstatvalsarray[j]);
}
}
/* Per-column statistics targets, if any */
appendIndexStatTargets(q, indxinfo);
/* Indexes can depend on extensions */
append_depends_on_extension(fout, q, &indxinfo->dobj,
@@ -16456,11 +16464,6 @@ dumpIndex(Archive *fout, const IndxInfo *indxinfo)
.section = SECTION_POST_DATA,
.createStmt = q->data,
.dropStmt = delq->data));
if (indstatcolsarray)
free(indstatcolsarray);
if (indstatvalsarray)
free(indstatvalsarray);
}
/* Dump Index Comments */
@@ -16724,6 +16727,9 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
fmtId(indxinfo->dobj.name));
}
/* Per-column statistics targets, if any */
appendIndexStatTargets(q, indxinfo);
/* If the index defines identity, we need to record that. */
if (indxinfo->indisreplident)
{

View File

@@ -3157,6 +3157,31 @@ my %tests = (
unlike => { exclude_dump_test_schema => 1, },
},
'CREATE TABLE table_with_constraint_stats' => {
create_order => 98,
create_sql => 'CREATE TABLE dump_test.table_constraint_stats (
col1 int,
col2 int);
ALTER TABLE dump_test.table_constraint_stats
ADD CONSTRAINT ex_with_stats
EXCLUDE USING btree ((col1 + 1) WITH =,
col1 WITH =, (col2 + 1) WITH =);
ALTER INDEX dump_test.ex_with_stats
ALTER COLUMN 1 SET STATISTICS 100;
ALTER INDEX dump_test.ex_with_stats
ALTER COLUMN 3 SET STATISTICS 200;',
regexp => qr/^
\QALTER INDEX dump_test.ex_with_stats ALTER COLUMN 1 SET STATISTICS 100;\E\n
\QALTER INDEX dump_test.ex_with_stats ALTER COLUMN 3 SET STATISTICS 200;\E\n
/xms,
like =>
{ %full_runs, %dump_test_schema_runs, section_post_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CREATE TABLE test_inheritance_parent' => {
create_order => 90,
create_sql => 'CREATE TABLE dump_test.test_inheritance_parent (