mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Use attnum to identify index columns in pg_restore_attribute_stats().
Previously we used attname for both table and index columns, but that is problematic for indexes because their attnames are assigned by internal rules that don't guarantee to preserve the names across dump and reload. (This is what's causing the remaining buildfarm failures in cross-version-upgrade tests.) Fortunately we can use attnum instead, since there's no such thing as adding or dropping columns in an existing index. We met this same problem previously with ALTER INDEX ... SET STATISTICS, and solved it the same way, cf commit 5b6d13eec. In pg_restore_attribute_stats() itself, we accept either attnum or attname, but the policy used by pg_dump is to always use attname for tables and attnum for indexes. Author: Tom Lane <tgl@sss.pgh.pa.us> Author: Corey Huinker <corey.huinker@gmail.com> Discussion: https://postgr.es/m/1457469.1740419458@sss.pgh.pa.us
This commit is contained in:
parent
f734c9fc3a
commit
40e27d04b4
@ -30209,8 +30209,8 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
For example, to set the <structname>relpages</structname> and
|
||||
<structname>reltuples</structname> of the table
|
||||
For example, to set the <structfield>relpages</structfield> and
|
||||
<structfield>reltuples</structfield> values for the table
|
||||
<structname>mytable</structname>:
|
||||
<programlisting>
|
||||
SELECT pg_restore_relation_stats(
|
||||
@ -30222,8 +30222,8 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
|
||||
<para>
|
||||
The argument <literal>relation</literal> with a value of type
|
||||
<type>regclass</type> is required, and specifies the table. Other
|
||||
arguments are the names of statistics corresponding to certain
|
||||
columns in <link
|
||||
arguments are the names and values of statistics corresponding to
|
||||
certain columns in <link
|
||||
linkend="catalog-pg-class"><structname>pg_class</structname></link>.
|
||||
The currently-supported relation statistics are
|
||||
<literal>relpages</literal> 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 <type>integer</type>.
|
||||
</para>
|
||||
<para>
|
||||
Additionally, this function supports argument name
|
||||
Additionally, this function accepts argument name
|
||||
<literal>version</literal> of type <type>integer</type>, which
|
||||
specifies the version from which the statistics originated, improving
|
||||
interpretation of statistics from older versions of
|
||||
<productname>PostgreSQL</productname>.
|
||||
specifies the server version from which the statistics originated.
|
||||
This is anticipated to be helpful in porting statistics from older
|
||||
versions of <productname>PostgreSQL</productname>.
|
||||
</para>
|
||||
<para>
|
||||
Minor errors are reported as a <literal>WARNING</literal> and
|
||||
ignored, and remaining statistics will still be restored. If all
|
||||
specified statistics are successfully restored, return
|
||||
specified statistics are successfully restored, returns
|
||||
<literal>true</literal>, otherwise <literal>false</literal>.
|
||||
</para>
|
||||
<para>
|
||||
@ -30281,7 +30281,7 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
|
||||
<returnvalue>boolean</returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
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 <xref
|
||||
linkend="sql-vacuum"/> or <xref linkend="sql-analyze"/>, 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
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
For example, to set the <structname>avg_width</structname> and
|
||||
<structname>null_frac</structname> for the attribute
|
||||
<structname>col1</structname> of the table
|
||||
For example, to set the <structfield>avg_width</structfield> and
|
||||
<structfield>null_frac</structfield> values for the attribute
|
||||
<structfield>col1</structfield> of the table
|
||||
<structname>mytable</structname>:
|
||||
<programlisting>
|
||||
SELECT pg_restore_attribute_stats(
|
||||
@ -30315,25 +30315,26 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
|
||||
</para>
|
||||
<para>
|
||||
The required arguments are <literal>relation</literal> with a value
|
||||
of type <type>regclass</type>, which specifies the table;
|
||||
<literal>attname</literal> with a value of type <type>name</type>,
|
||||
of type <type>regclass</type>, which specifies the table; either
|
||||
<literal>attname</literal> with a value of type <type>name</type> or
|
||||
<literal>attnum</literal> with a value of type <type>smallint</type>,
|
||||
which specifies the column; and <literal>inherited</literal>, which
|
||||
specifies whether the statistics includes values from child tables.
|
||||
Other arguments are the names of statistics corresponding to columns
|
||||
in <link
|
||||
specifies whether the statistics include values from child tables.
|
||||
Other arguments are the names and values of statistics corresponding
|
||||
to columns in <link
|
||||
linkend="view-pg-stats"><structname>pg_stats</structname></link>.
|
||||
</para>
|
||||
<para>
|
||||
Additionally, this function supports argument name
|
||||
Additionally, this function accepts argument name
|
||||
<literal>version</literal> of type <type>integer</type>, which
|
||||
specifies the version from which the statistics originated, improving
|
||||
interpretation of statistics from older versions of
|
||||
<productname>PostgreSQL</productname>.
|
||||
specifies the server version from which the statistics originated.
|
||||
This is anticipated to be helpful in porting statistics from older
|
||||
versions of <productname>PostgreSQL</productname>.
|
||||
</para>
|
||||
<para>
|
||||
Minor errors are reported as a <literal>WARNING</literal> and
|
||||
ignored, and remaining statistics will still be restored. If all
|
||||
specified statistics are successfully restored, return
|
||||
specified statistics are successfully restored, returns
|
||||
<literal>true</literal>, otherwise <literal>false</literal>.
|
||||
</para>
|
||||
<para>
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -4720,15 +4720,32 @@ 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,
|
||||
},
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user