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