Coverage Report

Created: 2025-06-13 06:06

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