Coverage Report

Created: 2026-08-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/commands/opclasscmds.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * opclasscmds.c
4
 *
5
 *    Routines for opclass (and opfamily) manipulation commands
6
 *
7
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8
 * Portions Copyright (c) 1994, Regents of the University of California
9
 *
10
 *
11
 * IDENTIFICATION
12
 *    src/backend/commands/opclasscmds.c
13
 *
14
 *-------------------------------------------------------------------------
15
 */
16
#include "postgres.h"
17
18
#include <limits.h>
19
20
#include "access/genam.h"
21
#include "access/hash.h"
22
#include "access/htup_details.h"
23
#include "access/nbtree.h"
24
#include "access/table.h"
25
#include "catalog/catalog.h"
26
#include "catalog/dependency.h"
27
#include "catalog/indexing.h"
28
#include "catalog/objectaccess.h"
29
#include "catalog/pg_am.h"
30
#include "catalog/pg_amop.h"
31
#include "catalog/pg_amproc.h"
32
#include "catalog/pg_namespace.h"
33
#include "catalog/pg_opclass.h"
34
#include "catalog/pg_operator.h"
35
#include "catalog/pg_opfamily.h"
36
#include "catalog/pg_proc.h"
37
#include "catalog/pg_type.h"
38
#include "commands/defrem.h"
39
#include "commands/event_trigger.h"
40
#include "miscadmin.h"
41
#include "parser/parse_func.h"
42
#include "parser/parse_oper.h"
43
#include "parser/parse_type.h"
44
#include "utils/acl.h"
45
#include "utils/builtins.h"
46
#include "utils/fmgroids.h"
47
#include "utils/lsyscache.h"
48
#include "utils/rel.h"
49
#include "utils/syscache.h"
50
51
static void AlterOpFamilyAdd(AlterOpFamilyStmt *stmt,
52
               Oid amoid, Oid opfamilyoid,
53
               int maxOpNumber, int maxProcNumber,
54
               int optsProcNumber, List *items);
55
static void AlterOpFamilyDrop(AlterOpFamilyStmt *stmt,
56
                Oid amoid, Oid opfamilyoid,
57
                int maxOpNumber, int maxProcNumber,
58
                List *items);
59
static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
60
static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
61
static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid,
62
              int opclassOptsProcNum);
63
static void addFamilyMember(List **list, OpFamilyMember *member);
64
static void storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
65
               List *operators, bool isAdd);
66
static void storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
67
              List *procedures, bool isAdd);
68
static bool typeDepNeeded(Oid typid, OpFamilyMember *member);
69
static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
70
              List *operators);
71
static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
72
               List *procedures);
73
74
/*
75
 * OpFamilyCacheLookup
76
 *    Look up an existing opfamily by name.
77
 *
78
 * Returns a syscache tuple reference, or NULL if not found.
79
 */
80
static HeapTuple
81
OpFamilyCacheLookup(Oid amID, List *opfamilyname, bool missing_ok)
82
0
{
83
0
  char     *schemaname;
84
0
  char     *opfname;
85
0
  HeapTuple htup;
86
87
  /* deconstruct the name list */
88
0
  DeconstructQualifiedName(opfamilyname, &schemaname, &opfname);
89
90
0
  if (schemaname)
91
0
  {
92
    /* Look in specific schema only */
93
0
    Oid     namespaceId;
94
95
0
    namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
96
0
    if (!OidIsValid(namespaceId))
97
0
      htup = NULL;
98
0
    else
99
0
      htup = SearchSysCache3(OPFAMILYAMNAMENSP,
100
0
                   ObjectIdGetDatum(amID),
101
0
                   PointerGetDatum(opfname),
102
0
                   ObjectIdGetDatum(namespaceId));
103
0
  }
104
0
  else
105
0
  {
106
    /* Unqualified opfamily name, so search the search path */
107
0
    Oid     opfID = OpfamilynameGetOpfid(amID, opfname);
108
109
0
    if (!OidIsValid(opfID))
110
0
      htup = NULL;
111
0
    else
112
0
      htup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfID));
113
0
  }
114
115
0
  if (!HeapTupleIsValid(htup) && !missing_ok)
116
0
  {
117
0
    HeapTuple amtup;
118
119
0
    amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
120
0
    if (!HeapTupleIsValid(amtup))
121
0
      elog(ERROR, "cache lookup failed for access method %u", amID);
122
0
    ereport(ERROR,
123
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
124
0
         errmsg("operator family \"%s\" does not exist for access method \"%s\"",
125
0
            NameListToString(opfamilyname),
126
0
            NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
127
0
  }
128
129
0
  return htup;
130
0
}
131
132
/*
133
 * get_opfamily_oid
134
 *    find an opfamily OID by possibly qualified name
135
 *
136
 * If not found, returns InvalidOid if missing_ok, else throws error.
137
 */
138
Oid
139
get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok)
140
0
{
141
0
  HeapTuple htup;
142
0
  Form_pg_opfamily opfamform;
143
0
  Oid     opfID;
144
145
0
  htup = OpFamilyCacheLookup(amID, opfamilyname, missing_ok);
146
0
  if (!HeapTupleIsValid(htup))
147
0
    return InvalidOid;
148
0
  opfamform = (Form_pg_opfamily) GETSTRUCT(htup);
149
0
  opfID = opfamform->oid;
150
0
  ReleaseSysCache(htup);
151
152
0
  return opfID;
153
0
}
154
155
/*
156
 * OpClassCacheLookup
157
 *    Look up an existing opclass by name.
158
 *
159
 * Returns a syscache tuple reference, or NULL if not found.
160
 */
161
static HeapTuple
162
OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok)
163
0
{
164
0
  char     *schemaname;
165
0
  char     *opcname;
166
0
  HeapTuple htup;
167
168
  /* deconstruct the name list */
169
0
  DeconstructQualifiedName(opclassname, &schemaname, &opcname);
170
171
0
  if (schemaname)
172
0
  {
173
    /* Look in specific schema only */
174
0
    Oid     namespaceId;
175
176
0
    namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
177
0
    if (!OidIsValid(namespaceId))
178
0
      htup = NULL;
179
0
    else
180
0
      htup = SearchSysCache3(CLAAMNAMENSP,
181
0
                   ObjectIdGetDatum(amID),
182
0
                   PointerGetDatum(opcname),
183
0
                   ObjectIdGetDatum(namespaceId));
184
0
  }
185
0
  else
186
0
  {
187
    /* Unqualified opclass name, so search the search path */
188
0
    Oid     opcID = OpclassnameGetOpcid(amID, opcname);
189
190
0
    if (!OidIsValid(opcID))
191
0
      htup = NULL;
192
0
    else
193
0
      htup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcID));
194
0
  }
195
196
0
  if (!HeapTupleIsValid(htup) && !missing_ok)
197
0
  {
198
0
    HeapTuple amtup;
199
200
0
    amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
201
0
    if (!HeapTupleIsValid(amtup))
202
0
      elog(ERROR, "cache lookup failed for access method %u", amID);
203
0
    ereport(ERROR,
204
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
205
0
         errmsg("operator class \"%s\" does not exist for access method \"%s\"",
206
0
            NameListToString(opclassname),
207
0
            NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
208
0
  }
209
210
0
  return htup;
211
0
}
212
213
/*
214
 * get_opclass_oid
215
 *    find an opclass OID by possibly qualified name
216
 *
217
 * If not found, returns InvalidOid if missing_ok, else throws error.
218
 */
219
Oid
220
get_opclass_oid(Oid amID, List *opclassname, bool missing_ok)
221
0
{
222
0
  HeapTuple htup;
223
0
  Form_pg_opclass opcform;
224
0
  Oid     opcID;
225
226
0
  htup = OpClassCacheLookup(amID, opclassname, missing_ok);
227
0
  if (!HeapTupleIsValid(htup))
228
0
    return InvalidOid;
229
0
  opcform = (Form_pg_opclass) GETSTRUCT(htup);
230
0
  opcID = opcform->oid;
231
0
  ReleaseSysCache(htup);
232
233
0
  return opcID;
234
0
}
235
236
/*
237
 * CreateOpFamily
238
 *    Internal routine to make the catalog entry for a new operator family.
239
 *
240
 * Caller must have done permissions checks etc. already.
241
 */
242
static ObjectAddress
243
CreateOpFamily(CreateOpFamilyStmt *stmt, const char *opfname,
244
         Oid namespaceoid, Oid amoid)
245
0
{
246
0
  Oid     opfamilyoid;
247
0
  Relation  rel;
248
0
  HeapTuple tup;
249
0
  Datum   values[Natts_pg_opfamily];
250
0
  bool    nulls[Natts_pg_opfamily];
251
0
  NameData  opfName;
252
0
  ObjectAddress myself,
253
0
        referenced;
254
255
0
  rel = table_open(OperatorFamilyRelationId, RowExclusiveLock);
256
257
  /*
258
   * Make sure there is no existing opfamily of this name (this is just to
259
   * give a more friendly error message than "duplicate key").
260
   */
261
0
  if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
262
0
                ObjectIdGetDatum(amoid),
263
0
                CStringGetDatum(opfname),
264
0
                ObjectIdGetDatum(namespaceoid)))
265
0
    ereport(ERROR,
266
0
        (errcode(ERRCODE_DUPLICATE_OBJECT),
267
0
         errmsg("operator family \"%s\" for access method \"%s\" already exists",
268
0
            opfname, stmt->amname)));
