Skip to content

Commit d32d146

Browse files
committed
Add functions pg_restore_relation_stats(), pg_restore_attribute_stats().
Similar to the pg_set_*_stats() functions, except with a variadic signature that's designed to be more future-proof. Additionally, most problems are reported as WARNINGs rather than ERRORs, allowing most stats to be restored even if some cannot. These functions are intended to be called from pg_dump to avoid the need to run ANALYZE after an upgrade. Author: Corey Huinker Discussion: https://postgr.es/m/CADkLM=eErgzn7ECDpwFcptJKOk9SxZEk5Pot4d94eVTZsvj3gw@mail.gmail.com
1 parent 534d0ea commit d32d146

File tree

9 files changed

+1891
-92
lines changed

9 files changed

+1891
-92
lines changed

doc/src/sgml/func.sgml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30267,6 +30267,55 @@ DETAIL: Make sure pg_wal_replay_wait() isn't called within a transaction with a
3026730267
</entry>
3026830268
</row>
3026930269

30270+
<row>
30271+
<entry role="func_table_entry"><para role="func_signature">
30272+
<indexterm>
30273+
<primary>pg_restore_relation_stats</primary>
30274+
</indexterm>
30275+
<function>pg_restore_relation_stats</function> (
30276+
<literal>VARIADIC</literal> <parameter>kwargs</parameter> <type>"any"</type> )
30277+
<returnvalue>boolean</returnvalue>
30278+
</para>
30279+
<para>
30280+
Similar to <function>pg_set_relation_stats()</function>, but intended
30281+
for bulk restore of relation statistics. The tracked statistics may
30282+
change from version to version, so the primary purpose of this
30283+
function is to maintain a consistent function signature to avoid
30284+
errors when restoring statistics from previous versions.
30285+
</para>
30286+
<para>
30287+
Arguments are passed as pairs of <replaceable>argname</replaceable>
30288+
and <replaceable>argvalue</replaceable>, where
30289+
<replaceable>argname</replaceable> corresponds to a named argument in
30290+
<function>pg_set_relation_stats()</function> and
30291+
<replaceable>argvalue</replaceable> is of the corresponding type.
30292+
</para>
30293+
<para>
30294+
Additionally, this function supports argument name
30295+
<literal>version</literal> of type <type>integer</type>, which
30296+
specifies the version from which the statistics originated, improving
30297+
intepretation of older statistics.
30298+
</para>
30299+
<para>
30300+
For example, to set the <structname>relpages</structname> and
30301+
<structname>reltuples</structname> of the table
30302+
<structname>mytable</structname>:
30303+
<programlisting>
30304+
SELECT pg_restore_relation_stats(
30305+
'relation', 'mytable'::regclass,
30306+
'relpages', 173::integer,
30307+
'reltuples', 10000::float4);
30308+
</programlisting>
30309+
</para>
30310+
<para>
30311+
Minor errors are reported as a <literal>WARNING</literal> and
30312+
ignored, and remaining statistics will still be restored. If all
30313+
specified statistics are successfully restored, return
30314+
<literal>true</literal>, otherwise <literal>false</literal>.
30315+
</para>
30316+
</entry>
30317+
</row>
30318+
3027030319
<row>
3027130320
<entry role="func_table_entry">
3027230321
<para role="func_signature">
@@ -30338,6 +30387,57 @@ DETAIL: Make sure pg_wal_replay_wait() isn't called within a transaction with a
3033830387
</entry>
3033930388
</row>
3034030389

