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