Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : * relation_stats.c
3 : : *
4 : : * PostgreSQL relation statistics manipulation
5 : : *
6 : : * Code supporting the direct import of relation statistics, similar to
7 : : * what is done by the ANALYZE command.
8 : : *
9 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
10 : : * Portions Copyright (c) 1994, Regents of the University of California
11 : : *
12 : : * IDENTIFICATION
13 : : * src/backend/statistics/relation_stats.c
14 : : *
15 : : *-------------------------------------------------------------------------
16 : : */
17 : :
18 : : #include "postgres.h"
19 : :
20 : : #include <math.h>
21 : :
22 : : #include "access/heapam.h"
23 : : #include "catalog/indexing.h"
24 : : #include "catalog/namespace.h"
25 : : #include "nodes/makefuncs.h"
26 : : #include "statistics/statistics.h"
27 : : #include "statistics/stat_utils.h"
28 : : #include "utils/builtins.h"
29 : : #include "utils/fmgroids.h"
30 : : #include "utils/fmgrprotos.h"
31 : : #include "utils/lsyscache.h"
32 : : #include "utils/syscache.h"
33 : :
34 : :
35 : : /*
36 : : * Positional argument numbers, names, and types for
37 : : * relation_statistics_update().
38 : : */
39 : :
40 : : enum relation_stats_argnum
41 : : {
42 : : RELSCHEMA_ARG = 0,
43 : : RELNAME_ARG,
44 : : RELPAGES_ARG,
45 : : RELTUPLES_ARG,
46 : : RELALLVISIBLE_ARG,
47 : : RELALLFROZEN_ARG,
48 : : NUM_RELATION_STATS_ARGS
49 : : };
50 : :
51 : : static struct StatsArgInfo relarginfo[] =
52 : : {
53 : : [RELSCHEMA_ARG] = {"schemaname", TEXTOID},
54 : : [RELNAME_ARG] = {"relname", TEXTOID},
55 : : [RELPAGES_ARG] = {"relpages", INT4OID},
56 : : [RELTUPLES_ARG] = {"reltuples", FLOAT4OID},
57 : : [RELALLVISIBLE_ARG] = {"relallvisible", INT4OID},
58 : : [RELALLFROZEN_ARG] = {"relallfrozen", INT4OID},
59 : : [NUM_RELATION_STATS_ARGS] = {0}
60 : : };
61 : :
62 : : static bool relation_statistics_update(const NullableDatum *args);
63 : : static bool relation_statistics_update_internal(Oid reloid,
64 : : const RelationStatsValues *statvalues);
65 : :
66 : : /*
67 : : * Internal function for modifying statistics for a relation.
68 : : */
69 : : static bool
70 : 1249 : relation_statistics_update(const NullableDatum *args)
71 : : {
72 : : char *nspname;
73 : : char *relname;
74 : : Oid reloid;
75 : 1249 : Oid locked_table = InvalidOid;
76 : : RelationStatsValues values;
77 : :
78 : 1249 : stats_check_required_arg(args, relarginfo, RELSCHEMA_ARG);
79 : 1241 : stats_check_required_arg(args, relarginfo, RELNAME_ARG);
80 : :
81 : 1233 : nspname = TextDatumGetCString(args[RELSCHEMA_ARG].value);
82 : 1233 : relname = TextDatumGetCString(args[RELNAME_ARG].value);
83 : :
84 [ - + ]: 1233 : if (RecoveryInProgress())
85 [ # # ]: 0 : ereport(ERROR,
86 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
87 : : errmsg("recovery is in progress"),
88 : : errhint("Statistics cannot be modified during recovery.")));
89 : :
90 : 1233 : reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
91 : : ShareUpdateExclusiveLock, 0,
92 : : RangeVarCallbackForStats, &locked_table);
93 : :
94 : : /* Collect the values to apply. */
95 : 1217 : values.version.value = (Datum) 0;
96 : 1217 : values.version.isnull = true;
97 : 1217 : values.relpages = args[RELPAGES_ARG];
98 : 1217 : values.reltuples = args[RELTUPLES_ARG];
99 : 1217 : values.relallvisible = args[RELALLVISIBLE_ARG];
100 : 1217 : values.relallfrozen = args[RELALLFROZEN_ARG];
101 : :
102 : 1217 : return relation_statistics_update_internal(reloid, &values);
103 : : }
104 : :
105 : : /*
106 : : * Workhorse function for relation_statistics_update.
107 : : */
108 : : static bool
109 : 1224 : relation_statistics_update_internal(Oid reloid,
110 : : const RelationStatsValues *statvalues)
111 : : {
112 : 1224 : int32 relpages = 0;
113 : 1224 : bool update_relpages = false;
114 : 1224 : float4 reltuples = 0;
115 : 1224 : bool update_reltuples = false;
116 : 1224 : int32 relallvisible = 0;
117 : 1224 : bool update_relallvisible = false;
118 : 1224 : int32 relallfrozen = 0;
119 : 1224 : bool update_relallfrozen = false;
120 : : Relation crel;
121 : : HeapTuple ctup;
122 : : Form_pg_class pgcform;
123 : 1224 : int replaces[4] = {0};
124 : 1224 : Datum values[4] = {0};
125 : 1224 : bool nulls[4] = {0};
126 : 1224 : int nreplaces = 0;
127 : 1224 : bool result = true;
128 : :
129 [ + + ]: 1224 : if (!statvalues->relpages.isnull)
130 : : {
131 : 1184 : relpages = DatumGetInt32(statvalues->relpages.value);
132 : 1184 : update_relpages = true;
133 : : }
134 : :
135 [ + + ]: 1224 : if (!statvalues->reltuples.isnull)
136 : : {
137 : 1200 : reltuples = DatumGetFloat4(statvalues->reltuples.value);
138 [ + + + + ]: 1200 : if (isnan(reltuples) || isinf(reltuples))
139 : : {
140 [ + - ]: 12 : ereport(WARNING,
141 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
142 : : errmsg("argument \"%s\" must be a finite value", "reltuples")));
143 : 12 : result = false;
144 : : }
145 [ + + ]: 1188 : else if (reltuples < -1.0)
146 : : {
147 [ + - ]: 4 : ereport(WARNING,
148 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
149 : : errmsg("argument \"%s\" must not be less than -1.0", "reltuples")));
150 : 4 : result = false;
151 : : }
152 : : else
153 : 1184 : update_reltuples = true;
154 : : }
155 : :
156 [ + + ]: 1224 : if (!statvalues->relallvisible.isnull)
157 : : {
158 : 1169 : relallvisible = DatumGetInt32(statvalues->relallvisible.value);
159 : 1169 : update_relallvisible = true;
160 : : }
161 : :
162 [ + + ]: 1224 : if (!statvalues->relallfrozen.isnull)
163 : : {
164 : 1169 : relallfrozen = DatumGetInt32(statvalues->relallfrozen.value);
165 : 1169 : update_relallfrozen = true;
166 : : }
167 : :
168 : : /*
169 : : * Take RowExclusiveLock on pg_class, consistent with
170 : : * vac_update_relstats().
171 : : */
172 : 1224 : crel = table_open(RelationRelationId, RowExclusiveLock);
173 : :
174 : 1224 : ctup = SearchSysCache1(RELOID, ObjectIdGetDatum(reloid));
175 [ - + ]: 1224 : if (!HeapTupleIsValid(ctup))
176 [ # # ]: 0 : elog(ERROR, "pg_class entry for relid %u not found", reloid);
177 : :
178 : 1224 : pgcform = (Form_pg_class) GETSTRUCT(ctup);
179 : :
180 [ + + + + ]: 1224 : if (update_relpages && relpages != pgcform->relpages)
181 : : {
182 : 594 : replaces[nreplaces] = Anum_pg_class_relpages;
183 : 594 : values[nreplaces] = Int32GetDatum(relpages);
184 : 594 : nreplaces++;
185 : : }
186 : :
187 [ + + + + ]: 1224 : if (update_reltuples && reltuples != pgcform->reltuples)
188 : : {
189 : 536 : replaces[nreplaces] = Anum_pg_class_reltuples;
190 : 536 : values[nreplaces] = Float4GetDatum(reltuples);
191 : 536 : nreplaces++;
192 : : }
193 : :
194 [ + + + + ]: 1224 : if (update_relallvisible && relallvisible != pgcform->relallvisible)
195 : : {
196 : 160 : replaces[nreplaces] = Anum_pg_class_relallvisible;
197 : 160 : values[nreplaces] = Int32GetDatum(relallvisible);
198 : 160 : nreplaces++;
199 : : }
200 : :
201 [ + + + + ]: 1224 : if (update_relallfrozen && relallfrozen != pgcform->relallfrozen)
202 : : {
203 : 114 : replaces[nreplaces] = Anum_pg_class_relallfrozen;
204 : 114 : values[nreplaces] = Int32GetDatum(relallfrozen);
205 : 114 : nreplaces++;
206 : : }
207 : :
208 [ + + ]: 1224 : if (nreplaces > 0)
209 : : {
210 : 710 : TupleDesc tupdesc = RelationGetDescr(crel);
211 : : HeapTuple newtup;
212 : :
213 : 710 : newtup = heap_modify_tuple_by_cols(ctup, tupdesc, nreplaces,
214 : : replaces, values, nulls);
215 : 710 : CatalogTupleUpdate(crel, &newtup->t_self, newtup);
216 : 710 : heap_freetuple(newtup);
217 : : }
218 : :
219 : 1224 : ReleaseSysCache(ctup);
220 : :
221 : : /* release the lock, consistent with vac_update_relstats() */
222 : 1224 : table_close(crel, RowExclusiveLock);
223 : :
224 : 1224 : CommandCounterIncrement();
225 : :
226 : 1224 : return result;
227 : : }
228 : :
229 : : /*
230 : : * Clear statistics for a given pg_class entry; that is, set back to initial
231 : : * stats for a newly-created table.
232 : : */
233 : : Datum
234 : 16 : pg_clear_relation_stats(PG_FUNCTION_ARGS)
235 : : {
236 : : NullableDatum args[NUM_RELATION_STATS_ARGS];
237 : :
238 : 16 : args[RELSCHEMA_ARG].value = PG_GETARG_DATUM(0);
239 : 16 : args[RELSCHEMA_ARG].isnull = PG_ARGISNULL(0);
240 : 16 : args[RELNAME_ARG].value = PG_GETARG_DATUM(1);
241 : 16 : args[RELNAME_ARG].isnull = PG_ARGISNULL(1);
242 : 16 : args[RELPAGES_ARG].value = Int32GetDatum(0);
243 : 16 : args[RELPAGES_ARG].isnull = false;
244 : 16 : args[RELTUPLES_ARG].value = Float4GetDatum(-1.0);
245 : 16 : args[RELTUPLES_ARG].isnull = false;
246 : 16 : args[RELALLVISIBLE_ARG].value = Int32GetDatum(0);
247 : 16 : args[RELALLVISIBLE_ARG].isnull = false;
248 : 16 : args[RELALLFROZEN_ARG].value = Int32GetDatum(0);
249 : 16 : args[RELALLFROZEN_ARG].isnull = false;
250 : :
251 : 16 : relation_statistics_update(args);
252 : 8 : PG_RETURN_VOID();
253 : : }
254 : :
255 : : Datum
256 : 1241 : pg_restore_relation_stats(PG_FUNCTION_ARGS)
257 : : {
258 : : NullableDatum positional_args[NUM_RELATION_STATS_ARGS];
259 : 1241 : bool result = true;
260 : :
261 [ + + ]: 1241 : if (!stats_fill_args_from_arg_pairs(fcinfo, positional_args,
262 : : relarginfo))
263 : 16 : result = false;
264 : :
265 [ + + ]: 1233 : if (!relation_statistics_update(positional_args))
266 : 16 : result = false;
267 : :
268 : 1209 : PG_RETURN_BOOL(result);
269 : : }
270 : :
271 : : /*
272 : : * Import relation statistics.
273 : : *
274 : : * See RelationStatsValues for the values to provide.
275 : : */
276 : : bool
277 : 7 : import_relation_statistics(Relation rel, const RelationStatsValues *statvalues)
278 : : {
279 : : Assert(statvalues);
280 : :
281 : 7 : return relation_statistics_update_internal(RelationGetRelid(rel),
282 : : statvalues);
283 : : }
|