269
270
  /*
271
   * Okay, let's create the pg_opfamily entry.
272
   */
273
0
  memset(values, 0, sizeof(values));
274
0
  memset(nulls, false, sizeof(nulls));
275
276
0
  opfamilyoid = GetNewOidWithIndex(rel, OpfamilyOidIndexId,
277
0
                   Anum_pg_opfamily_oid);
278
0
  values[Anum_pg_opfamily_oid - 1] = ObjectIdGetDatum(opfamilyoid);
279
0
  values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
280
0
  namestrcpy(&opfName, opfname);
281
0
  values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
282
0
  values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
283
0
  values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());
284
285
0
  tup = heap_form_tuple(rel->rd_att, values, nulls);
286
287
0
  CatalogTupleInsert(rel, tup);
288
289
0
  heap_freetuple(tup);
290
291
  /*
292
   * Create dependencies for the opfamily proper.
293
   */
294
0
  myself.classId = OperatorFamilyRelationId;
295
0
  myself.objectId = opfamilyoid;
296
0
  myself.objectSubId = 0;
297
298
  /* dependency on access method */
299
0
  referenced.classId = AccessMethodRelationId;
300
0
  referenced.objectId = amoid;
301
0
  referenced.objectSubId = 0;
302
0
  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
303
304
  /* dependency on namespace */
305
0
  referenced.classId = NamespaceRelationId;
306
0
  referenced.objectId = namespaceoid;
307
0
  referenced.objectSubId = 0;
308
0
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
309
310
  /* dependency on owner */
311
0
  recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
312
313
  /* dependency on extension */
314
0
  recordDependencyOnCurrentExtension(&myself, false);
315
316
  /* Report the new operator family to possibly interested event triggers */
317
0
  EventTriggerCollectSimpleCommand(myself, InvalidObjectAddress,
318
0
                   (Node *) stmt);
319
320
  /* Post creation hook for new operator family */
321
0
  InvokeObjectPostCreateHook(OperatorFamilyRelationId, opfamilyoid, 0);
322
323
0
  table_close(rel, RowExclusiveLock);
324
325
0
  return myself;
326
0
}
327
328
/*
329
 * DefineOpClass
330
 *    Define a new index operator class.
331
 */
332
ObjectAddress
333
DefineOpClass(CreateOpClassStmt *stmt)
334
0
{
335
0
  char     *opcname;    /* name of opclass we're creating */
336
0
  Oid     amoid,      /* our AM's oid */
337
0
        typeoid,    /* indexable datatype oid */
338
0
        storageoid,   /* storage datatype oid, if any */
339
0
        namespaceoid, /* namespace to create opclass in */
340
0
        opfamilyoid,  /* oid of containing opfamily */
341
0
        opclassoid;   /* oid of opclass we create */
342
0
  int     maxOpNumber,  /* amstrategies value */
343
0
        optsProcNumber, /* amoptsprocnum value */
344
0
        maxProcNumber;  /* amsupport value */
345
0
  bool    amstorage;    /* amstorage flag */
346
0
  bool    isDefault = stmt->isDefault;
347
0
  List     *operators;    /* OpFamilyMember list for operators */
348
0
  List     *procedures;   /* OpFamilyMember list for support procs */
349
0
  ListCell   *l;
350
0
  Relation  rel;
351
0
  HeapTuple tup;
352
0
  Form_pg_am  amform;
353
0
  const IndexAmRoutine *amroutine;
354
0
  Datum   values[Natts_pg_opclass];
355
0
  bool    nulls[Natts_pg_opclass];
356
0
  AclResult aclresult;
357
0
  NameData  opcName;
358
0
  ObjectAddress myself,
359
0
        referenced;
360
361
  /* Convert list of names to a name and namespace */
362
0
  namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname,
363
0
                           &opcname);
364
365
  /* Check we have creation rights in target namespace */
366
0
  aclresult = object_aclcheck(NamespaceRelationId, namespaceoid, GetUserId(), ACL_CREATE);
367
0
  if (aclresult != ACLCHECK_OK)
368
0
    aclcheck_error(aclresult, OBJECT_SCHEMA,
369
0
             get_namespace_name(namespaceoid));
370
371
  /* Get necessary info about access method */
372
0
  tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
373
0
  if (!HeapTupleIsValid(tup))
374
0
    ereport(ERROR,
375
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
376
0
         errmsg("access method \"%s\" does not exist",
377
0
            stmt->amname)));
378
379
0
  amform = (Form_pg_am) GETSTRUCT(tup);
380
0
  amoid = amform->oid;
381
0
  amroutine = GetIndexAmRoutineByAmId(amoid, false);
382
0
  ReleaseSysCache(tup);
383
384
0
  maxOpNumber = amroutine->amstrategies;
385
  /* if amstrategies is zero, just enforce that op numbers fit in int16 */
386
0
  if (maxOpNumber <= 0)
387
0
    maxOpNumber = SHRT_MAX;
388
0
  maxProcNumber = amroutine->amsupport;
389
0
  optsProcNumber = amroutine->amoptsprocnum;
390
0
  amstorage = amroutine->amstorage;
391
392
  /* XXX Should we make any privilege check against the AM? */
393
394
  /*
395
   * The question of appropriate permissions for CREATE OPERATOR CLASS is
396
   * interesting.  Creating an opclass is tantamount to granting public
397
   * execute access on the functions involved, since the index machinery
398
   * generally does not check access permission before using the functions.
399
   * A minimum expectation therefore is that the caller have execute
400
   * privilege with grant option.  Since we don't have a way to make the
401
   * opclass go away if the grant option is revoked, we choose instead to
402
   * require ownership of the functions.  It's also not entirely clear what
403
   * permissions should be required on the datatype, but ownership seems
404
   * like a safe choice.
405
   *
406
   * Currently, we require superuser privileges to create an opclass. This
407
   * seems necessary because we have no way to validate that the offered set
408
   * of operators and functions are consistent with the AM's expectations.
409
   * It would be nice to provide such a check someday, if it can be done
410
   * without solving the halting problem :-(
411
   *
412
   * XXX re-enable NOT_USED code sections below if you remove this test.
413
   */
414
0
  if (!superuser())
415
0
    ereport(ERROR,
416
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
417
0
         errmsg("must be superuser to create an operator class")));
418
419
  /* Look up the datatype */
420
0
  typeoid = typenameTypeId(NULL, stmt->datatype);
421
422
#ifdef NOT_USED
423
  /* XXX this is unnecessary given the superuser check above */
424
  /* Check we have ownership of the datatype */
425
  if (!object_ownercheck(TypeRelationId, typeoid, GetUserId()))
426
    aclcheck_error_type(ACLCHECK_NOT_OWNER, typeoid);
427
#endif
428
429
  /*
430
   * Look up the containing operator family, or create one if FAMILY option
431
   * was omitted and there's not a match already.
432
   */
433
0
  if (stmt->opfamilyname)
434
0
  {
435
0
    opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
436
0
  }
437
0
  else
438
0
  {
439
    /* Lookup existing family of same name and namespace */
440
0
    tup = SearchSysCache3(OPFAMILYAMNAMENSP,
441
0
                ObjectIdGetDatum(amoid),
442
0
                PointerGetDatum(opcname),
443
0
                ObjectIdGetDatum(namespaceoid));
444
0
    if (HeapTupleIsValid(tup))
445
0
    {
446
0
      opfamilyoid = ((Form_pg_opfamily) GETSTRUCT(tup))->oid;
447
448
      /*
449
       * XXX given the superuser check above, there's no need for an
450
       * ownership check here
451
       */
452
0
      ReleaseSysCache(tup);
453
0
    }
454
0
    else
455
0
    {
456
0
      CreateOpFamilyStmt *opfstmt;
457
0
      ObjectAddress tmpAddr;
458
459
0
      opfstmt = makeNode(CreateOpFamilyStmt);
460
0
      opfstmt->opfamilyname = stmt->opclassname;
461
0
      opfstmt->amname = stmt->amname;
462
463
      /*
464
       * Create it ... again no need for more permissions ...
465
       */
466
0
      tmpAddr = CreateOpFamily(opfstmt, opcname, namespaceoid, amoid);
467
0
      opfamilyoid = tmpAddr.objectId;
468
0
    }
469
0
  }
470
471
0
  operators = NIL;
472
0
  procedures = NIL;
473
474
  /* Storage datatype is optional */
475
0
  storageoid = InvalidOid;
476
477
  /*
478
   * Scan the "items" list to obtain additional info.
479
   */
480
0
  foreach(l, stmt->items)
481
0
  {
482
0
    CreateOpClassItem *item = lfirst_node(CreateOpClassItem, l);
483
0
    Oid     operOid;
484
0
    Oid     funcOid;
485
0
    Oid     sortfamilyOid;
486
0
    OpFamilyMember *member;
487
488
0
    switch (item->itemtype)
489
0
    {
490
0
      case OPCLASS_ITEM_OPERATOR:
491
0
        if (item->number <= 0 || item->number > maxOpNumber)
492
0
          ereport(ERROR,
493
0
              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
494
0
               errmsg("invalid operator number %d,"
495
0
                  " must be between 1 and %d",
496
0
                  item->number, maxOpNumber)));
497
0
        if (item->name->objargs != NIL)
498
0
          operOid = LookupOperWithArgs(item->name, false);
499
0
        else
500
0
        {
501
          /* Default to binary op on input datatype */
502
0
          operOid = LookupOperName(NULL, item->name->objname,
503
0
                       typeoid, typeoid,
504
0
                       false, -1);
505
0
        }
506
507
0
        if (item->order_family)
508
0
          sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
509
0
                           item->order_family,
510
0
                           false);
