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-2024, 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 "statistics/stat_utils.h"
23 : #include "utils/fmgrprotos.h"
24 : #include "utils/syscache.h"
25 :
26 : #define DEFAULT_RELPAGES Int32GetDatum(0)
27 : #define DEFAULT_RELTUPLES Float4GetDatum(-1.0)
28 : #define DEFAULT_RELALLVISIBLE Int32GetDatum(0)
29 :
30 : /*
31 : * Positional argument numbers, names, and types for
32 : * relation_statistics_update().
33 : */
34 :
35 : enum relation_stats_argnum
36 : {
37 : RELATION_ARG = 0,
38 : RELPAGES_ARG,
39 : RELTUPLES_ARG,
40 : RELALLVISIBLE_ARG,
41 : NUM_RELATION_STATS_ARGS
42 : };
43 :
44 : static struct StatsArgInfo relarginfo[] =
45 : {
46 : [RELATION_ARG] = {"relation", REGCLASSOID},
47 : [RELPAGES_ARG] = {"relpages", INT4OID},
48 : [RELTUPLES_ARG] = {"reltuples", FLOAT4OID},
49 : [RELALLVISIBLE_ARG] = {"relallvisible", INT4OID},
50 : [NUM_RELATION_STATS_ARGS] = {0}
51 : };
52 :
53 : static bool relation_statistics_update(FunctionCallInfo fcinfo, int elevel);
54 :
55 : /*
56 : * Internal function for modifying statistics for a relation.
57 : */
58 : static bool
59 108 : relation_statistics_update(FunctionCallInfo fcinfo, int elevel)
60 : {
61 : Oid reloid;
62 : Relation crel;
63 : HeapTuple ctup;
64 : Form_pg_class pgcform;
65 108 : int replaces[3] = {0};
66 108 : Datum values[3] = {0};
67 108 : bool nulls[3] = {0};
68 108 : int ncols = 0;
69 : TupleDesc tupdesc;
70 108 : bool result = true;
71 :
72 108 : stats_check_required_arg(fcinfo, relarginfo, RELATION_ARG);
73 108 : reloid = PG_GETARG_OID(RELATION_ARG);
74 :
75 108 : if (RecoveryInProgress())
76 0 : ereport(ERROR,
77 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
78 : errmsg("recovery is in progress"),
79 : errhint("Statistics cannot be modified during recovery.")));
80 :
81 108 : stats_lock_check_privileges(reloid);
82 :
83 : /*
84 : * Take RowExclusiveLock on pg_class, consistent with
85 : * vac_update_relstats().
86 : */
87 78 : crel = table_open(RelationRelationId, RowExclusiveLock);
88 :
89 78 : tupdesc = RelationGetDescr(crel);
90 78 : ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
91 78 : if (!HeapTupleIsValid(ctup))
92 : {
93 0 : ereport(elevel,
94 : (errcode(ERRCODE_OBJECT_IN_USE),
95 : errmsg("pg_class entry for relid %u not found", reloid)));
96 0 : table_close(crel, RowExclusiveLock);
97 0 : return false;
98 : }
99 :
100 78 : pgcform = (Form_pg_class) GETSTRUCT(ctup);
101 :
102 : /* relpages */
103 78 : if (!PG_ARGISNULL(RELPAGES_ARG))
104 : {
105 54 : int32 relpages = PG_GETARG_INT32(RELPAGES_ARG);
106 :
107 : /*
108 : * Partitioned tables may have relpages=-1. Note: for relations with
109 : * no storage, relpages=-1 is not used consistently, but must be
110 : * supported here.
111 : */
112 54 : if (relpages < -1)
113 : {
114 0 : ereport(elevel,
115 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
116 : errmsg("relpages cannot be < -1")));
117 0 : result = false;
118 : }
119 54 : else if (relpages != pgcform->relpages)
120 : {
121 42 : replaces[ncols] = Anum_pg_class_relpages;
122 42 : values[ncols] = Int32GetDatum(relpages);
123 42 : ncols++;
124 : }
125 : }
126 :
127 78 : if (!PG_ARGISNULL(RELTUPLES_ARG))
128 : {
129 48 : float reltuples = PG_GETARG_FLOAT4(RELTUPLES_ARG);
130 :
131 48 : if (reltuples < -1.0)
132 : {
133 0 : ereport(elevel,
134 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
135 : errmsg("reltuples cannot be < -1.0")));
136 0 : result = false;
137 : }
138 48 : else if (reltuples != pgcform->reltuples)
139 : {
140 36 : replaces[ncols] = Anum_pg_class_reltuples;
141 36 : values[ncols] = Float4GetDatum(reltuples);
142 36 : ncols++;
143 : }
144 :
145 : }
146 :
147 78 : if (!PG_ARGISNULL(RELALLVISIBLE_ARG))
148 : {
149 48 : int32 relallvisible = PG_GETARG_INT32(RELALLVISIBLE_ARG);
150 :
151 48 : if (relallvisible < 0)
152 : {
153 0 : ereport(elevel,
154 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
155 : errmsg("relallvisible cannot be < 0")));
156 0 : result = false;
157 : }
158 48 : else if (relallvisible != pgcform->relallvisible)
159 : {
160 36 : replaces[ncols] = Anum_pg_class_relallvisible;
161 36 : values[ncols] = Int32GetDatum(relallvisible);
162 36 : ncols++;
163 : }
164 : }
165 :
166 : /* only update pg_class if there is a meaningful change */
167 78 : if (ncols > 0)
168 : {
169 : HeapTuple newtup;
170 :
171 66 : newtup = heap_modify_tuple_by_cols(ctup, tupdesc, ncols, replaces, values,
172 : nulls);
173 66 : CatalogTupleUpdate(crel, &newtup->t_self, newtup);
174 66 : heap_freetuple(newtup);
175 : }
176 :
177 : /* release the lock, consistent with vac_update_relstats() */
178 78 : table_close(crel, RowExclusiveLock);
179 :
180 78 : CommandCounterIncrement();
181 :
182 78 : return result;
183 : }
184 :
185 : /*
186 : * Set statistics for a given pg_class entry.
187 : */
188 : Datum
189 48 : pg_set_relation_stats(PG_FUNCTION_ARGS)
190 : {
191 48 : relation_statistics_update(fcinfo, ERROR);
192 42 : PG_RETURN_VOID();
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 18 : pg_clear_relation_stats(PG_FUNCTION_ARGS)
201 : {
202 18 : LOCAL_FCINFO(newfcinfo, 4);
203 :
204 18 : InitFunctionCallInfoData(*newfcinfo, NULL, 4, InvalidOid, NULL, NULL);
205 :
206 18 : newfcinfo->args[0].value = PG_GETARG_OID(0);
207 18 : newfcinfo->args[0].isnull = PG_ARGISNULL(0);
208 18 : newfcinfo->args[1].value = DEFAULT_RELPAGES;
209 18 : newfcinfo->args[1].isnull = false;
210 18 : newfcinfo->args[2].value = DEFAULT_RELTUPLES;
211 18 : newfcinfo->args[2].isnull = false;
212 18 : newfcinfo->args[3].value = DEFAULT_RELALLVISIBLE;
213 18 : newfcinfo->args[3].isnull = false;
214 :
215 18 : relation_statistics_update(newfcinfo, ERROR);
216 6 : PG_RETURN_VOID();
217 : }
218 :
219 : Datum
220 60 : pg_restore_relation_stats(PG_FUNCTION_ARGS)
221 : {
222 60 : LOCAL_FCINFO(positional_fcinfo, NUM_RELATION_STATS_ARGS);
223 60 : bool result = true;
224 :
225 60 : InitFunctionCallInfoData(*positional_fcinfo, NULL,
226 : NUM_RELATION_STATS_ARGS,
227 : InvalidOid, NULL, NULL);
228 :
229 60 : if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
230 : relarginfo, WARNING))
231 12 : result = false;
232 :
233 42 : if (!relation_statistics_update(positional_fcinfo, WARNING))
234 0 : result = false;
235 :
236 30 : PG_RETURN_BOOL(result);
237 : }
|