30390+
<row>
30391+
<entry role="func_table_entry"><para role="func_signature">
30392+
<indexterm>
30393+
<primary>pg_restore_attribute_stats</primary>
30394+
</indexterm>
30395+
<function>pg_restore_attribute_stats</function> (
30396+
<literal>VARIADIC</literal> <parameter>kwargs</parameter> <type>"any"</type> )
30397+
<returnvalue>boolean</returnvalue>
30398+
</para>
30399+
<para>
30400+
Similar to <function>pg_set_attribute_stats()</function>, but
30401+
intended for bulk restore of attribute statistics. The tracked
30402+
statistics may change from version to version, so the primary purpose
30403+
of this function is to maintain a consistent function signature to
30404+
avoid errors when restoring statistics from previous versions.
30405+
</para>
30406+
<para>
30407+
Arguments are passed as pairs of <replaceable>argname</replaceable>
30408+
and <replaceable>argvalue</replaceable>, where
30409+
<replaceable>argname</replaceable> corresponds to a named argument in
30410+
<function>pg_set_attribute_stats()</function> and
30411+
<replaceable>argvalue</replaceable> is of the corresponding type.
30412+
</para>
30413+
<para>
30414+
Additionally, this function supports argument name
30415+
<literal>version</literal> of type <type>integer</type>, which
30416+
specifies the version from which the statistics originated, improving
30417+
intepretation of older statistics.
30418+
</para>
30419+
<para>
30420+
For example, to set the <structname>avg_width</structname> and
30421+
<structname>null_frac</structname> for the attribute
30422+
<structname>col1</structname> of the table
30423+
<structname>mytable</structname>:
30424+
<programlisting>
30425+
SELECT pg_restore_attribute_stats(
30426+
'relation', 'mytable'::regclass,
30427+
'attname', 'col1'::name,
30428+
'inherited', false,
30429+
'avg_width', 125::integer,
30430+
'null_frac', 0.5::real);
30431+
</programlisting>
30432+
</para>
30433+
<para>
30434+
Minor errors are reported as a <literal>WARNING</literal> and
30435+
ignored, and remaining statistics will still be restored. If all
30436+
specified statistics are successfully restored, return
30437+
<literal>true</literal>, otherwise <literal>false</literal>.
30438+
</para>
30439+
</entry>
30440+
</row>
3034130441
</tbody>
3034230442
</tgroup>
3034330443
</table>

src/backend/statistics/attribute_stats.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,3 +877,22 @@ pg_clear_attribute_stats(PG_FUNCTION_ARGS)
877877
delete_pg_statistic(reloid, attnum, inherited);
878878
PG_RETURN_VOID();
879879
}
880+
881+
Datum
882+
pg_restore_attribute_stats(PG_FUNCTION_ARGS)
883+
{
884+
LOCAL_FCINFO(positional_fcinfo, NUM_ATTRIBUTE_STATS_ARGS);
885+
bool result = true;
886+
887+
InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
888+
InvalidOid, NULL, NULL);
889+
890+
if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
891+
attarginfo, WARNING))
892+
result = false;
893+
894+
if (!attribute_statistics_update(positional_fcinfo, WARNING))
895+
result = false;
896+
897+
PG_RETURN_BOOL(result);
898+
}

src/backend/statistics/relation_stats.c

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel)
6767
bool nulls[3] = {0};
6868
int ncols = 0;
6969
TupleDesc tupdesc;
70-
HeapTuple newtup;
71-
70+
bool result = true;
7271

7372
stats_check_required_arg(fcinfo, relarginfo, RELATION_ARG);
7473
reloid = PG_GETARG_OID(RELATION_ARG);
@@ -109,11 +108,9 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel)
109108
ereport(elevel,
110109
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
111110
errmsg("relpages cannot be < -1")));
112-
table_close(crel, RowExclusiveLock);
113-
return false;
111+
result = false;
114112
}
115-
116-
if (relpages != pgcform->relpages)
113+
else if (relpages != pgcform->relpages)
117114
{
118115
replaces[ncols] = Anum_pg_class_relpages;
119116
values[ncols] = Int32GetDatum(relpages);
@@ -130,16 +127,15 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel)
130127
ereport(elevel,
131128
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
132129
errmsg("reltuples cannot be < -1.0")));
133-
table_close(crel, RowExclusiveLock);
134-
return false;
130+
result = false;
135131
}
136-
137-
if (reltuples != pgcform->reltuples)
132+
else if (reltuples != pgcform->reltuples)
138133
{
139134
replaces[ncols] = Anum_pg_class_reltuples;
140135
values[ncols] = Float4GetDatum(reltuples);
141136
ncols++;
142137
}
138+
143139
}
144140