511
0
        else
512
0
          sortfamilyOid = InvalidOid;
513
514
#ifdef NOT_USED
515
        /* XXX this is unnecessary given the superuser check above */
516
        /* Caller must own operator and its underlying function */
517
        if (!object_ownercheck(OperatorRelationId, operOid, GetUserId()))
518
          aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
519
                   get_opname(operOid));
520
        funcOid = get_opcode(operOid);
521
        if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
522
          aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
523
                   get_func_name(funcOid));
524
#endif
525
526
        /* Save the info */
527
0
        member = palloc0_object(OpFamilyMember);
528
0
        member->is_func = false;
529
0
        member->object = operOid;
530
0
        member->number = item->number;
531
0
        member->sortfamily = sortfamilyOid;
532
0
        assignOperTypes(member, amoid, typeoid);
533
0
        addFamilyMember(&operators, member);
534
0
        break;
535
0
      case OPCLASS_ITEM_FUNCTION:
536
0
        if (item->number <= 0 || item->number > maxProcNumber)
537
0
          ereport(ERROR,
538
0
              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
539
0
               errmsg("invalid function number %d,"
540
0
                  " must be between 1 and %d",
541
0
                  item->number, maxProcNumber)));
542
0
        funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
543
#ifdef NOT_USED
544
        /* XXX this is unnecessary given the superuser check above */
545
        /* Caller must own function */
546
        if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
547
          aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
548
                   get_func_name(funcOid));
549
#endif
550
        /* Save the info */
551
0
        member = palloc0_object(OpFamilyMember);
552
0
        member->is_func = true;
553
0
        member->object = funcOid;
554
0
        member->number = item->number;
555
556
        /* allow overriding of the function's actual arg types */
557
0
        if (item->class_args)
558
0
          processTypesSpec(item->class_args,
559
0
                   &member->lefttype, &member->righttype);
560
561
0
        assignProcTypes(member, amoid, typeoid, optsProcNumber);
562
0
        addFamilyMember(&procedures, member);
563
0
        break;
564
0
      case OPCLASS_ITEM_STORAGETYPE:
565
0
        if (OidIsValid(storageoid))
566
0
          ereport(ERROR,
567
0
              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
568
0
               errmsg("storage type specified more than once")));
569
0
        storageoid = typenameTypeId(NULL, item->storedtype);
570
571
#ifdef NOT_USED
572
        /* XXX this is unnecessary given the superuser check above */
573
        /* Check we have ownership of the datatype */
574
        if (!object_ownercheck(TypeRelationId, storageoid, GetUserId()))
575
          aclcheck_error_type(ACLCHECK_NOT_OWNER, storageoid);
576
#endif
577
0
        break;
578
0
      default:
579
0
        elog(ERROR, "unrecognized item type: %d", item->itemtype);
580
0
        break;
581
0
    }
582
0
  }
583
584
  /*
585
   * If storagetype is specified, make sure it's legal.
586
   */
587
0
  if (OidIsValid(storageoid))
588
0
  {
589
    /* Just drop the spec if same as column datatype */
590
0
    if (storageoid == typeoid)
591
0
      storageoid = InvalidOid;
592
0
    else if (!amstorage)
593
0
      ereport(ERROR,
594
0
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
595
0
           errmsg("storage type cannot be different from data type for access method \"%s\"",
596
0
              stmt->amname)));
597
0
  }
598
599
0
  rel = table_open(OperatorClassRelationId, RowExclusiveLock);
600
601
  /*
602
   * Make sure there is no existing opclass of this name (this is just to
603
   * give a more friendly error message than "duplicate key").
604
   */
605
0
  if (SearchSysCacheExists3(CLAAMNAMENSP,
606
0
                ObjectIdGetDatum(amoid),
607
0
                CStringGetDatum(opcname),
608
0
                ObjectIdGetDatum(namespaceoid)))
609
0
    ereport(ERROR,
610
0
        (errcode(ERRCODE_DUPLICATE_OBJECT),
611
0
         errmsg("operator class \"%s\" for access method \"%s\" already exists",
612
0
            opcname, stmt->amname)));
613
614
  /*
615
   * HACK: if we're trying to create btree_gist's gist_inet_ops or
616
   * gist_cidr_ops during a binary upgrade, avoid failure in the next stanza
617
   * by silently making the new opclass non-default.  Without this kluge, we
618
   * would fail to upgrade databases containing pre-1.9 versions of
619
   * contrib/btree_gist.  We can remove it sometime in the far future when
620
   * we don't expect any such databases to exist.  (The result of this hack
621
   * is that the installed version of btree_gist will approximate btree_gist
622
   * 1.9, how closely depending on whether it's 1.8 or something older.
623
   * ALTER EXTENSION UPDATE can be used to bring it up to real 1.9.)
624
   */
625
0
  if (isDefault && IsBinaryUpgrade)
626
0
  {
627
0
    if (amoid == GIST_AM_OID &&
628
0
      ((typeoid == INETOID && strcmp(opcname, "gist_inet_ops") == 0) ||
629
0
       (typeoid == CIDROID && strcmp(opcname, "gist_cidr_ops") == 0)))
630
0
      isDefault = false;
631
0
  }
632
633
  /*
634
   * If we are creating a default opclass, check there isn't one already.
635
   * (Note we do not restrict this test to visible opclasses; this ensures
636
   * that typcache.c can find unique solutions to its questions.)
637
   */
638
0
  if (isDefault)
639
0
  {
640
0
    ScanKeyData skey[1];
641
0
    SysScanDesc scan;
642
643
0
    ScanKeyInit(&skey[0],
644
0
          Anum_pg_opclass_opcmethod,
645
0
          BTEqualStrategyNumber, F_OIDEQ,
646
0
          ObjectIdGetDatum(amoid));
647
648
0
    scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
649
0
                  NULL, 1, skey);
650
651
0
    while (HeapTupleIsValid(tup = systable_getnext(scan)))
652
0
    {
653
0
      Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
654
655
0
      if (opclass->opcintype == typeoid && opclass->opcdefault)
656
0
        ereport(ERROR,
657
0
            (errcode(ERRCODE_DUPLICATE_OBJECT),
658
0
             errmsg("could not make operator class \"%s\" be default for type %s",
659
0
                opcname,
660
0
                TypeNameToString(stmt->datatype)),
661
0
             errdetail("Operator class \"%s\" already is the default.",
662
0
                   NameStr(opclass->opcname))));
663
0
    }
664
665
0
    systable_endscan(scan);
666
0
  }
667
668
  /*
669
   * Okay, let's create the pg_opclass entry.
670
   */
671
0
  memset(values, 0, sizeof(values));
672
0
  memset(nulls, false, sizeof(nulls));
673
674
0
  opclassoid = GetNewOidWithIndex(rel, OpclassOidIndexId,
675
0
                  Anum_pg_opclass_oid);
676
0
  values[Anum_pg_opclass_oid - 1] = ObjectIdGetDatum(opclassoid);
677
0
  values[Anum_pg_opclass_opcmethod - 1] = ObjectIdGetDatum(amoid);
678
0
  namestrcpy(&opcName, opcname);
679
0
  values[Anum_pg_opclass_opcname - 1] = NameGetDatum(&opcName);
680
0
  values[Anum_pg_opclass_opcnamespace - 1] = ObjectIdGetDatum(namespaceoid);
681
0
  values[Anum_pg_opclass_opcowner - 1] = ObjectIdGetDatum(GetUserId());
682
0
  values[Anum_pg_opclass_opcfamily - 1] = ObjectIdGetDatum(opfamilyoid);
683
0
  values[Anum_pg_opclass_opcintype - 1] = ObjectIdGetDatum(typeoid);
684
0
  values[Anum_pg_opclass_opcdefault - 1] = BoolGetDatum(isDefault);
685
0
  values[Anum_pg_opclass_opckeytype - 1] = ObjectIdGetDatum(storageoid);
686
687
0
  tup = heap_form_tuple(rel->rd_att, values, nulls);
688
689
0
  CatalogTupleInsert(rel, tup);
690
691
0
  heap_freetuple(tup);
692
693
  /*
694
   * Now that we have the opclass OID, set up default dependency info for
695
   * the pg_amop and pg_amproc entries.  Historically, CREATE OPERATOR CLASS
696
   * has created hard dependencies on the opclass, so that's what we use.
697
   */
698
0
  foreach(l, operators)
699
0
  {
700
0
    OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
701
702
0
    op->ref_is_hard = true;
703
0
    op->ref_is_family = false;
704
0
    op->refobjid = opclassoid;
705
0
  }
706
0
  foreach(l, procedures)
707
0
  {
708
0
    OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
709
710
0
    proc->ref_is_hard = true;
711
0
    proc->ref_is_family = false;
712
0
    proc->refobjid = opclassoid;
713
0
  }
714
715
  /*
716
   * Let the index AM editorialize on the dependency choices.  It could also
717
   * do further validation on the operators and functions, if it likes.
718
   */
719
0
  if (amroutine->amadjustmembers)
