YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/postgres/src/backend/access/hash/hashvalidate.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * hashvalidate.c
4
 *    Opclass validator for hash.
5
 *
6
 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 * IDENTIFICATION
10
 *    src/backend/access/hash/hashvalidate.c
11
 *
12
 *-------------------------------------------------------------------------
13
 */
14
#include "postgres.h"
15
16
#include "access/amvalidate.h"
17
#include "access/hash.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_opfamily.h"
23
#include "catalog/pg_proc.h"
24
#include "catalog/pg_type.h"
25
#include "parser/parse_coerce.h"
26
#include "utils/builtins.h"
27
#include "utils/fmgroids.h"
28
#include "utils/regproc.h"
29
#include "utils/syscache.h"
30
31
32
static bool check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype);
33
34
35
/*
36
 * Validator for a hash opclass.
37
 *
38
 * Some of the checks done here cover the whole opfamily, and therefore are
39
 * redundant when checking each opclass in a family.  But they don't run long
40
 * enough to be much of a problem, so we accept the duplication rather than
41
 * complicate the amvalidate API.
42
 */
43
bool
44
hashvalidate(Oid opclassoid)
45
0
{
46
0
  bool    result = true;
47
0
  HeapTuple classtup;
48
0
  Form_pg_opclass classform;
49
0
  Oid     opfamilyoid;
50
0
  Oid     opcintype;
51
0
  char     *opclassname;
52
0
  HeapTuple familytup;
53
0
  Form_pg_opfamily familyform;
54
0
  char     *opfamilyname;
55
0
  CatCList   *proclist,
56
0
         *oprlist;
57
0
  List     *grouplist;
58
0
  OpFamilyOpFuncGroup *opclassgroup;
59
0
  List     *hashabletypes = NIL;
60
0
  int     i;
61
0
  ListCell   *lc;
62
63
  /* Fetch opclass information */
64
0
  classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
65
0
  if (!HeapTupleIsValid(classtup))
66
0
    elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
67
0
  classform = (Form_pg_opclass) GETSTRUCT(classtup);
68
69
0
  opfamilyoid = classform->opcfamily;
70
0
  opcintype = classform->opcintype;
71
0
  opclassname = NameStr(classform->opcname);
72
73
  /* Fetch opfamily information */
74
0
  familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
75
0
  if (!HeapTupleIsValid(familytup))
76
0
    elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
77
0
  familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
78
79
0
  opfamilyname = NameStr(familyform->opfname);
80
81
  /* Fetch all operators and support functions of the opfamily */
82
0
  oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
83
0
  proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
84
85
  /* Check individual support functions */
86
0
  for (i = 0; i < proclist->n_members; i++)
87
0
  {
88
0
    HeapTuple proctup = &proclist->members[i]->tuple;
89
0
    Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
90
91
    /*
92
     * All hash functions should be registered with matching left/right
93
     * types
94
     */
95
0
    if (procform->amproclefttype != procform->amprocrighttype)
96
0
    {
97
0
      ereport(INFO,
98
0
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
99
0
           errmsg("operator family \"%s\" of access method %s contains support function %s with different left and right input types",
100
0
              opfamilyname, "hash",
101
0
              format_procedure(procform->amproc))));
102
0
      result = false;
103
0
    }
104
105
    /* Check procedure numbers and function signatures */
106
0
    switch (procform->amprocnum)
107
0
    {
108
0
      case HASHSTANDARD_PROC:
109
0
      case HASHEXTENDED_PROC:
110
0
        if (!check_hash_func_signature(procform->amproc, procform->amprocnum,
111
0
                         procform->amproclefttype))
112
0
        {
113
0
          ereport(INFO,
114
0
              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
115
0
               errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
116
0
                  opfamilyname, "hash",
117
0
                  format_procedure(procform->amproc),
118
0
                  procform->amprocnum)));
119
0
          result = false;
120
0
        }
121
0
        else
122
0
        {
123
          /* Remember which types we can hash */
124
0
          hashabletypes =
125
0
            list_append_unique_oid(hashabletypes,
126
0
                         procform->amproclefttype);
127
0
        }
128
0
        break;
129
0
      default:
130
0
        ereport(INFO,
131
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
132
0
             errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
133
0
                opfamilyname, "hash",
134
0
                format_procedure(procform->amproc),
135
0
                procform->amprocnum)));
136
0
        result = false;
137
0
        break;
138
0
    }
139
0
  }
140
141
  /* Check individual operators */
142
0
  for (i = 0; i < oprlist->n_members; i++)
143
0
  {
144
0
    HeapTuple oprtup = &oprlist->members[i]->tuple;
145
0
    Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
146
147
    /* Check that only allowed strategy numbers exist */
148
0
    if (oprform->amopstrategy < 1 ||
149
0
      oprform->amopstrategy > HTMaxStrategyNumber)
150
0
    {
151
0
      ereport(INFO,
152
0
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
153
0
           errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
154
0
              opfamilyname, "hash",
155
0
              format_operator(oprform->amopopr),
156
0
              oprform->amopstrategy)));
157
0
      result = false;
158
0
    }
159
160
    /* hash doesn't support ORDER BY operators */
161
0
    if (oprform->amoppurpose != AMOP_SEARCH ||
162
0
      OidIsValid(oprform->amopsortfamily))
163
0
    {
164
0
      ereport(INFO,
165
0
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
166
0
           errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
167
0
              opfamilyname, "hash",
168
0
              format_operator(oprform->amopopr))));
169
0
      result = false;
170
0
    }
171
172
    /* Check operator signature --- same for all hash strategies */
173
0
    if (!check_amop_signature(oprform->amopopr, BOOLOID,
174
0
                  oprform->amoplefttype,
175
0
                  oprform->amoprighttype))
176
0
    {
177
0
      ereport(INFO,
178
0
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
179
0
           errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
180
0
              opfamilyname, "hash",
181
0
              format_operator(oprform->amopopr))));
182
0
      result = false;
183
0
    }
184
185
    /* There should be relevant hash functions for each datatype */
186
0
    if (!list_member_oid(hashabletypes, oprform->amoplefttype) ||
187
0
      !list_member_oid(hashabletypes, oprform->amoprighttype))
188
0
    {
189
0
      ereport(INFO,
190
0
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
191
0
           errmsg("operator family \"%s\" of access method %s lacks support function for operator %s",
192
0
              opfamilyname, "hash",
193
0
              format_operator(oprform->amopopr))));
194
0
      result = false;
195
0
    }
196
0
  }
197
198
  /* Now check for inconsistent groups of operators/functions */
199
0
  grouplist = identify_opfamily_groups(oprlist, proclist);
200
0
  opclassgroup = NULL;
201
0
  foreach(lc, grouplist)
202
0
  {
203
0
    OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
204
205
    /* Remember the group exactly matching the test opclass */
206
0
    if (thisgroup->lefttype == opcintype &&
207
0
      thisgroup->righttype == opcintype)
208
0
      opclassgroup = thisgroup;
209
210
    /*
211
     * Complain if there seems to be an incomplete set of operators for
212
     * this datatype pair (implying that we have a hash function but no
213
     * operator).
214
     */
215
0
    if (thisgroup->operatorset != (1 << HTEqualStrategyNumber))
216
0
    {
217
0
      ereport(INFO,
218
0
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
219
0
           errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
220
0
              opfamilyname, "hash",
221
0
              format_type_be(thisgroup->lefttype),
222
0
              format_type_be(thisgroup->righttype))));
223
0
      result = false;
224
0
    }
225
0
  }
226
227
  /* Check that the originally-named opclass is supported */
228
  /* (if group is there, we already checked it adequately above) */
229
0
  if (!opclassgroup)
230
0
  {
231
0
    ereport(INFO,
232
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
233
0
         errmsg("operator class \"%s\" of access method %s is missing operator(s)",
234
0
            opclassname, "hash")));
235
0
    result = false;
236
0
  }