145141
if (!PG_ARGISNULL(RELALLVISIBLE_ARG))
@@ -151,11 +147,9 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel)
151147
ereport(elevel,
152148
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
153149
errmsg("relallvisible cannot be < 0")));
154-
table_close(crel, RowExclusiveLock);
155-
return false;
150+
result = false;
156151
}
157-
158-
if (relallvisible != pgcform->relallvisible)
152+
else if (relallvisible != pgcform->relallvisible)
159153
{
160154
replaces[ncols] = Anum_pg_class_relallvisible;
161155
values[ncols] = Int32GetDatum(relallvisible);
@@ -164,22 +158,20 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel)
164158
}
165159

166160
/* only update pg_class if there is a meaningful change */
167-
if (ncols == 0)
161+
if (ncols > 0)
168162
{
169-
table_close(crel, RowExclusiveLock);
170-
return false;
171-
}
172-
173-
newtup = heap_modify_tuple_by_cols(ctup, tupdesc, ncols, replaces, values,
174-
nulls);
163+
HeapTuple newtup;
175164

176-
CatalogTupleUpdate(crel, &newtup->t_self, newtup);
177-
heap_freetuple(newtup);
165+
newtup = heap_modify_tuple_by_cols(ctup, tupdesc, ncols, replaces, values,
166+
nulls);
167+
CatalogTupleUpdate(crel, &newtup->t_self, newtup);
168+
heap_freetuple(newtup);
169+
}
178170

179171
/* release the lock, consistent with vac_update_relstats() */
180172
table_close(crel, RowExclusiveLock);
181173

182-
return true;
174+
return result;
183175
}
184176