720
0
    amroutine->amadjustmembers(opfamilyoid,
721
0
                   opclassoid,
722
0
                   operators,
723
0
                   procedures);
724
725
  /*
726
   * Now add tuples to pg_amop and pg_amproc tying in the operators and
727
   * functions.  Dependencies on them are inserted, too.
728
   */
729
0
  storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
730
0
           operators, false);
731
0
  storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
732
0
          procedures, false);
733
734
  /* let event triggers know what happened */
735
0
  EventTriggerCollectCreateOpClass(stmt, opclassoid, operators, procedures);
736
737
  /*
738
   * Create dependencies for the opclass proper.  Note: we do not need a
739
   * dependency link to the AM, because that exists through the opfamily.
740
   */
741
0
  myself.classId = OperatorClassRelationId;
742
0
  myself.objectId = opclassoid;
743
0
  myself.objectSubId = 0;
744
745
  /* dependency on namespace */
746
0
  referenced.classId = NamespaceRelationId;
747
0
  referenced.objectId = namespaceoid;
748
0
  referenced.objectSubId = 0;
749
0
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
750
751
  /* dependency on opfamily */
752
0
  referenced.classId = OperatorFamilyRelationId;
753
0
  referenced.objectId = opfamilyoid;
754
0
  referenced.objectSubId = 0;
755
0
  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
756
757
  /* dependency on indexed datatype */
758
0
  referenced.classId = TypeRelationId;
759
0
  referenced.objectId = typeoid;
760
0
  referenced.objectSubId = 0;
761
0
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
762
763
  /* dependency on storage datatype */
764
0
  if (OidIsValid(storageoid))
765
0
  {
766
0
    referenced.classId = TypeRelationId;
767
0
    referenced.objectId = storageoid;
768
0
    referenced.objectSubId = 0;
769
0
    recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
770
0
  }
771
772
  /* dependency on owner */
773
0
  recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
774
775
  /* dependency on extension */
776
0
  recordDependencyOnCurrentExtension(&myself, false);
777
778
  /* Post creation hook for new operator class */
779
0
  InvokeObjectPostCreateHook(OperatorClassRelationId, opclassoid, 0);
780
781
0
  table_close(rel, RowExclusiveLock);
782
783
0
  return myself;
784
0
}
785
786
787
/*
788
 * DefineOpFamily
789
 *    Define a new index operator family.
790
 */
791
ObjectAddress
792
DefineOpFamily(CreateOpFamilyStmt *stmt)
793
0
{
794
0
  char     *opfname;    /* name of opfamily we're creating */
795
0
  Oid     amoid,      /* our AM's oid */
796
0
        namespaceoid; /* namespace to create opfamily in */
797
0
  AclResult aclresult;
798
799
  /* Convert list of names to a name and namespace */
800
0
  namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
801
0
                           &opfname);
802
803
  /* Check we have creation rights in target namespace */
804
0
  aclresult = object_aclcheck(NamespaceRelationId, namespaceoid, GetUserId(), ACL_CREATE);
805
0
  if (aclresult != ACLCHECK_OK)
806
0
    aclcheck_error(aclresult, OBJECT_SCHEMA,
807
0
             get_namespace_name(namespaceoid));
808
809
  /* Get access method OID, throwing an error if it doesn't exist. */
810
0
  amoid = get_index_am_oid(stmt->amname, false);
811
812
  /* XXX Should we make any privilege check against the AM? */
813
814
  /*
815
   * Currently, we require superuser privileges to create an opfamily. See
816
   * comments in DefineOpClass.
817
   */
818
0
  if (!superuser())
819
0
    ereport(ERROR,
820
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
821
0
         errmsg("must be superuser to create an operator family")));
822
823
  /* Insert pg_opfamily catalog entry */
824
0
  return CreateOpFamily(stmt, opfname, namespaceoid, amoid);
825
0
}
826
827
828
/*
829
 * AlterOpFamily
830
 *    Add or remove operators/procedures within an existing operator family.
831
 *
832
 * Note: this implements only ALTER OPERATOR FAMILY ... ADD/DROP.  Some
833
 * other commands called ALTER OPERATOR FAMILY exist, but go through
834
 * different code paths.
835
 */
836
Oid
837
AlterOpFamily(AlterOpFamilyStmt *stmt)
838
0
{
839
0
  Oid     amoid,      /* our AM's oid */
840
0
        opfamilyoid;  /* oid of opfamily */
841
0
  int     maxOpNumber,  /* amstrategies value */
842
0
        optsProcNumber, /* amoptsprocnum value */
843
0
        maxProcNumber;  /* amsupport value */
844
0
  HeapTuple tup;
845
0
  Form_pg_am  amform;
846
0
  const IndexAmRoutine *amroutine;
847
848
  /* Get necessary info about access method */
849
0
  tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
850
0
  if (!HeapTupleIsValid(tup))
851
0
    ereport(ERROR,
852
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
853
0
         errmsg("access method \"%s\" does not exist",
854
0
            stmt->amname)));
855
856
0
  amform = (Form_pg_am) GETSTRUCT(tup);
857
0
  amoid = amform->oid;
858
0
  amroutine = GetIndexAmRoutineByAmId(amoid, false);
859
0
  ReleaseSysCache(tup);
860
861
0
  maxOpNumber = amroutine->amstrategies;
862
  /* if amstrategies is zero, just enforce that op numbers fit in int16 */
863
0
  if (maxOpNumber <= 0)
864
0
    maxOpNumber = SHRT_MAX;
865
0
  maxProcNumber = amroutine->amsupport;
866
0
  optsProcNumber = amroutine->amoptsprocnum;
867
868
  /* XXX Should we make any privilege check against the AM? */
869
870
  /* Look up the opfamily */
871
0
  opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
872
873
  /*
874
   * Currently, we require superuser privileges to alter an opfamily.
875
   *
876
   * XXX re-enable NOT_USED code sections below if you remove this test.
877
   */
878
0
  if (!superuser())
879
0
    ereport(ERROR,
880
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
881
0
         errmsg("must be superuser to alter an operator family")));
882
883
  /*
884
   * ADD and DROP cases need separate code from here on down.
885
   */
886
0
  if (stmt->isDrop)
887
0
    AlterOpFamilyDrop(stmt, amoid, opfamilyoid,
888
0
              maxOpNumber, maxProcNumber, stmt->items);
889
0
  else
890
0
    AlterOpFamilyAdd(stmt, amoid, opfamilyoid,
891
0
             maxOpNumber, maxProcNumber, optsProcNumber,
892
0
             stmt->items);
893
894
0
  return opfamilyoid;
895
0
}
896
897
/*
898
 * ADD part of ALTER OP FAMILY
899
 */
900
static void
901
AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
902
         int maxOpNumber, int maxProcNumber, int optsProcNumber,
903
         List *items)
904
0
{
905
0
  const IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
906
0
  List     *operators;    /* OpFamilyMember list for operators */
907
0
  List     *procedures;   /* OpFamilyMember list for support procs */
908
0
  ListCell   *l;
909
910
0
  operators = NIL;
911
0
  procedures = NIL;
912
913
  /*
914
   * Scan the "items" list to obtain additional info.
915
   */
916
0
  foreach(l, items)
917
0
  {
918
0
    CreateOpClassItem *item = lfirst_node(CreateOpClassItem, l);
919
0
    Oid     operOid;
920
0
    Oid     funcOid;
921
0
    Oid     sortfamilyOid;
922
0
    OpFamilyMember *member;
923
924
0
    switch (item->itemtype)
925
0
    {
926
0
      case OPCLASS_ITEM_OPERATOR:
927
0
        if (item->number <= 0 || item->number > maxOpNumber)
928
0
          ereport(ERROR,
929
0
              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
930
0
               errmsg("invalid operator number %d,"
931
0
                  " must be between 1 and %d",
932
0
                  item->number, maxOpNumber)));
933
0
        if (item->name->objargs != NIL)
934
0
          operOid = LookupOperWithArgs(item->name, false);
935
0
        else
936
0
        {
937
0
          ereport(ERROR,
938
0
              (errcode(ERRCODE_SYNTAX_ERROR),
939
0
               errmsg("operator argument types must be specified in ALTER OPERATOR FAMILY")));
940
0
          operOid = InvalidOid; /* keep compiler quiet */
941
0
        }
942
943
0
        if (item->order_family)
944
0
          sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
945
0
                           item->order_family,
946
0
                           false);
947
0
        else
948
0
          sortfamilyOid = InvalidOid;
949
950
#ifdef NOT_USED
951
        /* XXX this is unnecessary given the superuser check above */
952
        /* Caller must own operator and its underlying function */
953
        if (!object_ownercheck(OperatorRelationId, operOid, GetUserId()))
954
          aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
955
                   get_opname(operOid));
956
        funcOid = get_opcode(operOid);
957
        if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
958
          aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
959
                   get_func_name(funcOid));
960
#endif
961
962
        /* Save the info */
963
0
        member = palloc0_object(OpFamilyMember);
964
0
        member->is_func = false;
965
0
        member->object = operOid;
966
0
        member->number = item->number;
967
0
        member->sortfamily = sortfamilyOid;
968
        /* We can set up dependency fields immediately */
969
        /* Historically, ALTER ADD has created soft dependencies */
970
0
        member->ref_is_hard = false;
971
0
        member->ref_is_family = true;
972
0
        member->refobjid = opfamilyoid;
973
0
        assignOperTypes(member, amoid, InvalidOid);
974
0
        addFamilyMember(&operators, member);
975
0
        break;
976
0
      case OPCLASS_ITEM_FUNCTION:
977
0
        if (item->number <= 0 || item->number > maxProcNumber)
978
0
          ereport(ERROR,
979
0
              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
980
0
               errmsg("invalid function number %d,"
981
0
                  " must be between 1 and %d",
982
0
                  item->number, maxProcNumber)));
983
0
        funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
984
#ifdef NOT_USED
985
        /* XXX this is unnecessary given the superuser check above */
986
        /* Caller must own function */
987
        if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
988
          aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
989
                   get_func_name(funcOid));
990
#endif
991
992
        /* Save the info */
993
0
        member = palloc0_object(OpFamilyMember);
994
0
        member->is_func = true;
995
0
        member->object = funcOid;
996
0
        member->number = item->number;
997
        /* We can set up dependency fields immediately */
998
        /* Historically, ALTER ADD has created soft dependencies */
999
0
        member->ref_is_hard = false;
1000
0
        member->ref_is_family = true;
1001
0
        member->refobjid = opfamilyoid;
1002
1003
        /* allow overriding of the function's actual arg types */
1004
0
        if (item->class_args)
1005
0
          processTypesSpec(item->class_args,
1006
0
                   &member->lefttype, &member->righttype);
1007
1008
0
        assignProcTypes(member, amoid, InvalidOid, optsProcNumber);
1009
0
        addFamilyMember(&procedures, member);
1010
0
        break;
1011
0
      case OPCLASS_ITEM_STORAGETYPE:
1012
0
        ereport(ERROR,
1013
0
            (errcode(ERRCODE_SYNTAX_ERROR),
1014
0
             errmsg("STORAGE cannot be specified in ALTER OPERATOR FAMILY")));
1015
0
        break;
1016
0
      default:
1017
0
        elog(ERROR, "unrecognized item type: %d", item->itemtype);
1018
0
        break;
1019
0
    }
1020
0
  }
1021
1022
  /*
1023
   * Let the index AM editorialize on the dependency choices.  It could also
1024
   * do further validation on the operators and functions, if it likes.
1025
   */
1026
0
  if (amroutine->amadjustmembers)
1027
0
    amroutine->amadjustmembers(opfamilyoid,
1028
0
                   InvalidOid, /* no specific opclass */
1029
0
                   operators,
1030
0
                   procedures);
1031
1032
  /*
1033
   * Add tuples to pg_amop and pg_amproc tying in the operators and
1034
   * functions.  Dependencies on them are inserted, too.
1035
   */
1036
0
  storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
1037
0
           operators, true);
1038
0
  storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
1039
0
          procedures, true);
1040
1041
  /* make information available to event triggers */
1042
0
  EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
1043
0
                  operators, procedures);
1044
0
}
1045
1046
/*
1047
 * DROP part of ALTER OP FAMILY
1048
 */
1049
static void
1050
AlterOpFamilyDrop(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
1051
          int maxOpNumber, int maxProcNumber, List *items)
1052
0
{
1053
0
  List     *operators;    /* OpFamilyMember list for operators */
1054
0
  List     *procedures;   /* OpFamilyMember list for support procs */
1055
0
  ListCell   *l;
1056
1057
0
  operators = NIL;
1058
0
  procedures = NIL;
1059
1060
  /*
1061
   * Scan the "items" list to obtain additional info.
1062
   */
1063
0
  foreach(l, items)
1064
0
  {
1065
0
    CreateOpClassItem *item = lfirst_node(CreateOpClassItem, l);
1066
0
    Oid     lefttype,
1067
0
          righttype;
1068
0
    OpFamilyMember *member;
1069
1070
0
    switch (item->itemtype)
1071
0
    {
1072
0
      case OPCLASS_ITEM_OPERATOR:
1073
0
        if (item->number <= 0 || item->number > maxOpNumber)
1074
0
          ereport(ERROR,
1075
0
              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1076
0
               errmsg("invalid operator number %d,"
1077
0
                  " must be between 1 and %d",
1078
0
                  item->number, maxOpNumber)));
1079
0
        processTypesSpec(item->class_args, &lefttype, &righttype);
1080
        /* Save the info */
1081
0
        member = palloc0_object(OpFamilyMember);
1082
0
        member->is_func = false;
1083
0
        member->number = item->number;
1084
0
        member->lefttype = lefttype;
1085
0
        member->righttype = righttype;
1086
0
        addFamilyMember(&operators, member);
1087
0
        break;
1088
0
      case OPCLASS_ITEM_FUNCTION:
1089
0
        if (item->number <= 0 || item->number > maxProcNumber)
1090
0
          ereport(ERROR,
1091
0
              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1092
0
               errmsg("invalid function number %d,"
1093
0
                  " must be between 1 and %d",
1094
0
                  item->number, maxProcNumber)));
1095
0
        processTypesSpec(item->class_args, &lefttype, &righttype);
1096
        /* Save the info */
1097
0
        member = palloc0_object(OpFamilyMember);
1098
0
        member->is_func = true;
1099
0
        member->number = item->number;
1100
0
        member->lefttype = lefttype;
1101
0
        member->righttype = righttype;
1102
0
        addFamilyMember(&procedures, member);
1103
0
        break;
1104
0
      case OPCLASS_ITEM_STORAGETYPE:
1105
        /* grammar prevents this from appearing */
1106
0
      default:
1107
0
        elog(ERROR, "unrecognized item type: %d", item->itemtype);
1108
0
        break;
1109
0
    }
1110
0
  }
1111
1112
  /*
1113
   * Remove tuples from pg_amop and pg_amproc.
1114
   */
1115
0
  dropOperators(stmt->opfamilyname, amoid, opfamilyoid, operators);
1116
0
  dropProcedures(stmt->opfamilyname, amoid, opfamilyoid, procedures);
1117
1118
  /* make information available to event triggers */
1119
0
  EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
1120
0
                  operators, procedures);
1121
0
}
1122
1123
1124
/*
1125
 * Deal with explicit arg types used in ALTER ADD/DROP
1126
 */
1127
static void
1128
processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
1129
0
{
1130
0
  TypeName   *typeName;
1131
1132
0
  Assert(args != NIL);
1133
1134
0
  typeName = (TypeName *) linitial(args);
1135
0
  *lefttype = typenameTypeId(NULL, typeName);
1136
1137
0
  if (list_length(args) > 1)
1138
0
  {
1139
0
    typeName = (TypeName *) lsecond(args);
1140
0
    *righttype = typenameTypeId(NULL, typeName);
1141
0
  }
1142
0
  else
1143
0
    *righttype = *lefttype;
1144
1145
0
  if (list_length(args) > 2)
1146
0
    ereport(ERROR,
1147
0
        (errcode(ERRCODE_SYNTAX_ERROR),
1148
0
         errmsg("one or two argument types must be specified")));
1149
0
}
1150
1151
1152
/*
1153
 * Determine the lefttype/righttype to assign to an operator,
1154
 * and do any validity checking we can manage.
1155
 */
1156
static void
1157
assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
1158
0
{
1159
0
  Operator  optup;
1160
0
  Form_pg_operator opform;
1161
1162
  /* Fetch the operator definition */
1163
0
  optup = SearchSysCache1(OPEROID, ObjectIdGetDatum(member->object));
1164
0
  if (!HeapTupleIsValid(optup))
1165
0
    elog(ERROR, "cache lookup failed for operator %u", member->object);
1166
0
  opform = (Form_pg_operator) GETSTRUCT(optup);
1167
1168
  /*
1169
   * Opfamily operators must be binary.
1170
   */
1171
0
  if (opform->oprkind != 'b')
1172
0
    ereport(ERROR,
1173
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1174
0
         errmsg("index operators must be binary")));
1175
1176
0
  if (OidIsValid(member->sortfamily))
1177
0
  {
1178
    /*
1179
     * Ordering op, check index supports that.  (We could perhaps also
1180
     * check that the operator returns a type supported by the sortfamily,
1181
     * but that seems more trouble than it's worth here.  If it does not,
1182
     * the operator will never be matchable to any ORDER BY clause, but no
1183
     * worse consequences can ensue.  Also, trying to check that would
1184
     * create an ordering hazard during dump/reload: it's possible that
1185
     * the family has been created but not yet populated with the required
1186
     * operators.)
1187
     */
1188
0
    if (!GetIndexAmRoutineByAmId(amoid, false)->amcanorderbyop)
1189
0
      ereport(ERROR,
1190
0
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1191
0
           errmsg("access method \"%s\" does not support ordering operators",
1192
0
              get_am_name(amoid))));
1193
0
  }
1194
0
  else
1195
0
  {
1196
    /*
1197
     * Search operators must return boolean.
1198
     */
1199
0
    if (opform->oprresult != BOOLOID)
1200
0
      ereport(ERROR,
1201
0
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1202
0
           errmsg("index search operators must return boolean")));
1203
0
  }
1204
1205
  /*
1206
   * If lefttype/righttype isn't specified, use the operator's input types
1207
   */
1208
0
  if (!OidIsValid(member->lefttype))
1209
0
    member->lefttype = opform->oprleft;
1210
0
  if (!OidIsValid(member->righttype))
1211
0
    member->righttype = opform->oprright;
1212
1213
0
  ReleaseSysCache(optup);
1214
0
}
1215
1216
/*
1217
 * Determine the lefttype/righttype to assign to a support procedure,
1218
 * and do any validity checking we can manage.
1219
 */
1220
static void
1221
assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid,
1222
        int opclassOptsProcNum)
1223
0
{
1224
0
  HeapTuple proctup;
1225
0
  Form_pg_proc procform;
1226
1227
  /* Fetch the procedure definition */
1228
0
  proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(member->object));
1229
0
  if (!HeapTupleIsValid(proctup))
1230
0
    elog(ERROR, "cache lookup failed for function %u", member->object);
1231
0
  procform = (Form_pg_proc) GETSTRUCT(proctup);
1232
1233
  /* Check the signature of the opclass options parsing function */
1234
0
  if (member->number == opclassOptsProcNum)
1235
0
  {
1236
0
    if (OidIsValid(typeoid))
1237
0
    {
1238
0
      if ((OidIsValid(member->lefttype) && member->lefttype != typeoid) ||
1239
0
        (OidIsValid(member->righttype) && member->righttype != typeoid))
1240
0
        ereport(ERROR,
1241
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1242
0
             errmsg("associated data types for operator class options parsing functions must match opclass input type")));
1243
0
    }
1244
0
    else
1245
0
    {
1246
0
      if (member->lefttype != member->righttype)
1247
0
        ereport(ERROR,
1248
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1249
0
             errmsg("left and right associated data types for operator class options parsing functions must match")));
1250
0
    }
1251
1252
0
    if (procform->prorettype != VOIDOID ||
1253
0
      procform->pronargs != 1 ||
1254
0
      procform->proargtypes.values[0] != INTERNALOID)
1255
0
      ereport(ERROR,
1256
0
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1257
0
           errmsg("invalid operator class options parsing function"),
1258
0
           errhint("Valid signature of operator class options parsing function is %s.",
1259
0
               "(internal) RETURNS void")));
1260
0
  }
1261
1262
  /*
1263
   * Ordering comparison procs must be 2-arg procs returning int4.  Ordering
1264
   * sortsupport procs must take internal and return void.  Ordering
1265
   * in_range procs must be 5-arg procs returning bool.  Ordering equalimage
1266
   * procs must take 1 arg and return bool.  Hashing support proc 1 must be
1267
   * a 1-arg proc returning int4, while proc 2 must be a 2-arg proc
1268
   * returning int8. Otherwise we don't know.
1269
   */
1270
0
  else if (GetIndexAmRoutineByAmId(amoid, false)->amcanorder)
1271
0
  {
1272
0
    if (member->number == BTORDER_PROC)
1273
0
    {
1274
0
      if (procform->pronargs != 2)
1275
0
        ereport(ERROR,
1276
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1277
0
             errmsg("ordering comparison functions must have two arguments")));
1278
0
      if (procform->prorettype != INT4OID)
1279
0
        ereport(ERROR,
1280
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1281
0
             errmsg("ordering comparison functions must return integer")));
1282
1283
      /*
1284
       * If lefttype/righttype isn't specified, use the proc's input
1285
       * types
1286
       */
1287
0
      if (!OidIsValid(member->lefttype))
1288
0
        member->lefttype = procform->proargtypes.values[0];
1289
0
      if (!OidIsValid(member->righttype))
1290
0
        member->righttype = procform->proargtypes.values[1];
1291
0
    }
1292
0
    else if (member->number == BTSORTSUPPORT_PROC)
1293
0
    {
1294
0
      if (procform->pronargs != 1 ||
1295
0
        procform->proargtypes.values[0] != INTERNALOID)
1296
0
        ereport(ERROR,
1297
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1298
0
             errmsg("ordering sort support functions must accept type \"internal\"")));
1299
0
      if (procform->prorettype != VOIDOID)
1300
0
        ereport(ERROR,
1301
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1302
0
             errmsg("ordering sort support functions must return void")));
1303
1304
      /*
1305
       * Can't infer lefttype/righttype from proc, so use default rule
1306
       */
1307
0
    }
1308
0
    else if (member->number == BTINRANGE_PROC)
1309
0
    {
1310
0
      if (procform->pronargs != 5)
1311
0
        ereport(ERROR,
1312
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1313
0
             errmsg("ordering in_range functions must have five arguments")));
1314
0
      if (procform->prorettype != BOOLOID)
1315
0
        ereport(ERROR,
1316
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1317
0
             errmsg("ordering in_range functions must return boolean")));
1318
1319
      /*
1320
       * If lefttype/righttype isn't specified, use the proc's input
1321
       * types (we look at the test-value and offset arguments)
1322
       */
1323
0
      if (!OidIsValid(member->lefttype))
1324
0
        member->lefttype = procform->proargtypes.values[0];
1325
0
      if (!OidIsValid(member->righttype))
1326
0
        member->righttype = procform->proargtypes.values[2];
1327
0
    }
1328
0
    else if (member->number == BTEQUALIMAGE_PROC)
1329
0
    {
1330
0
      if (procform->pronargs != 1)
1331
0
        ereport(ERROR,
1332
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1333
0
             errmsg("ordering equal image functions must have one argument")));
1334
0
      if (procform->prorettype != BOOLOID)
1335
0
        ereport(ERROR,
1336
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1337
0
             errmsg("ordering equal image functions must return boolean")));
1338
1339
      /*
1340
       * pg_amproc functions are indexed by (lefttype, righttype), but
1341
       * an equalimage function can only be called at CREATE INDEX time.
1342
       * The same opclass opcintype OID is always used for lefttype and
1343
       * righttype.  Providing a cross-type routine isn't sensible.
1344
       * Reject cross-type ALTER OPERATOR FAMILY ...  ADD FUNCTION 4
1345
       * statements here.
1346
       */
1347
0
      if (member->lefttype != member->righttype)
1348
0
        ereport(ERROR,
1349
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1350
0
             errmsg("ordering equal image functions must not be cross-type")));
1351
0
    }
1352
0
    else if (member->number == BTSKIPSUPPORT_PROC)
1353
0
    {
1354
0
      if (procform->pronargs != 1 ||
1355
0
        procform->proargtypes.values[0] != INTERNALOID)
1356
0
        ereport(ERROR,
1357
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1358
0
             errmsg("btree skip support functions must accept type \"internal\"")));
1359
0
      if (procform->prorettype != VOIDOID)
1360
0
        ereport(ERROR,
1361
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1362
0
             errmsg("btree skip support functions must return void")));
1363
1364
      /*
1365
       * pg_amproc functions are indexed by (lefttype, righttype), but a
1366
       * skip support function doesn't make sense in cross-type
1367
       * scenarios.  The same opclass opcintype OID is always used for
1368
       * lefttype and righttype.  Providing a cross-type routine isn't
1369
       * sensible.  Reject cross-type ALTER OPERATOR FAMILY ...  ADD
1370
       * FUNCTION 6 statements here.
1371
       */
1372
0
      if (member->lefttype != member->righttype)
1373
0
        ereport(ERROR,
1374
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1375
0
             errmsg("btree skip support functions must not be cross-type")));
1376
0
    }
1377
0
  }
1378
0
  else if (GetIndexAmRoutineByAmId(amoid, false)->amcanhash)
1379
0
  {
1380
0
    if (member->number == HASHSTANDARD_PROC)
1381
0
    {
1382
0
      if (procform->pronargs != 1)
1383
0
        ereport(ERROR,
1384
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1385
0
             errmsg("hash function 1 must have one argument")));
1386
0
      if (procform->prorettype != INT4OID)
1387
0
        ereport(ERROR,
1388
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1389
0
             errmsg("hash function 1 must return integer")));
1390
0
    }
1391
0
    else if (member->number == HASHEXTENDED_PROC)
1392
0
    {
1393
0
      if (procform->pronargs != 2)
1394
0
        ereport(ERROR,
1395
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1396
0
             errmsg("hash function 2 must have two arguments")));
1397
0
      if (procform->prorettype != INT8OID)
1398
0
        ereport(ERROR,
1399
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1400
0
             errmsg("hash function 2 must return bigint")));
1401
0
    }
1402
1403
    /*
1404
     * If lefttype/righttype isn't specified, use the proc's input type
1405
     */
1406
0
    if (!OidIsValid(member->lefttype))
1407
0
      member->lefttype = procform->proargtypes.values[0];
1408
0
    if (!OidIsValid(member->righttype))
1409
0
      member->righttype = procform->proargtypes.values[0];
1410
0
  }
1411
1412
  /*
1413
   * The default in CREATE OPERATOR CLASS is to use the class' opcintype as
1414
   * lefttype and righttype.  In CREATE or ALTER OPERATOR FAMILY, opcintype
1415
   * isn't available, so make the user specify the types.
1416
   */
1417
0
  if (!OidIsValid(member->lefttype))
1418
0
    member->lefttype = typeoid;
1419
0
  if (!OidIsValid(member->righttype))
1420
0
    member->righttype = typeoid;
1421
1422
0
  if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
1423
0
    ereport(ERROR,
1424
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1425
0
         errmsg("associated data types must be specified for index support function")));
1426
1427
0
  ReleaseSysCache(proctup);
1428
0
}
1429
1430
/*
1431
 * Add a new family member to the appropriate list, after checking for
1432
 * duplicated strategy or proc number.
1433
 */
1434
static void
1435
addFamilyMember(List **list, OpFamilyMember *member)
1436
0
{
1437
0
  ListCell   *l;
1438
1439
0
  foreach(l, *list)
1440
0
  {
1441
0
    OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
1442
1443
0
    if (old->number == member->number &&
1444
0
      old->lefttype == member->lefttype &&
1445
0
      old->righttype == member->righttype)
1446
0
    {
1447
0
      if (member->is_func)
1448
0
        ereport(ERROR,
1449
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1450
0
             errmsg("function number %d for (%s,%s) appears more than once",
1451
0
                member->number,
1452
0
                format_type_be(member->lefttype),
1453
0
                format_type_be(member->righttype))));
1454
0
      else
1455
0
        ereport(ERROR,
1456
0
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1457
0
             errmsg("operator number %d for (%s,%s) appears more than once",
1458
0
                member->number,
1459
0
                format_type_be(member->lefttype),
1460
0
                format_type_be(member->righttype))));
1461
0
    }
1462
0
  }
1463
0
  *list = lappend(*list, member);
1464
0
}
1465
1466
/*
1467
 * Dump the operators to pg_amop
1468
 *
1469
 * We also make dependency entries in pg_depend for the pg_amop entries.
1470
 */
1471
static void
1472
storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
1473
         List *operators, bool isAdd)
1474
0
{
1475
0
  Relation  rel;
1476
0
  Datum   values[Natts_pg_amop];
1477
0
  bool    nulls[Natts_pg_amop];
1478
0
  HeapTuple tup;
1479
0
  Oid     entryoid;
1480
0
  ObjectAddress myself,
1481
0
        referenced;
1482
0
  ListCell   *l;
1483
1484
0
  rel = table_open(AccessMethodOperatorRelationId, RowExclusiveLock);
1485
1486
0
  foreach(l, operators)
1487
0
  {
1488
0
    OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1489
0
    char    oppurpose;
1490
1491
    /*
1492
     * If adding to an existing family, check for conflict with an
1493
     * existing pg_amop entry (just to give a nicer error message)
1494
     */
1495
0
    if (isAdd &&
1496
0
      SearchSysCacheExists4(AMOPSTRATEGY,
1497
0
                  ObjectIdGetDatum(opfamilyoid),
1498
0
                  ObjectIdGetDatum(op->lefttype),
1499
0
                  ObjectIdGetDatum(op->righttype),
1500
0
                  Int16GetDatum(op->number)))
1501
0
      ereport(ERROR,
1502
0
          (errcode(ERRCODE_DUPLICATE_OBJECT),
1503
0
           errmsg("operator %d(%s,%s) already exists in operator family \"%s\"",
1504
0
              op->number,
1505
0
              format_type_be(op->lefttype),
1506
0
              format_type_be(op->righttype),
1507
0
              NameListToString(opfamilyname))));
1508
1509
0
    oppurpose = OidIsValid(op->sortfamily) ? AMOP_ORDER : AMOP_SEARCH;
1510
1511
    /* Create the pg_amop entry */
1512
0
    memset(values, 0, sizeof(values));
1513
0
    memset(nulls, false, sizeof(nulls));
1514
1515
0
    entryoid = GetNewOidWithIndex(rel, AccessMethodOperatorOidIndexId,
1516
0
                    Anum_pg_amop_oid);
1517
0
    values[Anum_pg_amop_oid - 1] = ObjectIdGetDatum(entryoid);
1518
0
    values[Anum_pg_amop_amopfamily - 1] = ObjectIdGetDatum(opfamilyoid);
1519
0
    values[Anum_pg_amop_amoplefttype - 1] = ObjectIdGetDatum(op->lefttype);
1520
0
    values[Anum_pg_amop_amoprighttype - 1] = ObjectIdGetDatum(op->righttype);
1521
0
    values[Anum_pg_amop_amopstrategy - 1] = Int16GetDatum(op->number);
1522
0
    values[Anum_pg_amop_amoppurpose - 1] = CharGetDatum(oppurpose);
1523
0
    values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
1524
0
    values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);
1525
0
    values[Anum_pg_amop_amopsortfamily - 1] = ObjectIdGetDatum(op->sortfamily);
1526
1527
0
    tup = heap_form_tuple(rel->rd_att, values, nulls);
1528
1529
0
    CatalogTupleInsert(rel, tup);
1530
1531
0
    heap_freetuple(tup);
1532
1533
    /* Make its dependencies */
1534
0
    myself.classId = AccessMethodOperatorRelationId;
1535
0
    myself.objectId = entryoid;
1536
0
    myself.objectSubId = 0;
1537
1538
0
    referenced.classId = OperatorRelationId;
1539
0
    referenced.objectId = op->object;
1540
0
    referenced.objectSubId = 0;
1541
1542
    /* see comments in amapi.h about dependency strength */
1543
0
    recordDependencyOn(&myself, &referenced,
1544
0
               op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
1545
1546
0
    referenced.classId = op->ref_is_family ? OperatorFamilyRelationId :
1547
0
      OperatorClassRelationId;
1548
0
    referenced.objectId = op->refobjid;
1549
0
    referenced.objectSubId = 0;
1550
1551
0
    recordDependencyOn(&myself, &referenced,
1552
0
               op->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
1553
1554
0
    if (typeDepNeeded(op->lefttype, op))
1555
0
    {
1556
0
      referenced.classId = TypeRelationId;
1557
0
      referenced.objectId = op->lefttype;
1558
0
      referenced.objectSubId = 0;
1559
1560
      /* see comments in amapi.h about dependency strength */
1561
0
      recordDependencyOn(&myself, &referenced,
1562
0
                 op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
1563
0
    }
1564
1565
0
    if (op->lefttype != op->righttype &&
1566
0
      typeDepNeeded(op->righttype, op))
1567
0
    {
1568
0
      referenced.classId = TypeRelationId;
1569
0
      referenced.objectId = op->righttype;
1570
0
      referenced.objectSubId = 0;
1571
1572
      /* see comments in amapi.h about dependency strength */
1573
0
      recordDependencyOn(&myself, &referenced,
1574
0
                 op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
1575
0
    }
1576
1577
    /* A search operator also needs a dep on the referenced opfamily */
1578
0
    if (OidIsValid(op->sortfamily))
1579
0
    {
1580
0
      referenced.classId = OperatorFamilyRelationId;
1581
0
      referenced.objectId = op->sortfamily;
1582
0
      referenced.objectSubId = 0;
1583
1584
0
      recordDependencyOn(&myself, &referenced,
1585
0
                 op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
1586
0
    }
1587
1588
    /* Post create hook of this access method operator */
1589
0
    InvokeObjectPostCreateHook(AccessMethodOperatorRelationId,
1590
0
                   entryoid, 0);
1591
0
  }
1592
1593
0
  table_close(rel, RowExclusiveLock);
1594
0
}
1595
1596
/*
1597
 * Dump the procedures (support routines) to pg_amproc
1598
 *
1599
 * We also make dependency entries in pg_depend for the pg_amproc entries.
1600
 */
1601
static void
1602
storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
1603
        List *procedures, bool isAdd)
1604
0
{
1605
0
  Relation  rel;
1606
0
  Datum   values[Natts_pg_amproc];
1607
0
  bool    nulls[Natts_pg_amproc];
1608
0
  HeapTuple tup;
1609
0
  Oid     entryoid;
1610
0
  ObjectAddress myself,
1611
0
        referenced;
1612
0
  ListCell   *l;
1613
1614
0
  rel = table_open(AccessMethodProcedureRelationId, RowExclusiveLock);
1615
1616
0
  foreach(l, procedures)
1617
0
  {
1618
0
    OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
1619
1620
    /*
1621
     * If adding to an existing family, check for conflict with an
1622
     * existing pg_amproc entry (just to give a nicer error message)
1623
     */
1624
0
    if (isAdd &&
1625
0
      SearchSysCacheExists4(AMPROCNUM,
1626
0
                  ObjectIdGetDatum(opfamilyoid),
1627
0
                  ObjectIdGetDatum(proc->lefttype),
1628
0
                  ObjectIdGetDatum(proc->righttype),
1629
0
                  Int16GetDatum(proc->number)))
1630
0
      ereport(ERROR,
1631
0
          (errcode(ERRCODE_DUPLICATE_OBJECT),
1632
0
           errmsg("function %d(%s,%s) already exists in operator family \"%s\"",
1633
0
              proc->number,
1634
0
              format_type_be(proc->lefttype),
1635
0
              format_type_be(proc->righttype),
1636
0
              NameListToString(opfamilyname))));
1637
1638
    /* Create the pg_amproc entry */
1639
0
    memset(values, 0, sizeof(values));
1640
0
    memset(nulls, false, sizeof(nulls));
1641
1642
0
    entryoid = GetNewOidWithIndex(rel, AccessMethodProcedureOidIndexId,
1643
0
                    Anum_pg_amproc_oid);
1644
0
    values[Anum_pg_amproc_oid - 1] = ObjectIdGetDatum(entryoid);
1645
0
    values[Anum_pg_amproc_amprocfamily - 1] = ObjectIdGetDatum(opfamilyoid);
1646
0
    values[Anum_pg_amproc_amproclefttype - 1] = ObjectIdGetDatum(proc->lefttype);
1647
0
    values[Anum_pg_amproc_amprocrighttype - 1] = ObjectIdGetDatum(proc->righttype);
1648
0
    values[Anum_pg_amproc_amprocnum - 1] = Int16GetDatum(proc->number);
1649
0
    values[Anum_pg_amproc_amproc - 1] = ObjectIdGetDatum(proc->object);
1650
1651
0
    tup = heap_form_tuple(rel->rd_att, values, nulls);
1652
1653
0
    CatalogTupleInsert(rel, tup);
1654
1655
0
    heap_freetuple(tup);
1656
1657
    /* Make its dependencies */
1658
0
    myself.classId = AccessMethodProcedureRelationId;
1659
0
    myself.objectId = entryoid;
1660
0
    myself.objectSubId = 0;
1661
1662
0
    referenced.classId = ProcedureRelationId;
1663
0
    referenced.objectId = proc->object;
1664
0
    referenced.objectSubId = 0;
1665
1666
    /* see comments in amapi.h about dependency strength */
1667
0
    recordDependencyOn(&myself, &referenced,
1668
0
               proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
1669
1670
0
    referenced.classId = proc->ref_is_family ? OperatorFamilyRelationId :
1671
0
      OperatorClassRelationId;
1672
0
    referenced.objectId = proc->refobjid;
1673
0
    referenced.objectSubId = 0;
1674
1675
0
    recordDependencyOn(&myself, &referenced,
1676
0
               proc->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
1677
1678
0
    if (typeDepNeeded(proc->lefttype, proc))
1679
0
    {
1680
0
      referenced.classId = TypeRelationId;
1681
0
      referenced.objectId = proc->lefttype;
1682
0
      referenced.objectSubId = 0;
1683
1684
      /* see comments in amapi.h about dependency strength */
1685
0
      recordDependencyOn(&myself, &referenced,
1686
0
                 proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
1687
0
    }
1688
1689
0
    if (proc->lefttype != proc->righttype &&
1690
0
      typeDepNeeded(proc->righttype, proc))
1691
0
    {
1692
0
      referenced.classId = TypeRelationId;
1693
0
      referenced.objectId = proc->righttype;
1694
0
      referenced.objectSubId = 0;
1695
1696
      /* see comments in amapi.h about dependency strength */
1697
0
      recordDependencyOn(&myself, &referenced,
1698
0
                 proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
1699
0
    }
1700
1701
    /* Post create hook of access method procedure */
1702
0
    InvokeObjectPostCreateHook(AccessMethodProcedureRelationId,
1703
0
                   entryoid, 0);
1704
0
  }
1705
1706
0
  table_close(rel, RowExclusiveLock);
1707
0
}
1708
1709
/*
1710
 * Detect whether a pg_amop or pg_amproc entry needs an explicit dependency
1711
 * on its lefttype or righttype.
1712
 *
1713
 * We make such a dependency unless the entry has an indirect dependency
1714
 * via its referenced operator or function.  That's nearly always true
1715
 * for operators, but might well not be true for support functions.
1716
 */
1717
static bool
1718
typeDepNeeded(Oid typid, OpFamilyMember *member)
1719
0
{
1720
0
  bool    result = true;
1721
1722
  /*
1723
   * If the type is pinned, we don't need a dependency.  This is a bit of a
1724
   * layering violation perhaps (recordDependencyOn would ignore the request
1725
   * anyway), but it's a cheap test and will frequently save a syscache
1726
   * lookup here.
1727
   */
1728
0
  if (IsPinnedObject(TypeRelationId, typid))
1729
0
    return false;
1730
1731
  /* Nope, so check the input types of the function or operator. */
1732
0
  if (member->is_func)
1733
0
  {
1734
0
    Oid      *argtypes;
1735
0
    int     nargs;
1736
1737
0
    (void) get_func_signature(member->object, &argtypes, &nargs);
1738
0
    for (int i = 0; i < nargs; i++)
1739
0
    {
1740
0
      if (typid == argtypes[i])
1741
0
      {
1742
0
        result = false; /* match, no dependency needed */
1743
0
        break;
1744
0
      }
1745
0
    }
1746
0
    pfree(argtypes);
1747
0
  }
1748
0
  else
1749
0
  {
1750
0
    Oid     lefttype,
1751
0
          righttype;
1752
1753
0
    op_input_types(member->object, &lefttype, &righttype);
1754
0
    if (typid == lefttype || typid == righttype)
1755
0
      result = false;   /* match, no dependency needed */
1756
0
  }
1757
0
  return result;
1758
0
}
1759
1760
1761
/*
1762
 * Remove operator entries from an opfamily.
1763
 *
1764
 * Note: this is only allowed for "loose" members of an opfamily, hence
1765
 * behavior is always RESTRICT.
1766
 */
1767
static void
1768
dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
1769
        List *operators)
1770
0
{
1771
0
  ListCell   *l;
1772
1773
0
  foreach(l, operators)
1774
0
  {
1775
0
    OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1776
0
    Oid     amopid;
1777
0
    ObjectAddress object;
1778
1779
0
    amopid = GetSysCacheOid4(AMOPSTRATEGY, Anum_pg_amop_oid,
1780
0
                 ObjectIdGetDatum(opfamilyoid),
1781
0
                 ObjectIdGetDatum(op->lefttype),
1782
0
                 ObjectIdGetDatum(op->righttype),
1783
0
                 Int16GetDatum(op->number));
1784
0
    if (!OidIsValid(amopid))
1785
0
      ereport(ERROR,
1786
0
          (errcode(ERRCODE_UNDEFINED_OBJECT),
1787
0
           errmsg("operator %d(%s,%s) does not exist in operator family \"%s\"",
1788
0
              op->number,
1789
0
              format_type_be(op->lefttype),
1790
0
              format_type_be(op->righttype),
1791
0
              NameListToString(opfamilyname))));
1792
1793
0
    object.classId = AccessMethodOperatorRelationId;
1794
0
    object.objectId = amopid;
1795
0
    object.objectSubId = 0;
1796
1797
0
    performDeletion(&object, DROP_RESTRICT, 0);
1798
0
  }
1799
0
}
1800
1801
/*
1802
 * Remove procedure entries from an opfamily.
1803
 *
1804
 * Note: this is only allowed for "loose" members of an opfamily, hence
1805
 * behavior is always RESTRICT.
1806
 */
1807
static void
1808
dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
1809
         List *procedures)
1810
0
{
1811
0
  ListCell   *l;
1812
1813
0
  foreach(l, procedures)
1814
0
  {
1815
0
    OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1816
0
    Oid     amprocid;
1817
0
    ObjectAddress object;
1818
1819
0
    amprocid = GetSysCacheOid4(AMPROCNUM, Anum_pg_amproc_oid,
1820
0
                   ObjectIdGetDatum(opfamilyoid),
1821
0
                   ObjectIdGetDatum(op->lefttype),
1822
0
                   ObjectIdGetDatum(op->righttype),
1823
0
                   Int16GetDatum(op->number));
1824
0
    if (!OidIsValid(amprocid))
1825
0
      ereport(ERROR,
1826
0
          (errcode(ERRCODE_UNDEFINED_OBJECT),
1827
0
           errmsg("function %d(%s,%s) does not exist in operator family \"%s\"",
1828
0
              op->number,
1829
0
              format_type_be(op->lefttype),
1830
0
              format_type_be(op->righttype),
1831
0
              NameListToString(opfamilyname))));
1832
1833
0
    object.classId = AccessMethodProcedureRelationId;
1834
0
    object.objectId = amprocid;
1835
0
    object.objectSubId = 0;
1836
1837
0
    performDeletion(&object, DROP_RESTRICT, 0);
1838
0
  }
1839
0
}
1840
1841
/*
1842
 * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
1843
 *
1844
 * Is there an operator class with the given name and signature already
1845
 * in the given namespace?  If so, raise an appropriate error message.
1846
 */
1847
void
1848
IsThereOpClassInNamespace(const char *opcname, Oid opcmethod,
1849
              Oid opcnamespace)
1850
0
{
1851
  /* make sure the new name doesn't exist */
1852
0
  if (SearchSysCacheExists3(CLAAMNAMENSP,
1853
0
                ObjectIdGetDatum(opcmethod),
1854
0
                CStringGetDatum(opcname),
1855
0
                ObjectIdGetDatum(opcnamespace)))
1856
0
    ereport(ERROR,
1857
0
        (errcode(ERRCODE_DUPLICATE_OBJECT),
1858
0
         errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
1859
0
            opcname,
1860
0
            get_am_name(opcmethod),
1861
0
            get_namespace_name(opcnamespace))));
1862
0
}
1863
1864
/*
1865
 * Subroutine for ALTER OPERATOR FAMILY SET SCHEMA/RENAME
1866
 *
1867
 * Is there an operator family with the given name and signature already
1868
 * in the given namespace?  If so, raise an appropriate error message.
1869
 */
1870
void
1871
IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
1872
               Oid opfnamespace)
1873
0
{
1874
  /* make sure the new name doesn't exist */
1875
0
  if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
1876
0
                ObjectIdGetDatum(opfmethod),
1877
0
                CStringGetDatum(opfname),
1878
0
                ObjectIdGetDatum(opfnamespace)))
1879
0
    ereport(ERROR,
1880
0
        (errcode(ERRCODE_DUPLICATE_OBJECT),
1881
0
         errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
1882
0
            opfname,
1883
0
            get_am_name(opfmethod),
1884
0
            get_namespace_name(opfnamespace))));
1885
0
}