diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 12206e0cfc6..0e6c5349652 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -30209,8 +30209,8 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
- For example, to set the relpages and
- reltuples of the table
+ For example, to set the relpages and
+ reltuples values for the table
mytable:
SELECT pg_restore_relation_stats(
@@ -30222,8 +30222,8 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
The argument relation with a value of type
regclass is required, and specifies the table. Other
- arguments are the names of statistics corresponding to certain
- columns in pg_class.
The currently-supported relation statistics are
relpages with a value of type
@@ -30232,16 +30232,16 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
value of type integer.
- Additionally, this function supports argument name
+ Additionally, this function accepts argument name
version of type integer, which
- specifies the version from which the statistics originated, improving
- interpretation of statistics from older versions of
- PostgreSQL.
+ specifies the server version from which the statistics originated.
+ This is anticipated to be helpful in porting statistics from older
+ versions of PostgreSQL.
Minor errors are reported as a WARNING and
ignored, and remaining statistics will still be restored. If all
- specified statistics are successfully restored, return
+ specified statistics are successfully restored, returns
true, otherwise false.
@@ -30281,7 +30281,7 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
boolean
- Create or update column-level statistics. Ordinarily, these
+ Creates or updates column-level statistics. Ordinarily, these
statistics are collected automatically or updated as a part of or , so it's not
necessary to call this function. However, it is useful after a
@@ -30300,9 +30300,9 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
- For example, to set the avg_width and
- null_frac for the attribute
- col1 of the table
+ For example, to set the avg_width and
+ null_frac values for the attribute
+ col1 of the table
mytable:
SELECT pg_restore_attribute_stats(
@@ -30315,25 +30315,26 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
The required arguments are relation with a value
- of type regclass, which specifies the table;
- attname with a value of type name,
+ of type regclass, which specifies the table; either
+ attname with a value of type name or
+ attnum with a value of type smallint,
which specifies the column; and inherited, which
- specifies whether the statistics includes values from child tables.
- Other arguments are the names of statistics corresponding to columns
- in pg_stats.
- Additionally, this function supports argument name
+ Additionally, this function accepts argument name
version of type integer, which
- specifies the version from which the statistics originated, improving
- interpretation of statistics from older versions of
- PostgreSQL.
+ specifies the server version from which the statistics originated.
+ This is anticipated to be helpful in porting statistics from older
+ versions of PostgreSQL.
Minor errors are reported as a WARNING and
ignored, and remaining statistics will still be restored. If all
- specified statistics are successfully restored, return
+ specified statistics are successfully restored, returns
true, otherwise false.
diff --git a/src/backend/statistics/attribute_stats.c b/src/backend/statistics/attribute_stats.c
index 66a5676c810..6bcbee0edba 100644
--- a/src/backend/statistics/attribute_stats.c
+++ b/src/backend/statistics/attribute_stats.c
@@ -38,6 +38,7 @@ enum attribute_stats_argnum
{
ATTRELATION_ARG = 0,
ATTNAME_ARG,
+ ATTNUM_ARG,
INHERITED_ARG,
NULL_FRAC_ARG,
AVG_WIDTH_ARG,
@@ -59,6 +60,7 @@ static struct StatsArgInfo attarginfo[] =
{
[ATTRELATION_ARG] = {"relation", REGCLASSOID},
[ATTNAME_ARG] = {"attname", NAMEOID},
+ [ATTNUM_ARG] = {"attnum", INT2OID},
[INHERITED_ARG] = {"inherited", BOOLOID},
[NULL_FRAC_ARG] = {"null_frac", FLOAT4OID},
[AVG_WIDTH_ARG] = {"avg_width", INT4OID},
@@ -76,6 +78,22 @@ static struct StatsArgInfo attarginfo[] =
[NUM_ATTRIBUTE_STATS_ARGS] = {0}
};
+enum clear_attribute_stats_argnum
+{
+ C_ATTRELATION_ARG = 0,
+ C_ATTNAME_ARG,
+ C_INHERITED_ARG,
+ C_NUM_ATTRIBUTE_STATS_ARGS
+};
+
+static struct StatsArgInfo cleararginfo[] =
+{
+ [C_ATTRELATION_ARG] = {"relation", REGCLASSOID},
+ [C_ATTNAME_ARG] = {"attname", NAMEOID},
+ [C_INHERITED_ARG] = {"inherited", BOOLOID},
+ [C_NUM_ATTRIBUTE_STATS_ARGS] = {0}
+};
+
static bool attribute_statistics_update(FunctionCallInfo fcinfo);
static Node *get_attr_expr(Relation rel, int attnum);
static void get_attr_stat_type(Oid reloid, AttrNumber attnum,
@@ -116,9 +134,9 @@ static bool
attribute_statistics_update(FunctionCallInfo fcinfo)
{
Oid reloid;
- Name attname;
- bool inherited;
+ char *attname;
AttrNumber attnum;
+ bool inherited;
Relation starel;
HeapTuple statup;
@@ -164,21 +182,51 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
/* lock before looking up attribute */
stats_lock_check_privileges(reloid);
- stats_check_required_arg(fcinfo, attarginfo, ATTNAME_ARG);
- attname = PG_GETARG_NAME(ATTNAME_ARG);
- attnum = get_attnum(reloid, NameStr(*attname));
+ /* user can specify either attname or attnum, but not both */
+ if (!PG_ARGISNULL(ATTNAME_ARG))
+ {
+ Name attnamename;
+
+ if (!PG_ARGISNULL(ATTNUM_ARG))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot specify both attname and attnum")));
+ attnamename = PG_GETARG_NAME(ATTNAME_ARG);
+ attname = NameStr(*attnamename);
+ attnum = get_attnum(reloid, attname);
+ /* note that this test covers attisdropped cases too: */
+ if (attnum == InvalidAttrNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ attname, get_rel_name(reloid))));
+ }
+ else if (!PG_ARGISNULL(ATTNUM_ARG))
+ {
+ attnum = PG_GETARG_INT16(ATTNUM_ARG);
+ attname = get_attname(reloid, attnum, true);
+ /* annoyingly, get_attname doesn't check attisdropped */
+ if (attname == NULL ||
+ !SearchSysCacheExistsAttName(reloid, attname))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column %d of relation \"%s\" does not exist",
+ attnum, get_rel_name(reloid))));
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("must specify either attname or attnum")));
+ attname = NULL; /* keep compiler quiet */
+ attnum = 0;
+ }
if (attnum < 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot modify statistics on system column \"%s\"",
- NameStr(*attname))));
-
- if (attnum == InvalidAttrNumber)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("column \"%s\" of relation \"%s\" does not exist",
- NameStr(*attname), get_rel_name(reloid))));
+ attname)));
stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG);
inherited = PG_GETARG_BOOL(INHERITED_ARG);
@@ -241,7 +289,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
&elemtypid, &elem_eq_opr))
{
ereport(WARNING,
- (errmsg("unable to determine element type of attribute \"%s\"", NameStr(*attname)),
+ (errmsg("unable to determine element type of attribute \"%s\"", attname),
errdetail("Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST.")));
elemtypid = InvalidOid;
elem_eq_opr = InvalidOid;
@@ -257,7 +305,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
{
ereport(WARNING,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("could not determine less-than operator for attribute \"%s\"", NameStr(*attname)),
+ errmsg("could not determine less-than operator for attribute \"%s\"", attname),
errdetail("Cannot set STATISTIC_KIND_HISTOGRAM or STATISTIC_KIND_CORRELATION.")));
do_histogram = false;
@@ -271,7 +319,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
{
ereport(WARNING,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("attribute \"%s\" is not a range type", NameStr(*attname)),
+ errmsg("attribute \"%s\" is not a range type", attname),
errdetail("Cannot set STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM or STATISTIC_KIND_BOUNDS_HISTOGRAM.")));
do_bounds_histogram = false;
@@ -857,8 +905,8 @@ pg_clear_attribute_stats(PG_FUNCTION_ARGS)
AttrNumber attnum;
bool inherited;
- stats_check_required_arg(fcinfo, attarginfo, ATTRELATION_ARG);
- reloid = PG_GETARG_OID(ATTRELATION_ARG);
+ stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELATION_ARG);
+ reloid = PG_GETARG_OID(C_ATTRELATION_ARG);
if (RecoveryInProgress())
ereport(ERROR,
@@ -868,8 +916,8 @@ pg_clear_attribute_stats(PG_FUNCTION_ARGS)
stats_lock_check_privileges(reloid);
- stats_check_required_arg(fcinfo, attarginfo, ATTNAME_ARG);
- attname = PG_GETARG_NAME(ATTNAME_ARG);
+ stats_check_required_arg(fcinfo, cleararginfo, C_ATTNAME_ARG);
+ attname = PG_GETARG_NAME(C_ATTNAME_ARG);
attnum = get_attnum(reloid, NameStr(*attname));
if (attnum < 0)
@@ -884,13 +932,39 @@ pg_clear_attribute_stats(PG_FUNCTION_ARGS)
errmsg("column \"%s\" of relation \"%s\" does not exist",
NameStr(*attname), get_rel_name(reloid))));
- stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG);
- inherited = PG_GETARG_BOOL(INHERITED_ARG);
+ stats_check_required_arg(fcinfo, cleararginfo, C_INHERITED_ARG);
+ inherited = PG_GETARG_BOOL(C_INHERITED_ARG);
delete_pg_statistic(reloid, attnum, inherited);
PG_RETURN_VOID();
}
+/*
+ * Import statistics for a given relation attribute.
+ *
+ * Inserts or replaces a row in pg_statistic for the given relation and
+ * attribute name or number. It takes input parameters that correspond to
+ * columns in the view pg_stats.
+ *
+ * Parameters are given in a pseudo named-attribute style: they must be
+ * pairs of parameter names (as text) and values (of appropriate types).
+ * We do that, rather than using regular named-parameter notation, so
+ * that we can add or change parameters without fear of breaking
+ * carelessly-written calls.
+ *
+ * Parameters null_frac, avg_width, and n_distinct all correspond to NOT NULL
+ * columns in pg_statistic. The remaining parameters all belong to a specific
+ * stakind. Some stakinds require multiple parameters, which must be specified
+ * together (or neither specified).
+ *
+ * Parameters are only superficially validated. Omitting a parameter or
+ * passing NULL leaves the statistic unchanged.
+ *
+ * Parameters corresponding to ANYARRAY columns are instead passed in as text
+ * values, which is a valid input string for an array of the type or element
+ * type of the attribute. Any error generated by the array_in() function will
+ * in turn fail the function.
+ */
Datum
pg_restore_attribute_stats(PG_FUNCTION_ARGS)
{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 0de6c959bb0..7c38c89bf08 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6819,7 +6819,8 @@ getFuncs(Archive *fout)
*/
static RelStatsInfo *
getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
- float reltuples, int32 relallvisible, char relkind)
+ float reltuples, int32 relallvisible, char relkind,
+ char **indAttNames, int nindAttNames)
{
if (!fout->dopt->dumpStatistics)
return NULL;
@@ -6848,6 +6849,8 @@ getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
info->reltuples = reltuples;
info->relallvisible = relallvisible;
info->relkind = relkind;
+ info->indAttNames = indAttNames;
+ info->nindAttNames = nindAttNames;
info->postponed_def = false;
return info;
@@ -7249,7 +7252,8 @@ getTables(Archive *fout, int *numTables)
/* Add statistics */
if (tblinfo[i].interesting)
getRelationStatistics(fout, &tblinfo[i].dobj, tblinfo[i].relpages,
- reltuples, relallvisible, tblinfo[i].relkind);
+ reltuples, relallvisible, tblinfo[i].relkind,
+ NULL, 0);
/*
* Read-lock target tables to make sure they aren't DROPPED or altered
@@ -7534,6 +7538,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
i_contableoid,
i_conoid,
i_condef,
+ i_indattnames,
i_tablespace,
i_indreloptions,
i_indstatcols,
@@ -7579,6 +7584,11 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
"c.tableoid AS contableoid, "
"c.oid AS conoid, "
"pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
+ "CASE WHEN i.indexprs IS NOT NULL THEN "
+ "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
+ " FROM pg_catalog.pg_attribute "
+ " WHERE attrelid = i.indexrelid) "
+ "ELSE NULL END AS indattnames, "
"(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
"t.reloptions AS indreloptions, ");
@@ -7698,6 +7708,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
i_contableoid = PQfnumber(res, "contableoid");
i_conoid = PQfnumber(res, "conoid");
i_condef = PQfnumber(res, "condef");
+ i_indattnames = PQfnumber(res, "indattnames");
i_tablespace = PQfnumber(res, "tablespace");
i_indreloptions = PQfnumber(res, "indreloptions");
i_indstatcols = PQfnumber(res, "indstatcols");
@@ -7714,6 +7725,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
{
Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
TableInfo *tbinfo = NULL;
+ char **indAttNames = NULL;
+ int nindAttNames = 0;
int numinds;
/* Count rows for this table */
@@ -7784,10 +7797,18 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
else
indexkind = RELKIND_PARTITIONED_INDEX;
- contype = *(PQgetvalue(res, j, i_contype));
- relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
- reltuples, relallvisible, indexkind);
+ if (!PQgetisnull(res, j, i_indattnames))
+ {
+ if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
+ &indAttNames, &nindAttNames))
+ pg_fatal("could not parse %s array", "indattnames");
+ }
+ relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
+ reltuples, relallvisible, indexkind,
+ indAttNames, nindAttNames);
+
+ contype = *(PQgetvalue(res, j, i_contype));
if (contype == 'p' || contype == 'u' || contype == 'x')
{
/*
@@ -10410,28 +10431,6 @@ dumpComment(Archive *fout, const char *type,
catalogId, subid, dumpId, NULL);
}
-/*
- * Tabular description of the parameters to pg_restore_attribute_stats()
- * param_name, param_type
- */
-static const char *att_stats_arginfo[][2] = {
- {"attname", "name"},
- {"inherited", "boolean"},
- {"null_frac", "float4"},
- {"avg_width", "integer"},
- {"n_distinct", "float4"},
- {"most_common_vals", "text"},
- {"most_common_freqs", "float4[]"},
- {"histogram_bounds", "text"},
- {"correlation", "float4"},
- {"most_common_elems", "text"},
- {"most_common_elem_freqs", "float4[]"},
- {"elem_count_histogram", "float4[]"},
- {"range_length_histogram", "text"},
- {"range_empty_frac", "float4"},
- {"range_bounds_histogram", "text"},
-};
-
/*
* appendNamedArgument --
*
@@ -10440,9 +10439,9 @@ static const char *att_stats_arginfo[][2] = {
*/
static void
appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
- const char *argval, const char *argtype)
+ const char *argtype, const char *argval)
{
- appendPQExpBufferStr(out, "\t");
+ appendPQExpBufferStr(out, ",\n\t");
appendStringLiteralAH(out, argname, fout);
appendPQExpBufferStr(out, ", ");
@@ -10451,68 +10450,6 @@ appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
appendPQExpBuffer(out, "::%s", argtype);
}
-/*
- * appendRelStatsImport --
- *
- * Append a formatted pg_restore_relation_stats statement.
- */
-static void
-appendRelStatsImport(PQExpBuffer out, Archive *fout, const RelStatsInfo *rsinfo,
- const char *qualified_name)
-{
- char reltuples_str[FLOAT_SHORTEST_DECIMAL_LEN];
-
- float_to_shortest_decimal_buf(rsinfo->reltuples, reltuples_str);
-
- appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
- appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
- fout->remoteVersion);
- appendPQExpBuffer(out, "\t'relation', '%s'::regclass,\n", qualified_name);
- appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
- appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", reltuples_str);
- appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer\n);\n",
- rsinfo->relallvisible);
-}
-
-/*
- * appendAttStatsImport --
- *
- * Append a series of formatted pg_restore_attribute_stats statements.
- */
-static void
-appendAttStatsImport(PQExpBuffer out, Archive *fout, PGresult *res,
- const char *qualified_name)
-{
- for (int rownum = 0; rownum < PQntuples(res); rownum++)
- {
- const char *sep = "";
-
- appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
- appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
- fout->remoteVersion);
- appendPQExpBuffer(out, "\t'relation', '%s'::regclass,\n",
- qualified_name);
- for (int argno = 0; argno < lengthof(att_stats_arginfo); argno++)
- {
- const char *argname = att_stats_arginfo[argno][0];
- const char *argtype = att_stats_arginfo[argno][1];
- int fieldno = PQfnumber(res, argname);
-
- if (fieldno < 0)
- pg_fatal("attribute stats export query missing field '%s'",
- argname);
-
- if (PQgetisnull(res, rownum, fieldno))
- continue;
-
- appendPQExpBufferStr(out, sep);
- appendNamedArgument(out, fout, argname, PQgetvalue(res, rownum, fieldno), argtype);
- sep = ",\n";
- }
- appendPQExpBufferStr(out, "\n);\n");
- }
-}
-
/*
* Decide which section to use based on the relkind of the parent object.
*
@@ -10549,14 +10486,30 @@ statisticsDumpSection(const RelStatsInfo *rsinfo)
static void
dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
{
+ const DumpableObject *dobj = &rsinfo->dobj;
PGresult *res;
PQExpBuffer query;
PQExpBuffer out;
PQExpBuffer tag;
- DumpableObject *dobj = (DumpableObject *) &rsinfo->dobj;
DumpId *deps = NULL;
int ndeps = 0;
- const char *qualified_name;
+ char *qualified_name;
+ char reltuples_str[FLOAT_SHORTEST_DECIMAL_LEN];
+ int i_attname;
+ int i_inherited;
+ int i_null_frac;
+ int i_avg_width;
+ int i_n_distinct;
+ int i_most_common_vals;
+ int i_most_common_freqs;
+ int i_histogram_bounds;
+ int i_correlation;
+ int i_most_common_elems;
+ int i_most_common_elem_freqs;
+ int i_elem_count_histogram;
+ int i_range_length_histogram;
+ int i_range_empty_frac;
+ int i_range_bounds_histogram;
/* nothing to do if we are not dumping statistics */
if (!fout->dopt->dumpStatistics)
@@ -10586,7 +10539,8 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
if (fout->remoteVersion >= 170000)
appendPQExpBufferStr(query,
- "s.range_length_histogram, s.range_empty_frac, "
+ "s.range_length_histogram, "
+ "s.range_empty_frac, "
"s.range_bounds_histogram ");
else
appendPQExpBufferStr(query,
@@ -10595,7 +10549,7 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
"NULL AS range_bounds_histogram ");
appendPQExpBufferStr(query,
- "FROM pg_stats s "
+ "FROM pg_catalog.pg_stats s "
"WHERE s.schemaname = $1 "
"AND s.tablename = $2 "
"ORDER BY s.attname, s.inherited");
@@ -10606,21 +10560,137 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
resetPQExpBuffer(query);
}
+ out = createPQExpBuffer();
+
+ qualified_name = pg_strdup(fmtQualifiedDumpable(rsinfo));
+
+ /* restore relation stats */
+ appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
+ appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+ fout->remoteVersion);
+ appendPQExpBufferStr(out, "\t'relation', ");
+ appendStringLiteralAH(out, qualified_name, fout);
+ appendPQExpBufferStr(out, "::regclass,\n");
+ appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
+ float_to_shortest_decimal_buf(rsinfo->reltuples, reltuples_str);
+ appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", reltuples_str);
+ appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer\n);\n",
+ rsinfo->relallvisible);
+
+ /* fetch attribute stats */
appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
appendStringLiteralAH(query, dobj->namespace->dobj.name, fout);
appendPQExpBufferStr(query, ", ");
appendStringLiteralAH(query, dobj->name, fout);
- appendPQExpBufferStr(query, "); ");
+ appendPQExpBufferStr(query, ");");
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
- out = createPQExpBuffer();
+ i_attname = PQfnumber(res, "attname");
+ i_inherited = PQfnumber(res, "inherited");
+ i_null_frac = PQfnumber(res, "null_frac");
+ i_avg_width = PQfnumber(res, "avg_width");
+ i_n_distinct = PQfnumber(res, "n_distinct");
+ i_most_common_vals = PQfnumber(res, "most_common_vals");
+ i_most_common_freqs = PQfnumber(res, "most_common_freqs");
+ i_histogram_bounds = PQfnumber(res, "histogram_bounds");
+ i_correlation = PQfnumber(res, "correlation");
+ i_most_common_elems = PQfnumber(res, "most_common_elems");
+ i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
+ i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
+ i_range_length_histogram = PQfnumber(res, "range_length_histogram");
+ i_range_empty_frac = PQfnumber(res, "range_empty_frac");
+ i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
- qualified_name = fmtQualifiedId(rsinfo->dobj.namespace->dobj.name,
- rsinfo->dobj.name);
+ /* restore attribute stats */
+ for (int rownum = 0; rownum < PQntuples(res); rownum++)
+ {
+ const char *attname;
- appendRelStatsImport(out, fout, rsinfo, qualified_name);
- appendAttStatsImport(out, fout, res, qualified_name);
+ appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
+ appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
+ fout->remoteVersion);
+ appendPQExpBufferStr(out, "\t'relation', ");
+ appendStringLiteralAH(out, qualified_name, fout);
+ appendPQExpBufferStr(out, "::regclass");
+
+ if (PQgetisnull(res, rownum, i_attname))
+ pg_fatal("attname cannot be NULL");
+ attname = PQgetvalue(res, rownum, i_attname);
+
+ /*
+ * Indexes look up attname in indAttNames to derive attnum, all others
+ * use attname directly. We must specify attnum for indexes, since
+ * their attnames are not necessarily stable across dump/reload.
+ */
+ if (rsinfo->nindAttNames == 0)
+ appendNamedArgument(out, fout, "attname", "name", attname);
+ else
+ {
+ bool found = false;
+
+ for (int i = 0; i < rsinfo->nindAttNames; i++)
+ {
+ if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
+ {
+ appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
+ i + 1);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ pg_fatal("could not find index attname \"%s\"", attname);
+ }
+
+ if (!PQgetisnull(res, rownum, i_inherited))
+ appendNamedArgument(out, fout, "inherited", "boolean",
+ PQgetvalue(res, rownum, i_inherited));
+ if (!PQgetisnull(res, rownum, i_null_frac))
+ appendNamedArgument(out, fout, "null_frac", "real",
+ PQgetvalue(res, rownum, i_null_frac));
+ if (!PQgetisnull(res, rownum, i_avg_width))
+ appendNamedArgument(out, fout, "avg_width", "integer",
+ PQgetvalue(res, rownum, i_avg_width));
+ if (!PQgetisnull(res, rownum, i_n_distinct))
+ appendNamedArgument(out, fout, "n_distinct", "real",
+ PQgetvalue(res, rownum, i_n_distinct));
+ if (!PQgetisnull(res, rownum, i_most_common_vals))
+ appendNamedArgument(out, fout, "most_common_vals", "text",
+ PQgetvalue(res, rownum, i_most_common_vals));
+ if (!PQgetisnull(res, rownum, i_most_common_freqs))
+ appendNamedArgument(out, fout, "most_common_freqs", "real[]",
+ PQgetvalue(res, rownum, i_most_common_freqs));
+ if (!PQgetisnull(res, rownum, i_histogram_bounds))
+ appendNamedArgument(out, fout, "histogram_bounds", "text",
+ PQgetvalue(res, rownum, i_histogram_bounds));
+ if (!PQgetisnull(res, rownum, i_correlation))
+ appendNamedArgument(out, fout, "correlation", "real",
+ PQgetvalue(res, rownum, i_correlation));
+ if (!PQgetisnull(res, rownum, i_most_common_elems))
+ appendNamedArgument(out, fout, "most_common_elems", "text",
+ PQgetvalue(res, rownum, i_most_common_elems));
+ if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
+ appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
+ PQgetvalue(res, rownum, i_most_common_elem_freqs));
+ if (!PQgetisnull(res, rownum, i_elem_count_histogram))
+ appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
+ PQgetvalue(res, rownum, i_elem_count_histogram));
+ if (fout->remoteVersion >= 170000)
+ {
+ if (!PQgetisnull(res, rownum, i_range_length_histogram))
+ appendNamedArgument(out, fout, "range_length_histogram", "text",
+ PQgetvalue(res, rownum, i_range_length_histogram));
+ if (!PQgetisnull(res, rownum, i_range_empty_frac))
+ appendNamedArgument(out, fout, "range_empty_frac", "real",
+ PQgetvalue(res, rownum, i_range_empty_frac));
+ if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
+ appendNamedArgument(out, fout, "range_bounds_histogram", "text",
+ PQgetvalue(res, rownum, i_range_bounds_histogram));
+ }
+ appendPQExpBufferStr(out, "\n);\n");
+ }
PQclear(res);
@@ -10634,8 +10704,9 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
.deps = deps,
.nDeps = ndeps));
- destroyPQExpBuffer(query);
+ free(qualified_name);
destroyPQExpBuffer(out);
+ destroyPQExpBuffer(query);
destroyPQExpBuffer(tag);
}
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 9d6a4857c4b..ca32f167878 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -442,6 +442,13 @@ typedef struct _relStatsInfo
float reltuples;
int32 relallvisible;
char relkind; /* 'r', 'm', 'i', etc */
+
+ /*
+ * indAttNames/nindAttNames are populated only if the relation is an index
+ * with at least one expression column; we don't need them otherwise.
+ */
+ char **indAttNames; /* attnames of the index, in order */
+ int32 nindAttNames; /* number of attnames stored (can be 0) */
bool postponed_def; /* stats must be postponed into post-data */
} RelStatsInfo;
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 3945e4f0e2a..c7bffc1b045 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -4720,24 +4720,41 @@ my %tests = (
CREATE TABLE dump_test.has_stats
AS SELECT g.g AS x, g.g / 2 AS y FROM generate_series(1,100) AS g(g);
CREATE MATERIALIZED VIEW dump_test.has_stats_mv AS SELECT * FROM dump_test.has_stats;
- CREATE INDEX dup_test_post_data_ix ON dump_test.has_stats((x - 1));
+ CREATE INDEX dup_test_post_data_ix ON dump_test.has_stats(x, (x - 1));
ANALYZE dump_test.has_stats, dump_test.has_stats_mv;',
- regexp => qr/pg_catalog.pg_restore_attribute_stats/,
+ regexp => qr/^
+ \QSELECT * FROM pg_catalog.pg_restore_relation_stats(\E\s+
+ 'version',\s'\d+'::integer,\s+
+ 'relation',\s'dump_test.dup_test_post_data_ix'::regclass,\s+
+ 'relpages',\s'\d+'::integer,\s+
+ 'reltuples',\s'\d+'::real,\s+
+ 'relallvisible',\s'\d+'::integer\s+
+ \);\s+
+ \QSELECT * FROM pg_catalog.pg_restore_attribute_stats(\E\s+
+ 'version',\s'\d+'::integer,\s+
+ 'relation',\s'dump_test.dup_test_post_data_ix'::regclass,\s+
+ 'attnum',\s'2'::smallint,\s+
+ 'inherited',\s'f'::boolean,\s+
+ 'null_frac',\s'0'::real,\s+
+ 'avg_width',\s'4'::integer,\s+
+ 'n_distinct',\s'-1'::real,\s+
+ 'histogram_bounds',\s'\{[0-9,]+\}'::text,\s+
+ 'correlation',\s'1'::real\s+
+ \);/xm,
like => {
%full_runs,
%dump_test_schema_runs,
no_data_no_schema => 1,
no_schema => 1,
- section_data => 1,
section_post_data => 1,
statistics_only => 1,
- },
+ },
unlike => {
exclude_dump_test_schema => 1,
no_statistics => 1,
only_dump_measurement => 1,
schema_only => 1,
- },
+ },
},
#
@@ -4759,11 +4776,11 @@ my %tests = (
section_data => 1,
section_post_data => 1,
statistics_only => 1,
- },
+ },
unlike => {
no_statistics => 1,
schema_only => 1,
- },
+ },
},
# CREATE TABLE with partitioned table and various AMs. One
diff --git a/src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm b/src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm
index 0a707c69c5c..ec874852d12 100644
--- a/src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm
+++ b/src/test/perl/PostgreSQL/Test/AdjustUpgrade.pm
@@ -345,7 +345,7 @@ sub adjust_old_dumpfile
{
$dump =~ s/
(^SELECT\s\*\sFROM\spg_catalog\.pg_restore_relation_stats\(
- \s+'relation',\s'public\.hash_[a-z0-9]*_heap'::regclass,
+ [^;]*'relation',\s'public\.hash_[a-z0-9]*_heap'::regclass,
[^;]*'relallvisible',)\s'\d+'::integer
/$1 ''::integer/mgx;
}
@@ -692,7 +692,7 @@ sub adjust_new_dumpfile
{
$dump =~ s/
(^SELECT\s\*\sFROM\spg_catalog\.pg_restore_relation_stats\(
- \s+'relation',\s'public\.hash_[a-z0-9]*_heap'::regclass,
+ [^;]*'relation',\s'public\.hash_[a-z0-9]*_heap'::regclass,
[^;]*'relallvisible',)\s'\d+'::integer
/$1 ''::integer/mgx;
}
diff --git a/src/test/regress/expected/stats_import.out b/src/test/regress/expected/stats_import.out
index 7e8b7f429c9..1f150f7b08d 100644
--- a/src/test/regress/expected/stats_import.out
+++ b/src/test/regress/expected/stats_import.out
@@ -278,6 +278,31 @@ SELECT pg_catalog.pg_restore_attribute_stats(
'attname', 'id'::name,
'inherited', false::boolean,
'version', 150000::integer,
+ 'null_frac', 0.2::real,
+ 'avg_width', 5::integer,
+ 'n_distinct', 0.6::real);
+ pg_restore_attribute_stats
+----------------------------
+ t
+(1 row)
+
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'id';
+ schemaname | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram | range_length_histogram | range_empty_frac | range_bounds_histogram
+--------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------+------------------------+------------------+------------------------
+ stats_import | test | id | f | 0.2 | 5 | 0.6 | | | | | | | | | |
+(1 row)
+
+-- ok: restore by attnum
+SELECT pg_catalog.pg_restore_attribute_stats(
+ 'relation', 'stats_import.test'::regclass,
+ 'attnum', 1::smallint,
+ 'inherited', false::boolean,
+ 'version', 150000::integer,
'null_frac', 0.4::real,
'avg_width', 5::integer,
'n_distinct', 0.6::real);
@@ -1241,7 +1266,7 @@ SELECT pg_catalog.pg_restore_attribute_stats(
'avg_width', 2::integer,
'n_distinct', 0.3::real);
ERROR: "relation" cannot be NULL
--- error: attname null
+-- error: missing attname
SELECT pg_catalog.pg_restore_attribute_stats(
'relation', 'stats_import.test'::regclass,
'attname', NULL::name,
@@ -1250,7 +1275,18 @@ SELECT pg_catalog.pg_restore_attribute_stats(
'null_frac', 0.1::real,
'avg_width', 2::integer,
'n_distinct', 0.3::real);
-ERROR: "attname" cannot be NULL
+ERROR: must specify either attname or attnum
+-- error: both attname and attnum
+SELECT pg_catalog.pg_restore_attribute_stats(
+ 'relation', 'stats_import.test'::regclass,
+ 'attname', 'id'::name,
+ 'attnum', 1::smallint,
+ 'inherited', false::boolean,
+ 'version', 150000::integer,
+ 'null_frac', 0.1::real,
+ 'avg_width', 2::integer,
+ 'n_distinct', 0.3::real);
+ERROR: cannot specify both attname and attnum
-- error: attname doesn't exist
SELECT pg_catalog.pg_restore_attribute_stats(
'relation', 'stats_import.test'::regclass,
diff --git a/src/test/regress/sql/stats_import.sql b/src/test/regress/sql/stats_import.sql
index 57422750b90..8c183bceb8a 100644
--- a/src/test/regress/sql/stats_import.sql
+++ b/src/test/regress/sql/stats_import.sql
@@ -184,6 +184,23 @@ SELECT pg_catalog.pg_restore_attribute_stats(
'attname', 'id'::name,
'inherited', false::boolean,
'version', 150000::integer,
+ 'null_frac', 0.2::real,
+ 'avg_width', 5::integer,
+ 'n_distinct', 0.6::real);
+
+SELECT *
+FROM pg_stats
+WHERE schemaname = 'stats_import'
+AND tablename = 'test'
+AND inherited = false
+AND attname = 'id';
+
+-- ok: restore by attnum
+SELECT pg_catalog.pg_restore_attribute_stats(
+ 'relation', 'stats_import.test'::regclass,
+ 'attnum', 1::smallint,
+ 'inherited', false::boolean,
+ 'version', 150000::integer,
'null_frac', 0.4::real,
'avg_width', 5::integer,
'n_distinct', 0.6::real);
@@ -902,7 +919,7 @@ SELECT pg_catalog.pg_restore_attribute_stats(
'avg_width', 2::integer,
'n_distinct', 0.3::real);
--- error: attname null
+-- error: missing attname
SELECT pg_catalog.pg_restore_attribute_stats(
'relation', 'stats_import.test'::regclass,
'attname', NULL::name,
@@ -912,6 +929,17 @@ SELECT pg_catalog.pg_restore_attribute_stats(
'avg_width', 2::integer,
'n_distinct', 0.3::real);
+-- error: both attname and attnum
+SELECT pg_catalog.pg_restore_attribute_stats(
+ 'relation', 'stats_import.test'::regclass,
+ 'attname', 'id'::name,
+ 'attnum', 1::smallint,
+ 'inherited', false::boolean,
+ 'version', 150000::integer,
+ 'null_frac', 0.1::real,
+ 'avg_width', 2::integer,
+ 'n_distinct', 0.3::real);
+
-- error: attname doesn't exist
SELECT pg_catalog.pg_restore_attribute_stats(
'relation', 'stats_import.test'::regclass,