185177
/*
@@ -215,3 +207,23 @@ pg_clear_relation_stats(PG_FUNCTION_ARGS)
215207
relation_statistics_update(newfcinfo, ERROR);
216208
PG_RETURN_VOID();
217209
}
210+
211+
Datum
212+
pg_restore_relation_stats(PG_FUNCTION_ARGS)
213+
{
214+
LOCAL_FCINFO(positional_fcinfo, NUM_RELATION_STATS_ARGS);
215+
bool result = true;
216+
217+
InitFunctionCallInfoData(*positional_fcinfo, NULL,
218+
NUM_RELATION_STATS_ARGS,
219+
InvalidOid, NULL, NULL);
220+
221+
if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
222+
relarginfo, WARNING))
223+
result = false;
224+
225+
if (!relation_statistics_update(positional_fcinfo, WARNING))
226+
result = false;
227+
228+
PG_RETURN_BOOL(result);
229+
}

src/backend/statistics/stat_utils.c

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "access/relation.h"
2020
#include "catalog/pg_database.h"
21+
#include "funcapi.h"
2122
#include "miscadmin.h"
2223
#include "statistics/stat_utils.h"
2324
#include "utils/acl.h"
@@ -165,3 +166,128 @@ stats_lock_check_privileges(Oid reloid)
165166

166167
relation_close(rel, NoLock);
167168
}
169+
170+
/*
171+
* Find the argument number for the given argument name, returning -1 if not
172+
* found.
173+
*/
174+
static int
175+
get_arg_by_name(const char *argname, struct StatsArgInfo *arginfo, int elevel)
176+
{
177+
int argnum;
178+
179+
for (argnum = 0; arginfo[argnum].argname != NULL; argnum++)
180+
if (pg_strcasecmp(argname, arginfo[argnum].argname) == 0)
181+
return argnum;
182+
183+
ereport(elevel,
184+
(errmsg("unrecognized argument name: \"%s\"", argname)));
185+
186+
return -1;
187+
}
188+
189+
/*
190+
* Ensure that a given argument matched the expected type.
191+
*/
192+
static bool
193+
stats_check_arg_type(const char *argname, Oid argtype, Oid expectedtype, int elevel)
194+
{
195+
if (argtype != expectedtype)
196+
{
197+
ereport(elevel,
198+
(errmsg("argument \"%s\" has type \"%s\", expected type \"%s\"",
199+
argname, format_type_be(argtype),
200+
format_type_be(expectedtype))));
201+
return false;
202+
}
203+
204+
return true;
205+
}
206+
207+
/*
208+
* Translate variadic argument pairs from 'pairs_fcinfo' into a
209+
* 'positional_fcinfo' appropriate for calling relation_statistics_update() or
210+
* attribute_statistics_update() with positional arguments.
211+
*
212+
* Caller should have already initialized positional_fcinfo with a size
213+
* appropriate for calling the intended positional function, and arginfo
214+
* should also match the intended positional function.
215+
*/
216+
bool
217+
stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
218+
FunctionCallInfo positional_fcinfo,
219+
struct StatsArgInfo *arginfo,
220+
int elevel)
221+
{
222+
Datum *args;
223+
bool *argnulls;
224+
Oid *types;
225+
int nargs;
226+
bool result = true;
227+
228+
/* clear positional args */
229+
for (int i = 0; arginfo[i].argname != NULL; i++)
230+
{
231+
positional_fcinfo->args[i].value = (Datum) 0;
232+
positional_fcinfo->args[i].isnull = true;
233+
}
234+
235+
nargs = extract_variadic_args(pairs_fcinfo, 0, true,
236+
&args, &types, &argnulls);
237+
238+
if (nargs % 2 != 0)
239+
ereport(ERROR,
240+
errmsg("variadic arguments must be name/value pairs"),
241+
errhint("Provide an even number of variadic arguments that can be divided into pairs."));
242+
243+
/*
244+
* For each argument name/value pair, find corresponding positional
245+
* argument for the argument name, and assign the argument value to
246+
* postitional_fcinfo.
247+
*/
248+
for (int i = 0; i < nargs; i += 2)
249+
{
250+
int argnum;
251+
char *argname;
252+
253+
if (argnulls[i])
254+
ereport(ERROR,
255+
(errmsg("name at variadic position %d is NULL", i + 1)));
256+
257+
if (types[i] != TEXTOID)
258+
ereport(ERROR,
259+
(errmsg("name at variadic position %d has type \"%s\", expected type \"%s\"",
260+
i + 1, format_type_be(types[i]),
261+
format_type_be(TEXTOID))));
262+
263+
if (argnulls[i + 1])
264+
continue;
265+
266+
argname = TextDatumGetCString(args[i]);
267+
268+
/*
269+
* The 'version' argument is a special case, not handled by arginfo
270+
* because it's not a valid positional argument.
271+
*
272+
* For now, 'version' is accepted but ignored. In the future it can be
273+
* used to interpret older statistics properly.
274+
*/
275+
if (pg_strcasecmp(argname, "version") == 0)
276+
continue;
277+
278+
argnum = get_arg_by_name(argname, arginfo, elevel);
279+
280+
if (argnum < 0 || !stats_check_arg_type(argname, types[i + 1],
281+
arginfo[argnum].argtype,
282+
elevel))
283+
{
284+
result = false;
285+
continue;
286+
}
287+
288+
positional_fcinfo->args[argnum].value = args[i + 1];
289+
positional_fcinfo->args[argnum].isnull = false;
290+
}
291+
292+
return result;
293+
}

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,6 @@
5757
*/
5858

5959
/* yyyymmddN */
60-
#define CATALOG_VERSION_NO 202410241
60+
#define CATALOG_VERSION_NO 202410242
6161

6262
#endif

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy