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-2025, 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/stat_utils.h"
25 : #include "utils/builtins.h"
26 : #include "utils/fmgroids.h"
27 : #include "utils/fmgrprotos.h"
28 : #include "utils/lsyscache.h"
29 : #include "utils/syscache.h"
30 :
31 :
32 : /*
33 : * Positional argument numbers, names, and types for
34 : * relation_statistics_update().
35 : */
36 :
37 : enum relation_stats_argnum
38 : {
39 : RELSCHEMA_ARG = 0,
40 : RELNAME_ARG,
41 : RELPAGES_ARG,
42 : RELTUPLES_ARG,
43 : RELALLVISIBLE_ARG,
44 : RELALLFROZEN_ARG,
45 : NUM_RELATION_STATS_ARGS
46 : };
47 :
48 : static struct StatsArgInfo relarginfo[] =
49 : {
50 : [RELSCHEMA_ARG] = {"schemaname", TEXTOID},
51 : [RELNAME_ARG] = {"relname", TEXTOID},
52 : [RELPAGES_ARG] = {"relpages", INT4OID},
53 : [RELTUPLES_ARG] = {"reltuples", FLOAT4OID},
54 : [RELALLVISIBLE_ARG] = {"relallvisible", INT4OID},
55 : [RELALLFROZEN_ARG] = {"relallfrozen", INT4OID},
56 : [NUM_RELATION_STATS_ARGS] = {0}
57 : };
58 :
59 : static bool relation_statistics_update(FunctionCallInfo fcinfo);
60 :
61 : /*
62 : * Internal function for modifying statistics for a relation.
63 : */
64 : static bool
65 2196 : relation_statistics_update(FunctionCallInfo fcinfo)
66 : {
67 2196 : bool result = true;
68 : char *nspname;
69 : char *relname;
70 : Oid reloid;
71 : Relation crel;
72 2196 : BlockNumber relpages = 0;
73 2196 : bool update_relpages = false;
74 2196 : float reltuples = 0;
75 2196 : bool update_reltuples = false;
76 2196 : BlockNumber relallvisible = 0;
77 2196 : bool update_relallvisible = false;
78 2196 : BlockNumber relallfrozen = 0;
79 2196 : bool update_relallfrozen = false;
80 : HeapTuple ctup;
81 : Form_pg_class pgcform;
82 2196 : int replaces[4] = {0};
83 2196 : Datum values[4] = {0};
84 2196 : bool nulls[4] = {0};
85 2196 : int nreplaces = 0;
86 2196 : Oid locked_table = InvalidOid;
87 :
88 2196 : stats_check_required_arg(fcinfo, relarginfo, RELSCHEMA_ARG);
89 2184 : stats_check_required_arg(fcinfo, relarginfo, RELNAME_ARG);
90 :
91 2172 : nspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
92 2172 : relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
93 :
94 2172 : if (RecoveryInProgress())
95 0 : ereport(ERROR,
96 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
97 : errmsg("recovery is in progress"),
98 : errhint("Statistics cannot be modified during recovery.")));
99 :
100 2172 : reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
101 : ShareUpdateExclusiveLock, 0,
102 : RangeVarCallbackForStats, &locked_table);
103 :
104 2148 : if (!PG_ARGISNULL(RELPAGES_ARG))
105 : {
106 2124 : relpages = PG_GETARG_UINT32(RELPAGES_ARG);
107 2124 : update_relpages = true;
108 : }
109 :
110 2148 : if (!PG_ARGISNULL(RELTUPLES_ARG))
111 : {
112 2112 : reltuples = PG_GETARG_FLOAT4(RELTUPLES_ARG);
113 2112 : if (reltuples < -1.0)
114 : {
115 0 : ereport(WARNING,
116 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
117 : errmsg("argument \"%s\" must not be less than -1.0", "reltuples")));
118 0 : result = false;
119 : }
120 : else
121 2112 : update_reltuples = true;
122 : }
123 :
124 2148 : if (!PG_ARGISNULL(RELALLVISIBLE_ARG))
125 : {
126 2112 : relallvisible = PG_GETARG_UINT32(RELALLVISIBLE_ARG);
127 2112 : update_relallvisible = true;
128 : }
129 :
130 2148 : if (!PG_ARGISNULL(RELALLFROZEN_ARG))
131 : {
132 2112 : relallfrozen = PG_GETARG_UINT32(RELALLFROZEN_ARG);
133 2112 : update_relallfrozen = true;
134 : }
135 :
136 : /*
137 : * Take RowExclusiveLock on pg_class, consistent with
138 : * vac_update_relstats().
139 : */
140 2148 : crel = table_open(RelationRelationId, RowExclusiveLock);
141 :
142 2148 : ctup = SearchSysCache1(RELOID, ObjectIdGetDatum(reloid));
143 2148 : if (!HeapTupleIsValid(ctup))
144 0 : elog(ERROR, "pg_class entry for relid %u not found", reloid);
145 :
146 2148 : pgcform = (Form_pg_class) GETSTRUCT(ctup);
147 :
148 2148 : if (update_relpages && relpages != pgcform->relpages)
149 : {
150 1096 : replaces[nreplaces] = Anum_pg_class_relpages;
151 1096 : values[nreplaces] = UInt32GetDatum(relpages);
152 1096 : nreplaces++;
153 : }
154 :
155 2148 : if (update_reltuples && reltuples != pgcform->reltuples)
156 : {
157 976 : replaces[nreplaces] = Anum_pg_class_reltuples;
158 976 : values[nreplaces] = Float4GetDatum(reltuples);
159 976 : nreplaces++;
160 : }
161 :
162 2148 : if (update_relallvisible && relallvisible != pgcform->relallvisible)
163 : {
164 250 : replaces[nreplaces] = Anum_pg_class_relallvisible;
165 250 : values[nreplaces] = UInt32GetDatum(relallvisible);
166 250 : nreplaces++;
167 : }
168 :
169 2148 : if (update_relallfrozen && relallfrozen != pgcform->relallfrozen)
170 : {
171 60 : replaces[nreplaces] = Anum_pg_class_relallfrozen;
172 60 : values[nreplaces] = UInt32GetDatum(relallfrozen);
173 60 : nreplaces++;
174 : }
175 :
176 2148 : if (nreplaces > 0)
177 : {
178 1264 : TupleDesc tupdesc = RelationGetDescr(crel);
179 : HeapTuple newtup;
180 :
181 1264 : newtup = heap_modify_tuple_by_cols(ctup, tupdesc, nreplaces,
182 : replaces, values, nulls);
183 1264 : CatalogTupleUpdate(crel, &newtup->t_self, newtup);
184 1264 : heap_freetuple(newtup);
185 : }
186 :
187 2148 : ReleaseSysCache(ctup);
188 :
189 : /* release the lock, consistent with vac_update_relstats() */
190 2148 : table_close(crel, RowExclusiveLock);
191 :
192 2148 : CommandCounterIncrement();
193 :
194 2148 : return result;
195 : }
196 :
197 : /*
198 : * Clear statistics for a given pg_class entry; that is, set back to initial
199 : * stats for a newly-created table.
200 : */
201 : Datum
202 24 : pg_clear_relation_stats(PG_FUNCTION_ARGS)
203 : {
204 24 : LOCAL_FCINFO(newfcinfo, 6);
205 :
206 24 : InitFunctionCallInfoData(*newfcinfo, NULL, 6, InvalidOid, NULL, NULL);
207 :
208 24 : newfcinfo->args[0].value = PG_GETARG_DATUM(0);
209 24 : newfcinfo->args[0].isnull = PG_ARGISNULL(0);
210 24 : newfcinfo->args[1].value = PG_GETARG_DATUM(1);
211 24 : newfcinfo->args[1].isnull = PG_ARGISNULL(1);
212 24 : newfcinfo->args[2].value = UInt32GetDatum(0);
213 24 : newfcinfo->args[2].isnull = false;
214 24 : newfcinfo->args[3].value = Float4GetDatum(-1.0);
215 24 : newfcinfo->args[3].isnull = false;
216 24 : newfcinfo->args[4].value = UInt32GetDatum(0);
217 24 : newfcinfo->args[4].isnull = false;
218 24 : newfcinfo->args[5].value = UInt32GetDatum(0);
219 24 : newfcinfo->args[5].isnull = false;
220 :
221 24 : relation_statistics_update(newfcinfo);
222 12 : PG_RETURN_VOID();
223 : }
224 :
225 : Datum
226 2184 : pg_restore_relation_stats(PG_FUNCTION_ARGS)
227 : {
228 2184 : LOCAL_FCINFO(positional_fcinfo, NUM_RELATION_STATS_ARGS);
229 2184 : bool result = true;
230 :
231 2184 : InitFunctionCallInfoData(*positional_fcinfo, NULL,
232 : NUM_RELATION_STATS_ARGS,
233 : InvalidOid, NULL, NULL);
234 :
235 2184 : if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
236 : relarginfo))
237 24 : result = false;
238 :
239 2172 : if (!relation_statistics_update(positional_fcinfo))
240 0 : result = false;
241 :
242 2136 : PG_RETURN_BOOL(result);
243 : }
|