Coverage Report

Created: 2026-08-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/access/gin/ginvalidate.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * ginvalidate.c
4
 *    Opclass validator for GIN.
5
 *
6
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 * IDENTIFICATION
10
 *      src/backend/access/gin/ginvalidate.c
11
 *
12
 *-------------------------------------------------------------------------
13
 */
14
#include "postgres.h"
15
16
#include "access/amvalidate.h"
17
#include "access/gin_private.h"
18
#include "access/htup_details.h"
19
#include "catalog/pg_amop.h"
20
#include "catalog/pg_amproc.h"
21
#include "catalog/pg_opclass.h"
22
#include "catalog/pg_type.h"
23
#include "utils/lsyscache.h"
24
#include "utils/regproc.h"
25
#include "utils/syscache.h"
26
27
/*
28
 * Validator for a GIN opclass.
29
 */
30
bool
31
ginvalidate(Oid opclassoid)
32
{
33
  bool    result = true;
34
  HeapTuple classtup;
35
  Form_pg_opclass classform;
36
  Oid     opfamilyoid;
37
  Oid     opcintype;
38
  Oid     opckeytype;
39
  char     *opclassname;
40
  char     *opfamilyname;
41
  CatCList   *proclist,
42
         *oprlist;
43
  List     *grouplist;
44
  OpFamilyOpFuncGroup *opclassgroup;
45
  int     i;
46
  ListCell   *lc;
47
48
  /* Fetch opclass information */
49
  classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
50
  if (!HeapTupleIsValid(classtup))
51
    elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
52
  classform = (Form_pg_opclass) GETSTRUCT(classtup);
53
54
  opfamilyoid = classform->opcfamily;
55
  opcintype = classform->opcintype;
56
  opckeytype = classform->opckeytype;
57
  if (!OidIsValid(opckeytype))
58
    opckeytype = opcintype;
59
  opclassname = NameStr(classform->opcname);
60
61
  /* Fetch opfamily information */
62
  opfamilyname = get_opfamily_name(opfamilyoid, false);
63
64
  /* Fetch all operators and support functions of the opfamily */
65
  oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
66
  proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
67
68
  /* Check individual support functions */
69
  for (i = 0; i < proclist->n_members; i++)
70
  {
71
    HeapTuple proctup = &proclist->members[i]->tuple;
72
    Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
73
    bool    ok;
74
75
    /*
76
     * All GIN support functions should be registered with matching
77
     * left/right types
78
     */
79
    if (procform->amproclefttype != procform->amprocrighttype)
80
    {
81
      ereport(INFO,
82
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
83
           errmsg("operator family \"%s\" of access method %s contains support function %s with different left and right input types",
84
              opfamilyname, "gin",
85
              format_procedure(procform->amproc))));
86
      result = false;
87
    }
88
89
    /*
90
     * We can't check signatures except within the specific opclass, since
91
     * we need to know the associated opckeytype in many cases.
92
     */
93
    if (procform->amproclefttype != opcintype)
94
      continue;
95
96
    /* Check procedure numbers and function signatures */
97
    switch (procform->amprocnum)
98
    {
99
      case GIN_COMPARE_PROC:
100
        ok = check_amproc_signature(procform->amproc, INT4OID, false,
101
                      2, 2, opckeytype, opckeytype);
102
        break;
103
      case GIN_EXTRACTVALUE_PROC:
104
        /* Some opclasses omit nullFlags */
105
        ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
106
                      2, 3, opcintype, INTERNALOID,
107
                      INTERNALOID);
108
        break;
109
      case GIN_EXTRACTQUERY_PROC:
110
        /* Some opclasses omit nullFlags and searchMode */
111
        ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
112
                      5, 7, opcintype, INTERNALOID,
113
                      INT2OID, INTERNALOID, INTERNALOID,
114
                      INTERNALOID, INTERNALOID);
115
        break;
116
      case GIN_CONSISTENT_PROC:
117
        /* Some opclasses omit queryKeys and nullFlags */
118
        ok = check_amproc_signature(procform->amproc, BOOLOID, false,
119
                      6, 8, INTERNALOID, INT2OID,
120
                      opcintype, INT4OID,
121
                      INTERNALOID, INTERNALOID,
122
                      INTERNALOID, INTERNALOID);
123
        break;
124
      case GIN_COMPARE_PARTIAL_PROC:
125
        ok = check_amproc_signature(procform->amproc, INT4OID, false,
126
                      4, 4, opckeytype, opckeytype,
127
                      INT2OID, INTERNALOID);
128
        break;
129
      case GIN_TRICONSISTENT_PROC:
130
        ok = check_amproc_signature(procform->amproc, CHAROID, false,
131
                      7, 7, INTERNALOID, INT2OID,
132
                      opcintype, INT4OID,
133
                      INTERNALOID, INTERNALOID,
134
                      INTERNALOID);
135
        break;
136
      case GIN_OPTIONS_PROC:
137
        ok = check_amoptsproc_signature(procform->amproc);
138
        break;
139
      default:
140
        ereport(INFO,
141
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
142
             errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
143
                opfamilyname, "gin",
144
                format_procedure(procform->amproc),
145
                procform->amprocnum)));
146
        result = false;
147
        continue;   /* don't want additional message */
148
    }
149
150
    if (!ok)
151
    {
152
      ereport(INFO,
153
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
154
           errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
155
              opfamilyname, "gin",
156
              format_procedure(procform->amproc),
157
              procform->amprocnum)));
158
      result = false;
159
    }
160
  }
161
162
  /* Check individual operators */
163
  for (i = 0; i < oprlist->n_members; i++)
