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 "access/heapam.h"
21 : : #include "catalog/indexing.h"
22 : : #include "catalog/namespace.h"
23 : : #include "nodes/makefuncs.h"
24 : : #include "statistics/statistics.h"
25 : : #include "statistics/stat_utils.h"
26 : : #include "utils/builtins.h"
27 : : #include "utils/fmgroids.h"
28 : : #include "utils/fmgrprotos.h"
29 : : #include "utils/lsyscache.h"
30 : : #include "utils/syscache.h"
31 : :
32 : :
33 : : /*
34 : : * Positional argument numbers, names, and types for
35 : : * relation_statistics_update().
36 : : */
37 : :
38 : : enum relation_stats_argnum
39 : : {
40 : : RELSCHEMA_ARG = 0,
41 : : RELNAME_ARG,
42 : : RELPAGES_ARG,
43 : : RELTUPLES_ARG,
44 : : RELALLVISIBLE_ARG,
45 : : RELALLFROZEN_ARG,
46 : : NUM_RELATION_STATS_ARGS
47 : : };
48 : :
49 : : static struct StatsArgInfo relarginfo[] =
50 : : {
51 : : [RELSCHEMA_ARG] = {"schemaname", TEXTOID},
52 : : [RELNAME_ARG] = {"relname", TEXTOID},
53 : : [RELPAGES_ARG] = {"relpages", INT4OID},
54 : : [RELTUPLES_ARG] = {"reltuples", FLOAT4OID},
55 : : [RELALLVISIBLE_ARG] = {"relallvisible", INT4OID},
56 : : [RELALLFROZEN_ARG] = {"relallfrozen", INT4OID},
57 : : [NUM_RELATION_STATS_ARGS] = {0}
58 : : };
59 : :
60 : : static bool relation_statistics_update(FunctionCallInfo fcinfo);
61 : : static bool relation_statistics_update_internal(Oid reloid,
62 : : FunctionCallInfo fcinfo);
63 : :
64 : : /*
65 : : * Internal function for modifying statistics for a relation.
66 : : */
67 : : static bool
68 : 1224 : relation_statistics_update(FunctionCallInfo fcinfo)
69 : : {
70 : : char *nspname;
71 : : char *relname;
72 : : Oid reloid;
73 : 1224 : Oid locked_table = InvalidOid;
74 : :
75 : 1224 : stats_check_required_arg(fcinfo, relarginfo, RELSCHEMA_ARG);
76 : 1216 : stats_check_required_arg(fcinfo, relarginfo, RELNAME_ARG);
77 : :
78 : 1208 : nspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
79 : 1208 : relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
80 : :
81 [ - + ]: 1208 : if (RecoveryInProgress())
82 [ # # ]: 0 : ereport(ERROR,
83 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
84 : : errmsg("recovery is in progress"),
85 : : errhint("Statistics cannot be modified during recovery.")));
86 : :
87 : 1208 : reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
88 : : ShareUpdateExclusiveLock, 0,
89 : : RangeVarCallbackForStats, &locked_table);
90 : :
91 : 1192 : return relation_statistics_update_internal(reloid, fcinfo);
92 : : }
93 : :
94 : : /*
95 : : * Workhorse function for relation_statistics_update.
96 : : */
97 : : static bool
98 : 1199 : relation_statistics_update_internal(Oid reloid, FunctionCallInfo fcinfo)
99 : : {
100 : 1199 : BlockNumber relpages = 0;
101 : 1199 : bool update_relpages = false;
102 : 1199 : float reltuples = 0;
103 : 1199 : bool update_reltuples = false;
104 : 1199 : BlockNumber relallvisible = 0;
105 : 1199 : bool update_relallvisible = false;
106 : 1199 : BlockNumber relallfrozen = 0;
107 : 1199 : bool update_relallfrozen = false;
108 : : Relation crel;
109 : : HeapTuple ctup;
110 : : Form_pg_class pgcform;
111 : 1199 : int replaces[4] = {0};
112 : 1199 : Datum values[4] = {0};
113 : 1199 : bool nulls[4] = {0};
114 : 1199 : int nreplaces = 0;
115 : 1199 : bool result = true;
116 : :
117 [ + + ]: 1199 : if (!PG_ARGISNULL(RELPAGES_ARG))
118 : : {
119 : 1183 : relpages = PG_GETARG_UINT32(RELPAGES_ARG);
120 : 1183 : update_relpages = true;
121 : : }
122 : :
123 [ + + ]: 1199 : if (!PG_ARGISNULL(RELTUPLES_ARG))
124 : : {
125 : 1175 : reltuples = PG_GETARG_FLOAT4(RELTUPLES_ARG);
126 [ - + ]: 1175 : if (reltuples < -1.0)
127 : : {
128 [ # # ]: 0 : ereport(WARNING,
129 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
130 : : errmsg("argument \"%s\" must not be less than -1.0", "reltuples")));
131 : 0 : result = false;
132 : : }
133 : : else
134 : 1175 : update_reltuples = true;
135 : : }
136 : :
137 [ + + ]: 1199 : if (!PG_ARGISNULL(RELALLVISIBLE_ARG))
138 : : {
139 : 1168 : relallvisible = PG_GETARG_UINT32(RELALLVISIBLE_ARG);
140 : 1168 : update_relallvisible = true;
141 : : }
142 : :
143 [ + + ]: 1199 : if (!PG_ARGISNULL(RELALLFROZEN_ARG))
144 : : {
145 : 1168 : relallfrozen = PG_GETARG_UINT32(RELALLFROZEN_ARG);
146 : 1168 : update_relallfrozen = true;
147 : : }
148 : :
149 : : /*
150 : : * Take RowExclusiveLock on pg_class, consistent with
151 : : * vac_update_relstats().
152 : : */
153 : 1199 : crel = table_open(RelationRelationId, RowExclusiveLock);
154 : :
155 : 1199 : ctup = SearchSysCache1(RELOID, ObjectIdGetDatum(reloid));
156 [ - + ]: 1199 : if (!HeapTupleIsValid(ctup))
157 [ # # ]: 0 : elog(ERROR, "pg_class entry for relid %u not found", reloid);
158 : :
159 : 1199 : pgcform = (Form_pg_class) GETSTRUCT(ctup);
160 : :
161 [ + + + + ]: 1199 : if (update_relpages && relpages != pgcform->relpages)
162 : : {
163 : 592 : replaces[nreplaces] = Anum_pg_class_relpages;
164 : 592 : values[nreplaces] = UInt32GetDatum(relpages);
165 : 592 : nreplaces++;
166 : : }
167 : :
168 [ + + + + ]: 1199 : if (update_reltuples && reltuples != pgcform->reltuples)
169 : : {
170 : 524 : replaces[nreplaces] = Anum_pg_class_reltuples;
171 : 524 : values[nreplaces] = Float4GetDatum(reltuples);
172 : 524 : nreplaces++;
173 : : }
174 : :
175 [ + + + + ]: 1199 : if (update_relallvisible && relallvisible != pgcform->relallvisible)
176 : : {
177 : 143 : replaces[nreplaces] = Anum_pg_class_relallvisible;
178 : 143 : values[nreplaces] = UInt32GetDatum(relallvisible);
179 : 143 : nreplaces++;
180 : : }
181 : :
182 [ + + + + ]: 1199 : if (update_relallfrozen && relallfrozen != pgcform->relallfrozen)
183 : : {
184 : 101 : replaces[nreplaces] = Anum_pg_class_relallfrozen;
185 : 101 : values[nreplaces] = UInt32GetDatum(relallfrozen);
186 : 101 : nreplaces++;
187 : : }
188 : :
189 [ + + ]: 1199 : if (nreplaces > 0)
190 : : {
191 : 700 : TupleDesc tupdesc = RelationGetDescr(crel);
192 : : HeapTuple newtup;
193 : :
194 : 700 : newtup = heap_modify_tuple_by_cols(ctup, tupdesc, nreplaces,
195 : : replaces, values, nulls);
196 : 700 : CatalogTupleUpdate(crel, &newtup->t_self, newtup);
197 : 700 : heap_freetuple(newtup);
198 : : }
199 : :
200 : 1199 : ReleaseSysCache(ctup);
201 : :
202 : : /* release the lock, consistent with vac_update_relstats() */
203 : 1199 : table_close(crel, RowExclusiveLock);
204 : :
205 : 1199 : CommandCounterIncrement();
206 : :
207 : 1199 : return result;
208 : : }
209 : :
210 : : /*
211 : : * Clear statistics for a given pg_class entry; that is, set back to initial
212 : : * stats for a newly-created table.
213 : : */
214 : : Datum
215 : 16 : pg_clear_relation_stats(PG_FUNCTION_ARGS)
216 : : {
217 : 16 : LOCAL_FCINFO(newfcinfo, 6);
218 : :
219 : 16 : InitFunctionCallInfoData(*newfcinfo, NULL, 6, InvalidOid, NULL, NULL);
220 : :
221 : 16 : newfcinfo->args[0].value = PG_GETARG_DATUM(0);
222 : 16 : newfcinfo->args[0].isnull = PG_ARGISNULL(0);
223 : 16 : newfcinfo->args[1].value = PG_GETARG_DATUM(1);
224 : 16 : newfcinfo->args[1].isnull = PG_ARGISNULL(1);
225 : 16 : newfcinfo->args[2].value = UInt32GetDatum(0);
226 : 16 : newfcinfo->args[2].isnull = false;
227 : 16 : newfcinfo->args[3].value = Float4GetDatum(-1.0);
228 : 16 : newfcinfo->args[3].isnull = false;
229 : 16 : newfcinfo->args[4].value = UInt32GetDatum(0);
230 : 16 : newfcinfo->args[4].isnull = false;
231 : 16 : newfcinfo->args[5].value = UInt32GetDatum(0);
232 : 16 : newfcinfo->args[5].isnull = false;
233 : :
234 : 16 : relation_statistics_update(newfcinfo);
235 : 8 : PG_RETURN_VOID();
236 : : }
237 : :
238 : : Datum
239 : 1216 : pg_restore_relation_stats(PG_FUNCTION_ARGS)
240 : : {
241 : 1216 : LOCAL_FCINFO(positional_fcinfo, NUM_RELATION_STATS_ARGS);
242 : 1216 : bool result = true;
243 : :
244 : 1216 : InitFunctionCallInfoData(*positional_fcinfo, NULL,
245 : : NUM_RELATION_STATS_ARGS,
246 : : InvalidOid, NULL, NULL);
247 : :
248 [ + + ]: 1216 : if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
249 : : relarginfo))
250 : 16 : result = false;
251 : :
252 [ - + ]: 1208 : if (!relation_statistics_update(positional_fcinfo))
253 : 0 : result = false;
254 : :
255 : 1184 : PG_RETURN_BOOL(result);
256 : : }
257 : :
258 : : /*
259 : : * Import relation statistics from NullableDatum inputs for all statistical
260 : : * values.
261 : : *
262 : : * For now, the 'version' argument is ignored. In the future it can be used
263 : : * to interpret older statistics properly.
264 : : */
265 : : bool
266 : 7 : import_relation_statistics(Relation rel,
267 : : const NullableDatum *version,
268 : : const NullableDatum *relpages,
269 : : const NullableDatum *reltuples,
270 : : const NullableDatum *relallvisible,
271 : : const NullableDatum *relallfrozen)
272 : : {
273 : 7 : LOCAL_FCINFO(newfcinfo, NUM_RELATION_STATS_ARGS);
274 : :
275 : : Assert(relpages);
276 : : Assert(reltuples);
277 : : Assert(relallvisible);
278 : : Assert(relallfrozen);
279 : :
280 : 7 : InitFunctionCallInfoData(*newfcinfo, NULL, NUM_RELATION_STATS_ARGS,
281 : : InvalidOid, NULL, NULL);
282 : :
283 : 7 : newfcinfo->args[RELSCHEMA_ARG].value =
284 : 7 : CStringGetTextDatum(get_namespace_name(RelationGetNamespace(rel)));
285 : 7 : newfcinfo->args[RELSCHEMA_ARG].isnull = false;
286 : 7 : newfcinfo->args[RELNAME_ARG].value =
287 : 7 : CStringGetTextDatum(RelationGetRelationName(rel));
288 : 7 : newfcinfo->args[RELNAME_ARG].isnull = false;
289 : :
290 : 7 : newfcinfo->args[RELPAGES_ARG] = *relpages;
291 : 7 : newfcinfo->args[RELTUPLES_ARG] = *reltuples;
292 : 7 : newfcinfo->args[RELALLVISIBLE_ARG] = *relallvisible;
293 : 7 : newfcinfo->args[RELALLFROZEN_ARG] = *relallfrozen;
294 : :
295 : 7 : return relation_statistics_update_internal(RelationGetRelid(rel),
296 : : newfcinfo);
297 : : }
|