Coverage Report

Created: 2025-08-12 06:43

/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
}