237
238
  /*
239
   * Complain if the opfamily doesn't have entries for all possible
240
   * combinations of its supported datatypes.  While missing cross-type
241
   * operators are not fatal, it seems reasonable to insist that all
242
   * built-in hash opfamilies be complete.
243
   */
244
0
  if (list_length(grouplist) !=
245
0
    list_length(hashabletypes) * list_length(hashabletypes))
246
0
  {
247
0
    ereport(INFO,
248
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
249
0
         errmsg("operator family \"%s\" of access method %s is missing cross-type operator(s)",
250
0
            opfamilyname, "hash")));
251
0
    result = false;
252
0
  }
253
254
0
  ReleaseCatCacheList(proclist);
255
0
  ReleaseCatCacheList(oprlist);
256
0
  ReleaseSysCache(familytup);
257
0
  ReleaseSysCache(classtup);
258
259
0
  return result;
260
0
}
261
262
263
/*
264
 * We need a custom version of check_amproc_signature because of assorted
265
 * hacks in the core hash opclass definitions.
266
 */
267
static bool
268
check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype)
269
0
{
270
0
  bool    result = true;
271
0
  Oid     restype;
272
0
  int16   nargs;
273
0
  HeapTuple tp;
274
0
  Form_pg_proc procform;
275
276
0
  switch (amprocnum)
277
0
  {
278
0
    case HASHSTANDARD_PROC:
279
0
      restype = INT4OID;
280
0
      nargs = 1;
281
0
      break;
282
283
0
    case HASHEXTENDED_PROC:
284
0
      restype = INT8OID;
285
0
      nargs = 2;
286
0
      break;
287
288
0
    default:
289
0
      elog(ERROR, "invalid amprocnum");
290
0
  }
291
292
0
  tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
293
0
  if (!HeapTupleIsValid(tp))
294
0
    elog(ERROR, "cache lookup failed for function %u", funcid);
295
0
  procform = (Form_pg_proc) GETSTRUCT(tp);
296
297
0
  if (procform->prorettype != restype || procform->proretset ||
298
0
    procform->pronargs != nargs)
299
0
    result = false;
300
301
0
  if (!IsBinaryCoercible(argtype, procform->proargtypes.values[0]))
302
0
  {
303
    /*
304
     * Some of the built-in hash opclasses cheat by using hash functions
305
     * that are different from but physically compatible with the opclass
306
     * datatype.  In some of these cases, even a "binary coercible" check
307
     * fails because there's no relevant cast.  For the moment, fix it by
308
     * having a whitelist of allowed cases.  Test the specific function
309
     * identity, not just its input type, because hashvarlena() takes
310
     * INTERNAL and allowing any such function seems too scary.
311
     */
312
0
    if ((funcid == F_HASHINT4 || funcid == F_HASHINT4EXTENDED) &&
313
0
      (argtype == DATEOID ||
314
0
       argtype == ABSTIMEOID || argtype == RELTIMEOID ||
315
0
       argtype == XIDOID || argtype == CIDOID))
316
0
       /* okay, allowed use of hashint4() */ ;
317
0
    else if ((funcid == F_TIMESTAMP_HASH ||
318
0
          funcid == F_TIMESTAMP_HASH_EXTENDED) &&
319
0
         argtype == TIMESTAMPTZOID)
320
0
       /* okay, allowed use of timestamp_hash() */ ;
321
0
    else if ((funcid == F_HASHCHAR || funcid == F_HASHCHAREXTENDED) &&
322
0
         argtype == BOOLOID)
323
0
       /* okay, allowed use of hashchar() */ ;
324
0
    else if ((funcid == F_HASHVARLENA || funcid == F_HASHVARLENAEXTENDED) &&
325
0
         argtype == BYTEAOID)
326
0
       /* okay, allowed use of hashvarlena() */ ;
327
0
    else
328
0
      result = false;
329
0
  }
330
331
  /* If function takes a second argument, it must be for a 64-bit salt. */
332
0
  if (nargs == 2 && procform->proargtypes.values[1] != INT8OID)
333
0
    result = false;
334
335
0
  ReleaseSysCache(tp);
336
0
  return result;
337
0
}