/src/postgres/src/backend/statistics/relation_stats.c
Line | Count | Source (jump to first uncovered line) |
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 | | relation_statistics_update(FunctionCallInfo fcinfo) |
65 | 0 | { |
66 | 0 | bool result = true; |
67 | 0 | char *nspname; |
68 | 0 | char *relname; |
69 | 0 | Oid reloid; |
70 | 0 | Relation crel; |
71 | 0 | BlockNumber relpages = 0; |
72 | 0 | bool update_relpages = false; |
73 | 0 | float reltuples = 0; |
74 | 0 | bool update_reltuples = false; |
75 | 0 | BlockNumber relallvisible = 0; |
76 | 0 | bool update_relallvisible = false; |
77 | 0 | BlockNumber relallfrozen = 0; |
78 | 0 | bool update_relallfrozen = false; |
79 | 0 | HeapTuple ctup; |
80 | 0 | Form_pg_class pgcform; |
81 | 0 | int replaces[4] = {0}; |
82 | 0 | Datum values[4] = {0}; |
83 | 0 | bool nulls[4] = {0}; |
84 | 0 | int nreplaces = 0; |
85 | |
|
86 | 0 | stats_check_required_arg(fcinfo, relarginfo, RELSCHEMA_ARG); |
87 | 0 | stats_check_required_arg(fcinfo, relarginfo, RELNAME_ARG); |
88 | |
|
89 | 0 | nspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG)); |
90 | 0 | relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG)); |
91 | |
|
92 | 0 | reloid = stats_lookup_relid(nspname, relname); |
93 | |
|
94 | 0 | if (RecoveryInProgress()) |
95 | 0 | ereport(ERROR, |
96 | 0 | (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), |
97 | 0 | errmsg("recovery is in progress"), |
98 | 0 | errhint("Statistics cannot be modified during recovery."))); |
99 | | |
100 | 0 | stats_lock_check_privileges(reloid); |
101 | |
|
102 | 0 | if (!PG_ARGISNULL(RELPAGES_ARG)) |
103 | 0 | { |
104 | 0 | relpages = PG_GETARG_UINT32(RELPAGES_ARG); |
105 | 0 | update_relpages = true; |
106 | 0 | } |
107 | |
|
108 | 0 | if (!PG_ARGISNULL(RELTUPLES_ARG)) |
109 | 0 | { |
110 | 0 | reltuples = PG_GETARG_FLOAT4(RELTUPLES_ARG); |
111 | 0 | if (reltuples < -1.0) |
112 | 0 | { |
113 | 0 | ereport(WARNING, |
114 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
115 | 0 | errmsg("reltuples cannot be < -1.0"))); |
116 | 0 | result = false; |
117 | 0 | } |
118 | 0 | else |
119 | 0 | update_reltuples = true; |
120 | 0 | } |
121 | | |
122 | 0 | if (!PG_ARGISNULL(RELALLVISIBLE_ARG)) |
123 | 0 | { |
124 | 0 | relallvisible = PG_GETARG_UINT32(RELALLVISIBLE_ARG); |
125 | 0 | update_relallvisible = true; |
126 | 0 | } |
127 | |
|
128 | 0 | if (!PG_ARGISNULL(RELALLFROZEN_ARG)) |
129 | 0 | { |
130 | 0 | relallfrozen = PG_GETARG_UINT32(RELALLFROZEN_ARG); |
131 | 0 | update_relallfrozen = true; |
132 | 0 | } |
133 | | |
134 | | /* |
135 | | * Take RowExclusiveLock on pg_class, consistent with |
136 | | * vac_update_relstats(). |
137 | | */ |
138 | 0 | crel = table_open(RelationRelationId, RowExclusiveLock); |
139 | |
|
140 | 0 | ctup = SearchSysCache1(RELOID, ObjectIdGetDatum(reloid)); |
141 | 0 | if (!HeapTupleIsValid(ctup)) |
142 | 0 | elog(ERROR, "pg_class entry for relid %u not found", reloid); |
143 | | |
144 | 0 | pgcform = (Form_pg_class) GETSTRUCT(ctup); |
145 | |
|
146 | 0 | if (update_relpages && relpages != pgcform->relpages) |
147 | 0 | { |
148 | 0 | replaces[nreplaces] = Anum_pg_class_relpages; |
149 | 0 | values[nreplaces] = UInt32GetDatum(relpages); |
150 | 0 | nreplaces++; |
151 | 0 | } |
152 | |
|
153 | 0 | if (update_reltuples && reltuples != pgcform->reltuples) |
154 | 0 | { |
155 | 0 | replaces[nreplaces] = Anum_pg_class_reltuples; |
156 | 0 | values[nreplaces] = Float4GetDatum(reltuples); |
157 | 0 | nreplaces++; |
158 | 0 | } |
159 | |
|
160 | 0 | if (update_relallvisible && relallvisible != pgcform->relallvisible) |
161 | 0 | { |
162 | 0 | replaces[nreplaces] = Anum_pg_class_relallvisible; |
163 | 0 | values[nreplaces] = UInt32GetDatum(relallvisible); |
164 | 0 | nreplaces++; |
165 | 0 | } |
166 | |
|
167 | 0 | if (update_relallfrozen && relallfrozen != pgcform->relallfrozen) |
168 | 0 | { |
169 | 0 | replaces[nreplaces] = Anum_pg_class_relallfrozen; |
170 | 0 | values[nreplaces] = UInt32GetDatum(relallfrozen); |
171 | 0 | nreplaces++; |
172 | 0 | } |
173 | |
|
174 | 0 | if (nreplaces > 0) |
175 | 0 | { |
176 | 0 | TupleDesc tupdesc = RelationGetDescr(crel); |
177 | 0 | HeapTuple newtup; |
178 | |
|
179 | 0 | newtup = heap_modify_tuple_by_cols(ctup, tupdesc, nreplaces, |
180 | 0 | replaces, values, nulls); |
181 | 0 | CatalogTupleUpdate(crel, &newtup->t_self, newtup); |
182 | 0 | heap_freetuple(newtup); |
183 | 0 | } |
184 | |
|
185 | 0 | ReleaseSysCache(ctup); |
186 | | |
187 | | /* release the lock, consistent with vac_update_relstats() */ |
188 | 0 | table_close(crel, RowExclusiveLock); |
189 | |
|
190 | 0 | CommandCounterIncrement(); |
191 | |
|
192 | 0 | return result; |
193 | 0 | } |
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 | | pg_clear_relation_stats(PG_FUNCTION_ARGS) |
201 | 0 | { |
202 | 0 | LOCAL_FCINFO(newfcinfo, 6); |
203 | |
|
204 | 0 | InitFunctionCallInfoData(*newfcinfo, NULL, 6, InvalidOid, NULL, NULL); |
205 | |
|
206 | 0 | newfcinfo->args[0].value = PG_GETARG_DATUM(0); |
207 | 0 | newfcinfo->args[0].isnull = PG_ARGISNULL(0); |
208 | 0 | newfcinfo->args[1].value = PG_GETARG_DATUM(1); |
209 | 0 | newfcinfo->args[1].isnull = PG_ARGISNULL(1); |
210 | 0 | newfcinfo->args[2].value = UInt32GetDatum(0); |
211 | 0 | newfcinfo->args[2].isnull = false; |
212 | 0 | newfcinfo->args[3].value = Float4GetDatum(-1.0); |
213 | 0 | newfcinfo->args[3].isnull = false; |
214 | 0 | newfcinfo->args[4].value = UInt32GetDatum(0); |
215 | 0 | newfcinfo->args[4].isnull = false; |
216 | 0 | newfcinfo->args[5].value = UInt32GetDatum(0); |
217 | 0 | newfcinfo->args[5].isnull = false; |
218 | |
|
219 | 0 | relation_statistics_update(newfcinfo); |
220 | 0 | PG_RETURN_VOID(); |
221 | 0 | } |
222 | | |
223 | | Datum |
224 | | pg_restore_relation_stats(PG_FUNCTION_ARGS) |
225 | 0 | { |
226 | 0 | LOCAL_FCINFO(positional_fcinfo, NUM_RELATION_STATS_ARGS); |
227 | 0 | bool result = true; |
228 | |
|
229 | 0 | InitFunctionCallInfoData(*positional_fcinfo, NULL, |
230 | 0 | NUM_RELATION_STATS_ARGS, |
231 | 0 | InvalidOid, NULL, NULL); |
232 | |
|
233 | 0 | if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo, |
234 | 0 | relarginfo)) |
235 | 0 | result = false; |
236 | |
|
237 | 0 | if (!relation_statistics_update(positional_fcinfo)) |
238 | 0 | result = false; |
239 | |
|
240 | 0 | PG_RETURN_BOOL(result); |
241 | 0 | } |