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