164
  {
165
    HeapTuple oprtup = &oprlist->members[i]->tuple;
166
    Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
167
168
    /* TODO: Check that only allowed strategy numbers exist */
169
    if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
170
    {
171
      ereport(INFO,
172
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
173
           errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
174
              opfamilyname, "gin",
175
              format_operator(oprform->amopopr),
176
              oprform->amopstrategy)));
177
      result = false;
178
    }
179
180
    /* gin doesn't support ORDER BY operators */
181
    if (oprform->amoppurpose != AMOP_SEARCH ||
182
      OidIsValid(oprform->amopsortfamily))
183
    {
184
      ereport(INFO,
185
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
186
           errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
187
              opfamilyname, "gin",
188
              format_operator(oprform->amopopr))));
189
      result = false;
190
    }
191
192
    /* Check operator signature --- same for all gin strategies */
193
    if (!check_amop_signature(oprform->amopopr, BOOLOID,
194
                  oprform->amoplefttype,
195
                  oprform->amoprighttype))
196
    {
197
      ereport(INFO,
198
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
199
           errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
200
              opfamilyname, "gin",
201
              format_operator(oprform->amopopr))));
202
      result = false;
203
    }
204
  }
205
206
  /* Now check for inconsistent groups of operators/functions */
207
  grouplist = identify_opfamily_groups(oprlist, proclist);
208
  opclassgroup = NULL;
209
  foreach(lc, grouplist)
210
  {
211
    OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
212
213
    /* Remember the group exactly matching the test opclass */
214
    if (thisgroup->lefttype == opcintype &&
215
      thisgroup->righttype == opcintype)
216
      opclassgroup = thisgroup;
217
218
    /*
219
     * There is not a lot we can do to check the operator sets, since each
220
     * GIN opclass is more or less a law unto itself, and some contain
221
     * only operators that are binary-compatible with the opclass datatype
222
     * (meaning that empty operator sets can be OK).  That case also means
223
     * that we shouldn't insist on nonempty function sets except for the
224
     * opclass's own group.
225
     */
226
  }
227
228
  /* Check that the originally-named opclass is complete */
229
  for (i = 1; i <= GINNProcs; i++)
230
  {
231
    if (opclassgroup &&
232
      (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
233
      continue;     /* got it */
234
    if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
235
      i == GIN_OPTIONS_PROC)
236
      continue;     /* optional method */
237
    if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
238
      continue;     /* don't need both, see check below loop */
239
    ereport(INFO,
240
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
241
         errmsg("operator class \"%s\" of access method %s is missing support function %d",
242
            opclassname, "gin", i)));
243
    result = false;
244
  }
245
  if (!opclassgroup ||
246
    ((opclassgroup->functionset & (1 << GIN_CONSISTENT_PROC)) == 0 &&
247
     (opclassgroup->functionset & (1 << GIN_TRICONSISTENT_PROC)) == 0))
248
  {
249
    ereport(INFO,
250
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
251
         errmsg("operator class \"%s\" of access method %s is missing support function %d or %d",
252
            opclassname, "gin",
253
            GIN_CONSISTENT_PROC, GIN_TRICONSISTENT_PROC)));
254
    result = false;
255
  }
256
257
258
  ReleaseCatCacheList(proclist);
259
  ReleaseCatCacheList(oprlist);
260
  ReleaseSysCache(classtup);
261
262
  return result;
263
}
264
265
/*
266
 * Prechecking function for adding operators/functions to a GIN opfamily.
267
 */
268
void
269
ginadjustmembers(Oid opfamilyoid,
270
         Oid opclassoid,
271
         List *operators,
272
         List *functions)
273
0
{
274
0
  ListCell   *lc;
275
276
  /*
277
   * Operator members of a GIN opfamily should never have hard dependencies,
278
   * since their connection to the opfamily depends only on what the support
279
   * functions think, and that can be altered.  For consistency, we make all
280
   * soft dependencies point to the opfamily, though a soft dependency on
281
   * the opclass would work as well in the CREATE OPERATOR CLASS case.
282
   */
283
0
  foreach(lc, operators)
284
0
  {
285
0
    OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
286
287
0
    op->ref_is_hard = false;
288
0
    op->ref_is_family = true;
289
0
    op->refobjid = opfamilyoid;
290
0
  }
291
292
  /*
293
   * Required support functions should have hard dependencies.  Preferably
294
   * those are just dependencies on the opclass, but if we're in ALTER
295
   * OPERATOR FAMILY, we leave the dependency pointing at the whole
296
   * opfamily.  (Given that GIN opclasses generally don't share opfamilies,
297
   * it seems unlikely to be worth working harder.)
298
   */
299
0
  foreach(lc, functions)
300
0
  {
301
0
    OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
302
303
0
    switch (op->number)
304
0
    {
305
0
      case GIN_EXTRACTVALUE_PROC:
306
0
      case GIN_EXTRACTQUERY_PROC:
307
        /* Required support function */
308
0
        op->ref_is_hard = true;
309
0
        break;
310
0
      case GIN_COMPARE_PROC:
311
0
      case GIN_CONSISTENT_PROC:
312
0
      case GIN_COMPARE_PARTIAL_PROC:
313
0
      case GIN_TRICONSISTENT_PROC:
314
0
      case GIN_OPTIONS_PROC:
315
        /* Optional, so force it to be a soft family dependency */
316
0
        op->ref_is_hard = false;
317
0
        op->ref_is_family = true;
318
0
        op->refobjid = opfamilyoid;
319
0
        break;
320
0
      default:
321
0
        ereport(ERROR,
322
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
323
0
             errmsg("support function number %d is invalid for access method %s",
324
0
                op->number, "gin")));
325
0
        break;
326
0
    }
327
0
  }
328
0
}