PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
opclasscmds.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * opclasscmds.c
4  *
5  * Routines for opclass (and opfamily) manipulation commands
6  *
7  * Portions Copyright (c) 1996-2017, 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/heapam.h"
22 #include "access/nbtree.h"
23 #include "access/htup_details.h"
24 #include "access/sysattr.h"
25 #include "catalog/dependency.h"
26 #include "catalog/indexing.h"
27 #include "catalog/objectaccess.h"
28 #include "catalog/opfam_internal.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/alter.h"
39 #include "commands/defrem.h"
40 #include "commands/event_trigger.h"
41 #include "miscadmin.h"
42 #include "parser/parse_func.h"
43 #include "parser/parse_oper.h"
44 #include "parser/parse_type.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 #include "utils/tqual.h"
51 
52 
53 static void AlterOpFamilyAdd(AlterOpFamilyStmt *stmt,
54  Oid amoid, Oid opfamilyoid,
55  int maxOpNumber, int maxProcNumber,
56  List *items);
57 static void AlterOpFamilyDrop(AlterOpFamilyStmt *stmt,
58  Oid amoid, Oid opfamilyoid,
59  int maxOpNumber, int maxProcNumber,
60  List *items);
61 static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
62 static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
63 static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
64 static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc);
65 static void storeOperators(List *opfamilyname, Oid amoid,
66  Oid opfamilyoid, Oid opclassoid,
67  List *operators, bool isAdd);
68 static void storeProcedures(List *opfamilyname, Oid amoid,
69  Oid opfamilyoid, Oid opclassoid,
70  List *procedures, bool isAdd);
71 static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
72  List *operators);
73 static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
74  List *procedures);
75 
76 /*
77  * OpFamilyCacheLookup
78  * Look up an existing opfamily by name.
79  *
80  * Returns a syscache tuple reference, or NULL if not found.
81  */
82 static HeapTuple
83 OpFamilyCacheLookup(Oid amID, List *opfamilyname, bool missing_ok)
84 {
85  char *schemaname;
86  char *opfname;
87  HeapTuple htup;
88 
89  /* deconstruct the name list */
90  DeconstructQualifiedName(opfamilyname, &schemaname, &opfname);
91 
92  if (schemaname)
93  {
94  /* Look in specific schema only */
95  Oid namespaceId;
96 
97  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
98  if (!OidIsValid(namespaceId))
99  htup = NULL;
100  else
102  ObjectIdGetDatum(amID),
103  PointerGetDatum(opfname),
104  ObjectIdGetDatum(namespaceId));
105  }
106  else
107  {
108  /* Unqualified opfamily name, so search the search path */
109  Oid opfID = OpfamilynameGetOpfid(amID, opfname);
110 
111  if (!OidIsValid(opfID))
112  htup = NULL;
113  else
115  }
116 
117  if (!HeapTupleIsValid(htup) && !missing_ok)
118  {
119  HeapTuple amtup;
120 
121  amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
122  if (!HeapTupleIsValid(amtup))
123  elog(ERROR, "cache lookup failed for access method %u", amID);
124  ereport(ERROR,
125  (errcode(ERRCODE_UNDEFINED_OBJECT),
126  errmsg("operator family \"%s\" does not exist for access method \"%s\"",
127  NameListToString(opfamilyname),
128  NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
129  }
130 
131  return htup;
132 }
133 
134 /*
135  * get_opfamily_oid
136  * find an opfamily OID by possibly qualified name
137  *
138  * If not found, returns InvalidOid if missing_ok, else throws error.
139  */
140 Oid
141 get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok)
142 {
143  HeapTuple htup;
144  Oid opfID;
145 
146  htup = OpFamilyCacheLookup(amID, opfamilyname, missing_ok);
147  if (!HeapTupleIsValid(htup))
148  return InvalidOid;
149  opfID = HeapTupleGetOid(htup);
150  ReleaseSysCache(htup);
151 
152  return opfID;
153 }
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 {
164  char *schemaname;
165  char *opcname;
166  HeapTuple htup;
167 
168  /* deconstruct the name list */
169  DeconstructQualifiedName(opclassname, &schemaname, &opcname);
170 
171  if (schemaname)
172  {
173  /* Look in specific schema only */
174  Oid namespaceId;
175 
176  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
177  if (!OidIsValid(namespaceId))
178  htup = NULL;
179  else
181  ObjectIdGetDatum(amID),
182  PointerGetDatum(opcname),
183  ObjectIdGetDatum(namespaceId));
184  }
185  else
186  {
187  /* Unqualified opclass name, so search the search path */
188  Oid opcID = OpclassnameGetOpcid(amID, opcname);
189 
190  if (!OidIsValid(opcID))
191  htup = NULL;
192  else
193  htup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcID));
194  }
195 
196  if (!HeapTupleIsValid(htup) && !missing_ok)
197  {
198  HeapTuple amtup;
199 
200  amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
201  if (!HeapTupleIsValid(amtup))
202  elog(ERROR, "cache lookup failed for access method %u", amID);
203  ereport(ERROR,
204  (errcode(ERRCODE_UNDEFINED_OBJECT),
205  errmsg("operator class \"%s\" does not exist for access method \"%s\"",
206  NameListToString(opclassname),
207  NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
208  }
209 
210  return htup;
211 }
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 {
222  HeapTuple htup;
223  Oid opcID;
224 
225  htup = OpClassCacheLookup(amID, opclassname, missing_ok);
226  if (!HeapTupleIsValid(htup))
227  return InvalidOid;
228  opcID = HeapTupleGetOid(htup);
229  ReleaseSysCache(htup);
230 
231  return opcID;
232 }
233 
234 /*
235  * CreateOpFamily
236  * Internal routine to make the catalog entry for a new operator family.
237  *
238  * Caller must have done permissions checks etc. already.
239  */
240 static ObjectAddress
241 CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
242 {
243  Oid opfamilyoid;
244  Relation rel;
245  HeapTuple tup;
247  bool nulls[Natts_pg_opfamily];
248  NameData opfName;
249  ObjectAddress myself,
250  referenced;
251 
253 
254  /*
255  * Make sure there is no existing opfamily of this name (this is just to
256  * give a more friendly error message than "duplicate key").
257  */
259  ObjectIdGetDatum(amoid),
260  CStringGetDatum(opfname),
261  ObjectIdGetDatum(namespaceoid)))
262  ereport(ERROR,
264  errmsg("operator family \"%s\" for access method \"%s\" already exists",
265  opfname, amname)));
266 
267  /*
268  * Okay, let's create the pg_opfamily entry.
269  */
270  memset(values, 0, sizeof(values));
271  memset(nulls, false, sizeof(nulls));
272 
273  values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
274  namestrcpy(&opfName, opfname);
275  values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
276  values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
278 
279  tup = heap_form_tuple(rel->rd_att, values, nulls);
280 
281  opfamilyoid = CatalogTupleInsert(rel, tup);
282 
283  heap_freetuple(tup);
284 
285  /*
286  * Create dependencies for the opfamily proper.
287  */
289  myself.objectId = opfamilyoid;
290  myself.objectSubId = 0;
291 
292  /* dependency on access method */
293  referenced.classId = AccessMethodRelationId;
294  referenced.objectId = amoid;
295  referenced.objectSubId = 0;
296  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
297 
298  /* dependency on namespace */
299  referenced.classId = NamespaceRelationId;
300  referenced.objectId = namespaceoid;
301  referenced.objectSubId = 0;
302  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
303 
304  /* dependency on owner */
306 
307  /* dependency on extension */
308  recordDependencyOnCurrentExtension(&myself, false);
309 
310  /* Post creation hook for new operator family */
312 
314 
315  return myself;
316 }
317 
318 /*
319  * DefineOpClass
320  * Define a new index operator class.
321  */
324 {
325  char *opcname; /* name of opclass we're creating */
326  Oid amoid, /* our AM's oid */
327  typeoid, /* indexable datatype oid */
328  storageoid, /* storage datatype oid, if any */
329  namespaceoid, /* namespace to create opclass in */
330  opfamilyoid, /* oid of containing opfamily */
331  opclassoid; /* oid of opclass we create */
332  int maxOpNumber, /* amstrategies value */
333  maxProcNumber; /* amsupport value */
334  bool amstorage; /* amstorage flag */
335  List *operators; /* OpFamilyMember list for operators */
336  List *procedures; /* OpFamilyMember list for support procs */
337  ListCell *l;
338  Relation rel;
339  HeapTuple tup;
340  IndexAmRoutine *amroutine;
342  bool nulls[Natts_pg_opclass];
343  AclResult aclresult;
344  NameData opcName;
345  ObjectAddress myself,
346  referenced;
347 
348  /* Convert list of names to a name and namespace */
349  namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname,
350  &opcname);
351 
352  /* Check we have creation rights in target namespace */
353  aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
354  if (aclresult != ACLCHECK_OK)
356  get_namespace_name(namespaceoid));
357 
358  /* Get necessary info about access method */
360  if (!HeapTupleIsValid(tup))
361  ereport(ERROR,
362  (errcode(ERRCODE_UNDEFINED_OBJECT),
363  errmsg("access method \"%s\" does not exist",
364  stmt->amname)));
365 
366  amoid = HeapTupleGetOid(tup);
367  amroutine = GetIndexAmRoutineByAmId(amoid, false);
368  ReleaseSysCache(tup);
369 
370  maxOpNumber = amroutine->amstrategies;
371  /* if amstrategies is zero, just enforce that op numbers fit in int16 */
372  if (maxOpNumber <= 0)
373  maxOpNumber = SHRT_MAX;
374  maxProcNumber = amroutine->amsupport;
375  amstorage = amroutine->amstorage;
376 
377  /* XXX Should we make any privilege check against the AM? */
378 
379  /*
380  * The question of appropriate permissions for CREATE OPERATOR CLASS is
381  * interesting. Creating an opclass is tantamount to granting public
382  * execute access on the functions involved, since the index machinery
383  * generally does not check access permission before using the functions.
384  * A minimum expectation therefore is that the caller have execute
385  * privilege with grant option. Since we don't have a way to make the
386  * opclass go away if the grant option is revoked, we choose instead to
387  * require ownership of the functions. It's also not entirely clear what
388  * permissions should be required on the datatype, but ownership seems
389  * like a safe choice.
390  *
391  * Currently, we require superuser privileges to create an opclass. This
392  * seems necessary because we have no way to validate that the offered set
393  * of operators and functions are consistent with the AM's expectations.
394  * It would be nice to provide such a check someday, if it can be done
395  * without solving the halting problem :-(
396  *
397  * XXX re-enable NOT_USED code sections below if you remove this test.
398  */
399  if (!superuser())
400  ereport(ERROR,
401  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
402  errmsg("must be superuser to create an operator class")));
403 
404  /* Look up the datatype */
405  typeoid = typenameTypeId(NULL, stmt->datatype);
406 
407 #ifdef NOT_USED
408  /* XXX this is unnecessary given the superuser check above */
409  /* Check we have ownership of the datatype */
410  if (!pg_type_ownercheck(typeoid, GetUserId()))
412 #endif
413 
414  /*
415  * Look up the containing operator family, or create one if FAMILY option
416  * was omitted and there's not a match already.
417  */
418  if (stmt->opfamilyname)
419  {
420  opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
421  }
422  else
423  {
424  /* Lookup existing family of same name and namespace */
426  ObjectIdGetDatum(amoid),
427  PointerGetDatum(opcname),
428  ObjectIdGetDatum(namespaceoid));
429  if (HeapTupleIsValid(tup))
430  {
431  opfamilyoid = HeapTupleGetOid(tup);
432 
433  /*
434  * XXX given the superuser check above, there's no need for an
435  * ownership check here
436  */
437  ReleaseSysCache(tup);
438  }
439  else
440  {
441  ObjectAddress tmpAddr;
442 
443  /*
444  * Create it ... again no need for more permissions ...
445  */
446  tmpAddr = CreateOpFamily(stmt->amname, opcname,
447  namespaceoid, amoid);
448  opfamilyoid = tmpAddr.objectId;
449  }
450  }
451 
452  operators = NIL;
453  procedures = NIL;
454 
455  /* Storage datatype is optional */
456  storageoid = InvalidOid;
457 
458  /*
459  * Scan the "items" list to obtain additional info.
460  */
461  foreach(l, stmt->items)
462  {
464  Oid operOid;
465  Oid funcOid;
466  Oid sortfamilyOid;
467  OpFamilyMember *member;
468 
469  switch (item->itemtype)
470  {
472  if (item->number <= 0 || item->number > maxOpNumber)
473  ereport(ERROR,
474  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
475  errmsg("invalid operator number %d,"
476  " must be between 1 and %d",
477  item->number, maxOpNumber)));
478  if (item->name->objargs != NIL)
479  operOid = LookupOperWithArgs(item->name, false);
480  else
481  {
482  /* Default to binary op on input datatype */
483  operOid = LookupOperName(NULL, item->name->objname,
484  typeoid, typeoid,
485  false, -1);
486  }
487 
488  if (item->order_family)
489  sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
490  item->order_family,
491  false);
492  else
493  sortfamilyOid = InvalidOid;
494 
495 #ifdef NOT_USED
496  /* XXX this is unnecessary given the superuser check above */
497  /* Caller must own operator and its underlying function */
498  if (!pg_oper_ownercheck(operOid, GetUserId()))
500  get_opname(operOid));
501  funcOid = get_opcode(operOid);
502  if (!pg_proc_ownercheck(funcOid, GetUserId()))
504  get_func_name(funcOid));
505 #endif
506 
507  /* Save the info */
508  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
509  member->object = operOid;
510  member->number = item->number;
511  member->sortfamily = sortfamilyOid;
512  assignOperTypes(member, amoid, typeoid);
513  addFamilyMember(&operators, member, false);
514  break;
516  if (item->number <= 0 || item->number > maxProcNumber)
517  ereport(ERROR,
518  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
519  errmsg("invalid procedure number %d,"
520  " must be between 1 and %d",
521  item->number, maxProcNumber)));
522  funcOid = LookupFuncWithArgs(item->name, false);
523 #ifdef NOT_USED
524  /* XXX this is unnecessary given the superuser check above */
525  /* Caller must own function */
526  if (!pg_proc_ownercheck(funcOid, GetUserId()))
528  get_func_name(funcOid));
529 #endif
530 
531  /* Save the info */
532  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
533  member->object = funcOid;
534  member->number = item->number;
535 
536  /* allow overriding of the function's actual arg types */
537  if (item->class_args)
539  &member->lefttype, &member->righttype);
540 
541  assignProcTypes(member, amoid, typeoid);
542  addFamilyMember(&procedures, member, true);
543  break;
545  if (OidIsValid(storageoid))
546  ereport(ERROR,
547  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
548  errmsg("storage type specified more than once")));
549  storageoid = typenameTypeId(NULL, item->storedtype);
550 
551 #ifdef NOT_USED
552  /* XXX this is unnecessary given the superuser check above */
553  /* Check we have ownership of the datatype */
554  if (!pg_type_ownercheck(storageoid, GetUserId()))
556 #endif
557  break;
558  default:
559  elog(ERROR, "unrecognized item type: %d", item->itemtype);
560  break;
561  }
562  }
563 
564  /*
565  * If storagetype is specified, make sure it's legal.
566  */
567  if (OidIsValid(storageoid))
568  {
569  /* Just drop the spec if same as column datatype */
570  if (storageoid == typeoid)
571  storageoid = InvalidOid;
572  else if (!amstorage)
573  ereport(ERROR,
574  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
575  errmsg("storage type cannot be different from data type for access method \"%s\"",
576  stmt->amname)));
577  }
578 
580 
581  /*
582  * Make sure there is no existing opclass of this name (this is just to
583  * give a more friendly error message than "duplicate key").
584  */
586  ObjectIdGetDatum(amoid),
587  CStringGetDatum(opcname),
588  ObjectIdGetDatum(namespaceoid)))
589  ereport(ERROR,
591  errmsg("operator class \"%s\" for access method \"%s\" already exists",
592  opcname, stmt->amname)));
593 
594  /*
595  * If we are creating a default opclass, check there isn't one already.
596  * (Note we do not restrict this test to visible opclasses; this ensures
597  * that typcache.c can find unique solutions to its questions.)
598  */
599  if (stmt->isDefault)
600  {
601  ScanKeyData skey[1];
602  SysScanDesc scan;
603 
604  ScanKeyInit(&skey[0],
606  BTEqualStrategyNumber, F_OIDEQ,
607  ObjectIdGetDatum(amoid));
608 
610  NULL, 1, skey);
611 
612  while (HeapTupleIsValid(tup = systable_getnext(scan)))
613  {
614  Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
615 
616  if (opclass->opcintype == typeoid && opclass->opcdefault)
617  ereport(ERROR,
619  errmsg("could not make operator class \"%s\" be default for type %s",
620  opcname,
621  TypeNameToString(stmt->datatype)),
622  errdetail("Operator class \"%s\" already is the default.",
623  NameStr(opclass->opcname))));
624  }
625 
626  systable_endscan(scan);
627  }
628 
629  /*
630  * Okay, let's create the pg_opclass entry.
631  */
632  memset(values, 0, sizeof(values));
633  memset(nulls, false, sizeof(nulls));
634 
635  values[Anum_pg_opclass_opcmethod - 1] = ObjectIdGetDatum(amoid);
636  namestrcpy(&opcName, opcname);
637  values[Anum_pg_opclass_opcname - 1] = NameGetDatum(&opcName);
638  values[Anum_pg_opclass_opcnamespace - 1] = ObjectIdGetDatum(namespaceoid);
640  values[Anum_pg_opclass_opcfamily - 1] = ObjectIdGetDatum(opfamilyoid);
641  values[Anum_pg_opclass_opcintype - 1] = ObjectIdGetDatum(typeoid);
643  values[Anum_pg_opclass_opckeytype - 1] = ObjectIdGetDatum(storageoid);
644 
645  tup = heap_form_tuple(rel->rd_att, values, nulls);
646 
647  opclassoid = CatalogTupleInsert(rel, tup);
648 
649  heap_freetuple(tup);
650 
651  /*
652  * Now add tuples to pg_amop and pg_amproc tying in the operators and
653  * functions. Dependencies on them are inserted, too.
654  */
655  storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
656  opclassoid, operators, false);
657  storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
658  opclassoid, procedures, false);
659 
660  /* let event triggers know what happened */
661  EventTriggerCollectCreateOpClass(stmt, opclassoid, operators, procedures);
662 
663  /*
664  * Create dependencies for the opclass proper. Note: we do not need a
665  * dependency link to the AM, because that exists through the opfamily.
666  */
668  myself.objectId = opclassoid;
669  myself.objectSubId = 0;
670 
671  /* dependency on namespace */
672  referenced.classId = NamespaceRelationId;
673  referenced.objectId = namespaceoid;
674  referenced.objectSubId = 0;
675  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
676 
677  /* dependency on opfamily */
678  referenced.classId = OperatorFamilyRelationId;
679  referenced.objectId = opfamilyoid;
680  referenced.objectSubId = 0;
681  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
682 
683  /* dependency on indexed datatype */
684  referenced.classId = TypeRelationId;
685  referenced.objectId = typeoid;
686  referenced.objectSubId = 0;
687  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
688 
689  /* dependency on storage datatype */
690  if (OidIsValid(storageoid))
691  {
692  referenced.classId = TypeRelationId;
693  referenced.objectId = storageoid;
694  referenced.objectSubId = 0;
695  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
696  }
697 
698  /* dependency on owner */
700 
701  /* dependency on extension */
702  recordDependencyOnCurrentExtension(&myself, false);
703 
704  /* Post creation hook for new operator class */
706 
708 
709  return myself;
710 }
711 
712 
713 /*
714  * DefineOpFamily
715  * Define a new index operator family.
716  */
719 {
720  char *opfname; /* name of opfamily we're creating */
721  Oid amoid, /* our AM's oid */
722  namespaceoid; /* namespace to create opfamily in */
723  AclResult aclresult;
724 
725  /* Convert list of names to a name and namespace */
726  namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
727  &opfname);
728 
729  /* Check we have creation rights in target namespace */
730  aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
731  if (aclresult != ACLCHECK_OK)
733  get_namespace_name(namespaceoid));
734 
735  /* Get access method OID, throwing an error if it doesn't exist. */
736  amoid = get_index_am_oid(stmt->amname, false);
737 
738  /* XXX Should we make any privilege check against the AM? */
739 
740  /*
741  * Currently, we require superuser privileges to create an opfamily. See
742  * comments in DefineOpClass.
743  */
744  if (!superuser())
745  ereport(ERROR,
746  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
747  errmsg("must be superuser to create an operator family")));
748 
749  /* Insert pg_opfamily catalog entry */
750  return CreateOpFamily(stmt->amname, opfname, namespaceoid, amoid);
751 }
752 
753 
754 /*
755  * AlterOpFamily
756  * Add or remove operators/procedures within an existing operator family.
757  *
758  * Note: this implements only ALTER OPERATOR FAMILY ... ADD/DROP. Some
759  * other commands called ALTER OPERATOR FAMILY exist, but go through
760  * different code paths.
761  */
762 Oid
764 {
765  Oid amoid, /* our AM's oid */
766  opfamilyoid; /* oid of opfamily */
767  int maxOpNumber, /* amstrategies value */
768  maxProcNumber; /* amsupport value */
769  HeapTuple tup;
770  IndexAmRoutine *amroutine;
771 
772  /* Get necessary info about access method */
774  if (!HeapTupleIsValid(tup))
775  ereport(ERROR,
776  (errcode(ERRCODE_UNDEFINED_OBJECT),
777  errmsg("access method \"%s\" does not exist",
778  stmt->amname)));
779 
780  amoid = HeapTupleGetOid(tup);
781  amroutine = GetIndexAmRoutineByAmId(amoid, false);
782  ReleaseSysCache(tup);
783 
784  maxOpNumber = amroutine->amstrategies;
785  /* if amstrategies is zero, just enforce that op numbers fit in int16 */
786  if (maxOpNumber <= 0)
787  maxOpNumber = SHRT_MAX;
788  maxProcNumber = amroutine->amsupport;
789 
790  /* XXX Should we make any privilege check against the AM? */
791 
792  /* Look up the opfamily */
793  opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
794 
795  /*
796  * Currently, we require superuser privileges to alter an opfamily.
797  *
798  * XXX re-enable NOT_USED code sections below if you remove this test.
799  */
800  if (!superuser())
801  ereport(ERROR,
802  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
803  errmsg("must be superuser to alter an operator family")));
804 
805  /*
806  * ADD and DROP cases need separate code from here on down.
807  */
808  if (stmt->isDrop)
809  AlterOpFamilyDrop(stmt, amoid, opfamilyoid,
810  maxOpNumber, maxProcNumber, stmt->items);
811  else
812  AlterOpFamilyAdd(stmt, amoid, opfamilyoid,
813  maxOpNumber, maxProcNumber, stmt->items);
814 
815  return opfamilyoid;
816 }
817 
818 /*
819  * ADD part of ALTER OP FAMILY
820  */
821 static void
822 AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
823  int maxOpNumber, int maxProcNumber, List *items)
824 {
825  List *operators; /* OpFamilyMember list for operators */
826  List *procedures; /* OpFamilyMember list for support procs */
827  ListCell *l;
828 
829  operators = NIL;
830  procedures = NIL;
831 
832  /*
833  * Scan the "items" list to obtain additional info.
834  */
835  foreach(l, items)
836  {
838  Oid operOid;
839  Oid funcOid;
840  Oid sortfamilyOid;
841  OpFamilyMember *member;
842 
843  switch (item->itemtype)
844  {
846  if (item->number <= 0 || item->number > maxOpNumber)
847  ereport(ERROR,
848  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
849  errmsg("invalid operator number %d,"
850  " must be between 1 and %d",
851  item->number, maxOpNumber)));
852  if (item->name->objargs != NIL)
853  operOid = LookupOperWithArgs(item->name, false);
854  else
855  {
856  ereport(ERROR,
857  (errcode(ERRCODE_SYNTAX_ERROR),
858  errmsg("operator argument types must be specified in ALTER OPERATOR FAMILY")));
859  operOid = InvalidOid; /* keep compiler quiet */
860  }
861 
862  if (item->order_family)
863  sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
864  item->order_family,
865  false);
866  else
867  sortfamilyOid = InvalidOid;
868 
869 #ifdef NOT_USED
870  /* XXX this is unnecessary given the superuser check above */
871  /* Caller must own operator and its underlying function */
872  if (!pg_oper_ownercheck(operOid, GetUserId()))
874  get_opname(operOid));
875  funcOid = get_opcode(operOid);
876  if (!pg_proc_ownercheck(funcOid, GetUserId()))
878  get_func_name(funcOid));
879 #endif
880 
881  /* Save the info */
882  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
883  member->object = operOid;
884  member->number = item->number;
885  member->sortfamily = sortfamilyOid;
886  assignOperTypes(member, amoid, InvalidOid);
887  addFamilyMember(&operators, member, false);
888  break;
890  if (item->number <= 0 || item->number > maxProcNumber)
891  ereport(ERROR,
892  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
893  errmsg("invalid procedure number %d,"
894  " must be between 1 and %d",
895  item->number, maxProcNumber)));
896  funcOid = LookupFuncWithArgs(item->name, false);
897 #ifdef NOT_USED
898  /* XXX this is unnecessary given the superuser check above */
899  /* Caller must own function */
900  if (!pg_proc_ownercheck(funcOid, GetUserId()))
902  get_func_name(funcOid));
903 #endif
904 
905  /* Save the info */
906  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
907  member->object = funcOid;
908  member->number = item->number;
909 
910  /* allow overriding of the function's actual arg types */
911  if (item->class_args)
913  &member->lefttype, &member->righttype);
914 
915  assignProcTypes(member, amoid, InvalidOid);
916  addFamilyMember(&procedures, member, true);
917  break;
919  ereport(ERROR,
920  (errcode(ERRCODE_SYNTAX_ERROR),
921  errmsg("STORAGE cannot be specified in ALTER OPERATOR FAMILY")));
922  break;
923  default:
924  elog(ERROR, "unrecognized item type: %d", item->itemtype);
925  break;
926  }
927  }
928 
929  /*
930  * Add tuples to pg_amop and pg_amproc tying in the operators and
931  * functions. Dependencies on them are inserted, too.
932  */
933  storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
934  InvalidOid, operators, true);
935  storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
936  InvalidOid, procedures, true);
937 
938  /* make information available to event triggers */
939  EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
940  operators, procedures);
941 }
942 
943 /*
944  * DROP part of ALTER OP FAMILY
945  */
946 static void
947 AlterOpFamilyDrop(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
948  int maxOpNumber, int maxProcNumber, List *items)
949 {
950  List *operators; /* OpFamilyMember list for operators */
951  List *procedures; /* OpFamilyMember list for support procs */
952  ListCell *l;
953 
954  operators = NIL;
955  procedures = NIL;
956 
957  /*
958  * Scan the "items" list to obtain additional info.
959  */
960  foreach(l, items)
961  {
963  Oid lefttype,
964  righttype;
965  OpFamilyMember *member;
966 
967  switch (item->itemtype)
968  {
970  if (item->number <= 0 || item->number > maxOpNumber)
971  ereport(ERROR,
972  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
973  errmsg("invalid operator number %d,"
974  " must be between 1 and %d",
975  item->number, maxOpNumber)));
976  processTypesSpec(item->class_args, &lefttype, &righttype);
977  /* Save the info */
978  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
979  member->number = item->number;
980  member->lefttype = lefttype;
981  member->righttype = righttype;
982  addFamilyMember(&operators, member, false);
983  break;
985  if (item->number <= 0 || item->number > maxProcNumber)
986  ereport(ERROR,
987  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
988  errmsg("invalid procedure number %d,"
989  " must be between 1 and %d",
990  item->number, maxProcNumber)));
991  processTypesSpec(item->class_args, &lefttype, &righttype);
992  /* Save the info */
993  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
994  member->number = item->number;
995  member->lefttype = lefttype;
996  member->righttype = righttype;
997  addFamilyMember(&procedures, member, true);
998  break;
1000  /* grammar prevents this from appearing */
1001  default:
1002  elog(ERROR, "unrecognized item type: %d", item->itemtype);
1003  break;
1004  }
1005  }
1006 
1007  /*
1008  * Remove tuples from pg_amop and pg_amproc.
1009  */
1010  dropOperators(stmt->opfamilyname, amoid, opfamilyoid, operators);
1011  dropProcedures(stmt->opfamilyname, amoid, opfamilyoid, procedures);
1012 
1013  /* make information available to event triggers */
1014  EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
1015  operators, procedures);
1016 }
1017 
1018 
1019 /*
1020  * Deal with explicit arg types used in ALTER ADD/DROP
1021  */
1022 static void
1023 processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
1024 {
1025  TypeName *typeName;
1026 
1027  Assert(args != NIL);
1028 
1029  typeName = (TypeName *) linitial(args);
1030  *lefttype = typenameTypeId(NULL, typeName);
1031 
1032  if (list_length(args) > 1)
1033  {
1034  typeName = (TypeName *) lsecond(args);
1035  *righttype = typenameTypeId(NULL, typeName);
1036  }
1037  else
1038  *righttype = *lefttype;
1039 
1040  if (list_length(args) > 2)
1041  ereport(ERROR,
1042  (errcode(ERRCODE_SYNTAX_ERROR),
1043  errmsg("one or two argument types must be specified")));
1044 }
1045 
1046 
1047 /*
1048  * Determine the lefttype/righttype to assign to an operator,
1049  * and do any validity checking we can manage.
1050  */
1051 static void
1052 assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
1053 {
1054  Operator optup;
1055  Form_pg_operator opform;
1056 
1057  /* Fetch the operator definition */
1058  optup = SearchSysCache1(OPEROID, ObjectIdGetDatum(member->object));
1059  if (optup == NULL)
1060  elog(ERROR, "cache lookup failed for operator %u", member->object);
1061  opform = (Form_pg_operator) GETSTRUCT(optup);
1062 
1063  /*
1064  * Opfamily operators must be binary.
1065  */
1066  if (opform->oprkind != 'b')
1067  ereport(ERROR,
1068  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1069  errmsg("index operators must be binary")));
1070 
1071  if (OidIsValid(member->sortfamily))
1072  {
1073  /*
1074  * Ordering op, check index supports that. (We could perhaps also
1075  * check that the operator returns a type supported by the sortfamily,
1076  * but that seems more trouble than it's worth here. If it does not,
1077  * the operator will never be matchable to any ORDER BY clause, but no
1078  * worse consequences can ensue. Also, trying to check that would
1079  * create an ordering hazard during dump/reload: it's possible that
1080  * the family has been created but not yet populated with the required
1081  * operators.)
1082  */
1083  IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
1084 
1085  if (!amroutine->amcanorderbyop)
1086  ereport(ERROR,
1087  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1088  errmsg("access method \"%s\" does not support ordering operators",
1089  get_am_name(amoid))));
1090  }
1091  else
1092  {
1093  /*
1094  * Search operators must return boolean.
1095  */
1096  if (opform->oprresult != BOOLOID)
1097  ereport(ERROR,
1098  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1099  errmsg("index search operators must return boolean")));
1100  }
1101 
1102  /*
1103  * If lefttype/righttype isn't specified, use the operator's input types
1104  */
1105  if (!OidIsValid(member->lefttype))
1106  member->lefttype = opform->oprleft;
1107  if (!OidIsValid(member->righttype))
1108  member->righttype = opform->oprright;
1109 
1110  ReleaseSysCache(optup);
1111 }
1112 
1113 /*
1114  * Determine the lefttype/righttype to assign to a support procedure,
1115  * and do any validity checking we can manage.
1116  */
1117 static void
1118 assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
1119 {
1120  HeapTuple proctup;
1121  Form_pg_proc procform;
1122 
1123  /* Fetch the procedure definition */
1124  proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(member->object));
1125  if (proctup == NULL)
1126  elog(ERROR, "cache lookup failed for function %u", member->object);
1127  procform = (Form_pg_proc) GETSTRUCT(proctup);
1128 
1129  /*
1130  * btree comparison procs must be 2-arg procs returning int4, while btree
1131  * sortsupport procs must take internal and return void. hash support
1132  * procs must be 1-arg procs returning int4. Otherwise we don't know.
1133  */
1134  if (amoid == BTREE_AM_OID)
1135  {
1136  if (member->number == BTORDER_PROC)
1137  {
1138  if (procform->pronargs != 2)
1139  ereport(ERROR,
1140  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1141  errmsg("btree comparison procedures must have two arguments")));
1142  if (procform->prorettype != INT4OID)
1143  ereport(ERROR,
1144  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1145  errmsg("btree comparison procedures must return integer")));
1146 
1147  /*
1148  * If lefttype/righttype isn't specified, use the proc's input
1149  * types
1150  */
1151  if (!OidIsValid(member->lefttype))
1152  member->lefttype = procform->proargtypes.values[0];
1153  if (!OidIsValid(member->righttype))
1154  member->righttype = procform->proargtypes.values[1];
1155  }
1156  else if (member->number == BTSORTSUPPORT_PROC)
1157  {
1158  if (procform->pronargs != 1 ||
1159  procform->proargtypes.values[0] != INTERNALOID)
1160  ereport(ERROR,
1161  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1162  errmsg("btree sort support procedures must accept type \"internal\"")));
1163  if (procform->prorettype != VOIDOID)
1164  ereport(ERROR,
1165  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1166  errmsg("btree sort support procedures must return void")));
1167 
1168  /*
1169  * Can't infer lefttype/righttype from proc, so use default rule
1170  */
1171  }
1172  }
1173  else if (amoid == HASH_AM_OID)
1174  {
1175  if (procform->pronargs != 1)
1176  ereport(ERROR,
1177  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1178  errmsg("hash procedures must have one argument")));
1179  if (procform->prorettype != INT4OID)
1180  ereport(ERROR,
1181  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1182  errmsg("hash procedures must return integer")));
1183 
1184  /*
1185  * If lefttype/righttype isn't specified, use the proc's input type
1186  */
1187  if (!OidIsValid(member->lefttype))
1188  member->lefttype = procform->proargtypes.values[0];
1189  if (!OidIsValid(member->righttype))
1190  member->righttype = procform->proargtypes.values[0];
1191  }
1192 
1193  /*
1194  * The default in CREATE OPERATOR CLASS is to use the class' opcintype as
1195  * lefttype and righttype. In CREATE or ALTER OPERATOR FAMILY, opcintype
1196  * isn't available, so make the user specify the types.
1197  */
1198  if (!OidIsValid(member->lefttype))
1199  member->lefttype = typeoid;
1200  if (!OidIsValid(member->righttype))
1201  member->righttype = typeoid;
1202 
1203  if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
1204  ereport(ERROR,
1205  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1206  errmsg("associated data types must be specified for index support procedure")));
1207 
1208  ReleaseSysCache(proctup);
1209 }
1210 
1211 /*
1212  * Add a new family member to the appropriate list, after checking for
1213  * duplicated strategy or proc number.
1214  */
1215 static void
1216 addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
1217 {
1218  ListCell *l;
1219 
1220  foreach(l, *list)
1221  {
1222  OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
1223 
1224  if (old->number == member->number &&
1225  old->lefttype == member->lefttype &&
1226  old->righttype == member->righttype)
1227  {
1228  if (isProc)
1229  ereport(ERROR,
1230  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1231  errmsg("procedure number %d for (%s,%s) appears more than once",
1232  member->number,
1233  format_type_be(member->lefttype),
1234  format_type_be(member->righttype))));
1235  else
1236  ereport(ERROR,
1237  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1238  errmsg("operator number %d for (%s,%s) appears more than once",
1239  member->number,
1240  format_type_be(member->lefttype),
1241  format_type_be(member->righttype))));
1242  }
1243  }
1244  *list = lappend(*list, member);
1245 }
1246 
1247 /*
1248  * Dump the operators to pg_amop
1249  *
1250  * We also make dependency entries in pg_depend for the opfamily entries.
1251  * If opclassoid is valid then make an INTERNAL dependency on that opclass,
1252  * else make an AUTO dependency on the opfamily.
1253  */
1254 static void
1255 storeOperators(List *opfamilyname, Oid amoid,
1256  Oid opfamilyoid, Oid opclassoid,
1257  List *operators, bool isAdd)
1258 {
1259  Relation rel;
1261  bool nulls[Natts_pg_amop];
1262  HeapTuple tup;
1263  Oid entryoid;
1264  ObjectAddress myself,
1265  referenced;
1266  ListCell *l;
1267 
1269 
1270  foreach(l, operators)
1271  {
1272  OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1273  char oppurpose;
1274 
1275  /*
1276  * If adding to an existing family, check for conflict with an
1277  * existing pg_amop entry (just to give a nicer error message)
1278  */
1279  if (isAdd &&
1281  ObjectIdGetDatum(opfamilyoid),
1284  Int16GetDatum(op->number)))
1285  ereport(ERROR,
1287  errmsg("operator %d(%s,%s) already exists in operator family \"%s\"",
1288  op->number,
1289  format_type_be(op->lefttype),
1291  NameListToString(opfamilyname))));
1292 
1293  oppurpose = OidIsValid(op->sortfamily) ? AMOP_ORDER : AMOP_SEARCH;
1294 
1295  /* Create the pg_amop entry */
1296  memset(values, 0, sizeof(values));
1297  memset(nulls, false, sizeof(nulls));
1298 
1299  values[Anum_pg_amop_amopfamily - 1] = ObjectIdGetDatum(opfamilyoid);
1302  values[Anum_pg_amop_amopstrategy - 1] = Int16GetDatum(op->number);
1303  values[Anum_pg_amop_amoppurpose - 1] = CharGetDatum(oppurpose);
1304  values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
1305  values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);
1307 
1308  tup = heap_form_tuple(rel->rd_att, values, nulls);
1309 
1310  entryoid = CatalogTupleInsert(rel, tup);
1311 
1312  heap_freetuple(tup);
1313 
1314  /* Make its dependencies */
1316  myself.objectId = entryoid;
1317  myself.objectSubId = 0;
1318 
1319  referenced.classId = OperatorRelationId;
1320  referenced.objectId = op->object;
1321  referenced.objectSubId = 0;
1322 
1323  if (OidIsValid(opclassoid))
1324  {
1325  /* if contained in an opclass, use a NORMAL dep on operator */
1326  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1327 
1328  /* ... and an INTERNAL dep on the opclass */
1329  referenced.classId = OperatorClassRelationId;
1330  referenced.objectId = opclassoid;
1331  referenced.objectSubId = 0;
1332  recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1333  }
1334  else
1335  {
1336  /* if "loose" in the opfamily, use a AUTO dep on operator */
1337  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1338 
1339  /* ... and an AUTO dep on the opfamily */
1340  referenced.classId = OperatorFamilyRelationId;
1341  referenced.objectId = opfamilyoid;
1342  referenced.objectSubId = 0;
1343  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1344  }
1345 
1346  /* A search operator also needs a dep on the referenced opfamily */
1347  if (OidIsValid(op->sortfamily))
1348  {
1349  referenced.classId = OperatorFamilyRelationId;
1350  referenced.objectId = op->sortfamily;
1351  referenced.objectSubId = 0;
1352  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1353  }
1354  /* Post create hook of this access method operator */
1356  entryoid, 0);
1357  }
1358 
1360 }
1361 
1362 /*
1363  * Dump the procedures (support routines) to pg_amproc
1364  *
1365  * We also make dependency entries in pg_depend for the opfamily entries.
1366  * If opclassoid is valid then make an INTERNAL dependency on that opclass,
1367  * else make an AUTO dependency on the opfamily.
1368  */
1369 static void
1370 storeProcedures(List *opfamilyname, Oid amoid,
1371  Oid opfamilyoid, Oid opclassoid,
1372  List *procedures, bool isAdd)
1373 {
1374  Relation rel;
1376  bool nulls[Natts_pg_amproc];
1377  HeapTuple tup;
1378  Oid entryoid;
1379  ObjectAddress myself,
1380  referenced;
1381  ListCell *l;
1382 
1384 
1385  foreach(l, procedures)
1386  {
1387  OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
1388 
1389  /*
1390  * If adding to an existing family, check for conflict with an
1391  * existing pg_amproc entry (just to give a nicer error message)
1392  */
1393  if (isAdd &&
1395  ObjectIdGetDatum(opfamilyoid),
1396  ObjectIdGetDatum(proc->lefttype),
1397  ObjectIdGetDatum(proc->righttype),
1398  Int16GetDatum(proc->number)))
1399  ereport(ERROR,
1401  errmsg("function %d(%s,%s) already exists in operator family \"%s\"",
1402  proc->number,
1403  format_type_be(proc->lefttype),
1404  format_type_be(proc->righttype),
1405  NameListToString(opfamilyname))));
1406 
1407  /* Create the pg_amproc entry */
1408  memset(values, 0, sizeof(values));
1409  memset(nulls, false, sizeof(nulls));
1410 
1411  values[Anum_pg_amproc_amprocfamily - 1] = ObjectIdGetDatum(opfamilyoid);
1414  values[Anum_pg_amproc_amprocnum - 1] = Int16GetDatum(proc->number);
1415  values[Anum_pg_amproc_amproc - 1] = ObjectIdGetDatum(proc->object);
1416 
1417  tup = heap_form_tuple(rel->rd_att, values, nulls);
1418 
1419  entryoid = CatalogTupleInsert(rel, tup);
1420 
1421  heap_freetuple(tup);
1422 
1423  /* Make its dependencies */
1425  myself.objectId = entryoid;
1426  myself.objectSubId = 0;
1427 
1428  referenced.classId = ProcedureRelationId;
1429  referenced.objectId = proc->object;
1430  referenced.objectSubId = 0;
1431 
1432  if (OidIsValid(opclassoid))
1433  {
1434  /* if contained in an opclass, use a NORMAL dep on procedure */
1435  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1436 
1437  /* ... and an INTERNAL dep on the opclass */
1438  referenced.classId = OperatorClassRelationId;
1439  referenced.objectId = opclassoid;
1440  referenced.objectSubId = 0;
1441  recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1442  }
1443  else
1444  {
1445  /* if "loose" in the opfamily, use a AUTO dep on procedure */
1446  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1447 
1448  /* ... and an AUTO dep on the opfamily */
1449  referenced.classId = OperatorFamilyRelationId;
1450  referenced.objectId = opfamilyoid;
1451  referenced.objectSubId = 0;
1452  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1453  }
1454  /* Post create hook of access method procedure */
1456  entryoid, 0);
1457  }
1458 
1460 }
1461 
1462 
1463 /*
1464  * Remove operator entries from an opfamily.
1465  *
1466  * Note: this is only allowed for "loose" members of an opfamily, hence
1467  * behavior is always RESTRICT.
1468  */
1469 static void
1470 dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
1471  List *operators)
1472 {
1473  ListCell *l;
1474 
1475  foreach(l, operators)
1476  {
1477  OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1478  Oid amopid;
1479  ObjectAddress object;
1480 
1481  amopid = GetSysCacheOid4(AMOPSTRATEGY,
1482  ObjectIdGetDatum(opfamilyoid),
1485  Int16GetDatum(op->number));
1486  if (!OidIsValid(amopid))
1487  ereport(ERROR,
1488  (errcode(ERRCODE_UNDEFINED_OBJECT),
1489  errmsg("operator %d(%s,%s) does not exist in operator family \"%s\"",
1490  op->number,
1491  format_type_be(op->lefttype),
1493  NameListToString(opfamilyname))));
1494 
1495  object.classId = AccessMethodOperatorRelationId;
1496  object.objectId = amopid;
1497  object.objectSubId = 0;
1498 
1499  performDeletion(&object, DROP_RESTRICT, 0);
1500  }
1501 }
1502 
1503 /*
1504  * Remove procedure entries from an opfamily.
1505  *
1506  * Note: this is only allowed for "loose" members of an opfamily, hence
1507  * behavior is always RESTRICT.
1508  */
1509 static void
1510 dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
1511  List *procedures)
1512 {
1513  ListCell *l;
1514 
1515  foreach(l, procedures)
1516  {
1517  OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1518  Oid amprocid;
1519  ObjectAddress object;
1520 
1521  amprocid = GetSysCacheOid4(AMPROCNUM,
1522  ObjectIdGetDatum(opfamilyoid),
1525  Int16GetDatum(op->number));
1526  if (!OidIsValid(amprocid))
1527  ereport(ERROR,
1528  (errcode(ERRCODE_UNDEFINED_OBJECT),
1529  errmsg("function %d(%s,%s) does not exist in operator family \"%s\"",
1530  op->number,
1531  format_type_be(op->lefttype),
1533  NameListToString(opfamilyname))));
1534 
1535  object.classId = AccessMethodProcedureRelationId;
1536  object.objectId = amprocid;
1537  object.objectSubId = 0;
1538 
1539  performDeletion(&object, DROP_RESTRICT, 0);
1540  }
1541 }
1542 
1543 /*
1544  * Deletion subroutines for use by dependency.c.
1545  */
1546 void
1548 {
1549  Relation rel;
1550  HeapTuple tup;
1551 
1553 
1554  tup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyOid));
1555  if (!HeapTupleIsValid(tup)) /* should not happen */
1556  elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid);
1557 
1558  CatalogTupleDelete(rel, &tup->t_self);
1559 
1560  ReleaseSysCache(tup);
1561 
1563 }
1564 
1565 void
1567 {
1568  Relation rel;
1569  HeapTuple tup;
1570 
1572 
1573  tup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassOid));
1574  if (!HeapTupleIsValid(tup)) /* should not happen */
1575  elog(ERROR, "cache lookup failed for opclass %u", opclassOid);
1576 
1577  CatalogTupleDelete(rel, &tup->t_self);
1578 
1579  ReleaseSysCache(tup);
1580 
1582 }
1583 
1584 void
1586 {
1587  Relation rel;
1588  HeapTuple tup;
1589  ScanKeyData skey[1];
1590  SysScanDesc scan;
1591 
1592  ScanKeyInit(&skey[0],
1594  BTEqualStrategyNumber, F_OIDEQ,
1595  ObjectIdGetDatum(entryOid));
1596 
1598 
1600  NULL, 1, skey);
1601 
1602  /* we expect exactly one match */
1603  tup = systable_getnext(scan);
1604  if (!HeapTupleIsValid(tup))
1605  elog(ERROR, "could not find tuple for amop entry %u", entryOid);
1606 
1607  CatalogTupleDelete(rel, &tup->t_self);
1608 
1609  systable_endscan(scan);
1611 }
1612 
1613 void
1615 {
1616  Relation rel;
1617  HeapTuple tup;
1618  ScanKeyData skey[1];
1619  SysScanDesc scan;
1620 
1621  ScanKeyInit(&skey[0],
1623  BTEqualStrategyNumber, F_OIDEQ,
1624  ObjectIdGetDatum(entryOid));
1625 
1627 
1629  NULL, 1, skey);
1630 
1631  /* we expect exactly one match */
1632  tup = systable_getnext(scan);
1633  if (!HeapTupleIsValid(tup))
1634  elog(ERROR, "could not find tuple for amproc entry %u", entryOid);
1635 
1636  CatalogTupleDelete(rel, &tup->t_self);
1637 
1638  systable_endscan(scan);
1640 }
1641 
1642 /*
1643  * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
1644  *
1645  * Is there an operator class with the given name and signature already
1646  * in the given namespace? If so, raise an appropriate error message.
1647  */
1648 void
1649 IsThereOpClassInNamespace(const char *opcname, Oid opcmethod,
1650  Oid opcnamespace)
1651 {
1652  /* make sure the new name doesn't exist */
1654  ObjectIdGetDatum(opcmethod),
1655  CStringGetDatum(opcname),
1656  ObjectIdGetDatum(opcnamespace)))
1657  ereport(ERROR,
1659  errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
1660  opcname,
1661  get_am_name(opcmethod),
1662  get_namespace_name(opcnamespace))));
1663 }
1664 
1665 /*
1666  * Subroutine for ALTER OPERATOR FAMILY SET SCHEMA/RENAME
1667  *
1668  * Is there an operator family with the given name and signature already
1669  * in the given namespace? If so, raise an appropriate error message.
1670  */
1671 void
1672 IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
1673  Oid opfnamespace)
1674 {
1675  /* make sure the new name doesn't exist */
1677  ObjectIdGetDatum(opfmethod),
1678  CStringGetDatum(opfname),
1679  ObjectIdGetDatum(opfnamespace)))
1680  ereport(ERROR,
1682  errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
1683  opfname,
1684  get_am_name(opfmethod),
1685  get_namespace_name(opfnamespace))));
1686 }
Oid LookupOperWithArgs(ObjectWithArgs *oper, bool noError)
Definition: parse_oper.c:140
#define NIL
Definition: pg_list.h:69
#define Anum_pg_amop_amopsortfamily
Definition: pg_amop.h:91
#define GetSysCacheOid4(cacheId, key1, key2, key3, key4)
Definition: syscache.h:189
#define NamespaceRelationId
Definition: pg_namespace.h:34
#define NameGetDatum(X)
Definition: postgres.h:601
uint16 amsupport
Definition: amapi.h:169
#define Anum_pg_opclass_opcmethod
Definition: pg_opclass.h:75
#define BTORDER_PROC
Definition: nbtree.h:229
#define OperatorRelationId
Definition: pg_operator.h:32
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:2853
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:499
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
Definition: opclasscmds.c:1052
#define Anum_pg_opfamily_opfmethod
Definition: pg_opfamily.h:51
#define Anum_pg_opclass_opckeytype
Definition: pg_opclass.h:82
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:145
Definition: syscache.h:36
#define Anum_pg_amop_amopmethod
Definition: pg_amop.h:90
#define AMOP_SEARCH
Definition: pg_amop.h:69
#define BTSORTSUPPORT_PROC
Definition: nbtree.h:230
void RemoveOpClassById(Oid opclassOid)
Definition: opclasscmds.c:1566
Oid GetUserId(void)
Definition: miscinit.c:283
TypeName * storedtype
Definition: parsenodes.h:2527
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
bool amcanorderbyop
Definition: amapi.h:173
Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p)
Definition: namespace.c:2956
#define PointerGetDatum(X)
Definition: postgres.h:562
char * TypeNameToString(const TypeName *typeName)
Definition: parse_type.c:459
#define ProcedureRelationId
Definition: pg_proc.h:33
static HeapTuple OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok)
Definition: opclasscmds.c:162
#define Anum_pg_opfamily_opfowner
Definition: pg_opfamily.h:54
#define BTREE_AM_OID
Definition: pg_am.h:70
bool amstorage
Definition: amapi.h:187
#define Int16GetDatum(X)
Definition: postgres.h:457
#define Anum_pg_opfamily_opfnamespace
Definition: pg_opfamily.h:53
#define Anum_pg_opclass_opcintype
Definition: pg_opclass.h:80
#define OperatorClassRelationId
Definition: pg_opclass.h:49
#define INT4OID
Definition: pg_type.h:316
#define Anum_pg_opclass_opcowner
Definition: pg_opclass.h:78
#define OperatorFamilyRelationId
Definition: pg_opfamily.h:29
#define SearchSysCacheExists4(cacheId, key1, key2, key3, key4)
Definition: syscache.h:180
#define AccessMethodOperatorOidIndexId
Definition: indexing.h:79
bool pg_oper_ownercheck(Oid oper_oid, Oid roleid)
Definition: aclchk.c:4598
int errcode(int sqlerrcode)
Definition: elog.c:575
static HeapTuple OpFamilyCacheLookup(Oid amID, List *opfamilyname, bool missing_ok)
Definition: opclasscmds.c:83
static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid, List *procedures)
Definition: opclasscmds.c:1510
bool superuser(void)
Definition: superuser.c:47
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
#define Natts_pg_amop
Definition: pg_amop.h:83
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:255
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
void DeconstructQualifiedName(List *names, char **nspname_p, char **objname_p)
Definition: namespace.c:2769
Oid LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright, bool noError, int location)
Definition: parse_oper.c:102
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:692
#define Anum_pg_amop_amoprighttype
Definition: pg_amop.h:86
#define heap_close(r, l)
Definition: heapam.h:97
Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok)
Definition: opclasscmds.c:220
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:159
void RemoveAmProcEntryById(Oid entryOid)
Definition: opclasscmds.c:1614
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1372
unsigned int Oid
Definition: postgres_ext.h:31
#define Anum_pg_opfamily_opfname
Definition: pg_opfamily.h:52
bool pg_type_ownercheck(Oid type_oid, Oid roleid)
Definition: aclchk.c:4572
#define TypeRelationId
Definition: pg_type.h:34
ObjectAddress DefineOpClass(CreateOpClassStmt *stmt)
Definition: opclasscmds.c:323
int namestrcpy(Name name, const char *str)
Definition: name.c:216
Oid OpclassnameGetOpcid(Oid amid, const char *opcname)
Definition: namespace.c:1760
#define OidIsValid(objectId)
Definition: c.h:538
#define HASH_AM_OID
Definition: pg_am.h:73
AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4484
void IsThereOpClassInNamespace(const char *opcname, Oid opcmethod, Oid opcnamespace)
Definition: opclasscmds.c:1649
#define Anum_pg_opclass_opcfamily
Definition: pg_opclass.h:79
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:328
#define OpclassAmNameNspIndexId
Definition: indexing.h:198
#define lsecond(l)
Definition: pg_list.h:116
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:156
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition: aclchk.c:3457
#define AccessMethodOperatorRelationId
Definition: pg_amop.h:54
#define AccessMethodProcedureOidIndexId
Definition: indexing.h:84
#define Natts_pg_opfamily
Definition: pg_opfamily.h:50
#define OPCLASS_ITEM_OPERATOR
Definition: parsenodes.h:2513
#define Anum_pg_amproc_amproclefttype
Definition: pg_amproc.h:67
char * get_opname(Oid opno)
Definition: lsyscache.c:1119
static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, List *operators)
Definition: opclasscmds.c:1470
Oid OpfamilynameGetOpfid(Oid amid, const char *opfname)
Definition: namespace.c:1843
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:416
#define Anum_pg_opclass_opcnamespace
Definition: pg_opclass.h:77
static void AlterOpFamilyDrop(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid, int maxOpNumber, int maxProcNumber, List *items)
Definition: opclasscmds.c:947
#define linitial(l)
Definition: pg_list.h:111
#define VOIDOID
Definition: pg_type.h:690
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
#define ACL_CREATE
Definition: parsenodes.h:82
#define SearchSysCacheExists3(cacheId, key1, key2, key3)
Definition: syscache.h:178
Oid CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:162
IndexAmRoutine * GetIndexAmRoutineByAmId(Oid amoid, bool noerror)
Definition: amapi.c:56
#define Anum_pg_opclass_opcname
Definition: pg_opclass.h:76
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1412
#define Anum_pg_amproc_amprocfamily
Definition: pg_amproc.h:66
ItemPointerData t_self
Definition: htup.h:65
#define lfirst_node(type, lc)
Definition: pg_list.h:109
Definition: c.h:493
TypeName * datatype
Definition: parsenodes.h:2508
void RemoveOpFamilyById(Oid opfamilyOid)
Definition: opclasscmds.c:1547
ObjectWithArgs * name
Definition: parsenodes.h:2521
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3033
Oid get_index_am_oid(const char *amname, bool missing_ok)
Definition: amcmds.c:183
static ObjectAddress CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
Definition: opclasscmds.c:241
#define AccessMethodRelationId
Definition: pg_am.h:32
char * get_am_name(Oid amOid)
Definition: amcmds.c:202
void aclcheck_error(AclResult aclerr, AclObjectKind objectkind, const char *objectname)
Definition: aclchk.c:3399
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:873
static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
Definition: opclasscmds.c:1216
#define CStringGetDatum(X)
Definition: postgres.h:584
#define Anum_pg_opclass_opcdefault
Definition: pg_opclass.h:81
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:303
#define Anum_pg_amproc_amprocnum
Definition: pg_amproc.h:69
#define ereport(elevel, rest)
Definition: elog.h:122
#define Anum_pg_amproc_amprocrighttype
Definition: pg_amproc.h:68
#define Anum_pg_amop_amoplefttype
Definition: pg_amop.h:85
Oid LookupFuncWithArgs(ObjectWithArgs *func, bool noError)
Definition: parse_func.c:1990
void EventTriggerCollectAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid, List *operators, List *procedures)
List * lappend(List *list, void *datum)
Definition: list.c:128
char * NameListToString(List *names)
Definition: namespace.c:3063
void RemoveAmOpEntryById(Oid entryOid)
Definition: opclasscmds.c:1585
static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
Definition: opclasscmds.c:1023
void * palloc0(Size size)
Definition: mcxt.c:878
AclResult
Definition: acl.h:170
uintptr_t Datum
Definition: postgres.h:372
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1117
#define Anum_pg_amop_amoppurpose
Definition: pg_amop.h:88
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1284
TupleDesc rd_att
Definition: rel.h:115
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:83
#define BoolGetDatum(X)
Definition: postgres.h:408
#define InvalidOid
Definition: postgres_ext.h:36
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1094
#define INTERNALOID
Definition: pg_type.h:698
#define OPCLASS_ITEM_FUNCTION
Definition: parsenodes.h:2514
#define Natts_pg_opclass
Definition: pg_opclass.h:74
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:675
#define lfirst(lc)
Definition: pg_list.h:106
void recordDependencyOnCurrentExtension(const ObjectAddress *object, bool isReplace)
Definition: pg_depend.c:139
static void AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid, int maxOpNumber, int maxProcNumber, List *items)
Definition: opclasscmds.c:822
#define OPCLASS_ITEM_STORAGETYPE
Definition: parsenodes.h:2515
void EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt, Oid opcoid, List *operators, List *procedures)
Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok)
Definition: opclasscmds.c:141
static int list_length(const List *l)
Definition: pg_list.h:89
static void storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid, Oid opclassoid, List *procedures, bool isAdd)
Definition: opclasscmds.c:1370
#define Anum_pg_amproc_amproc
Definition: pg_amproc.h:70
#define BOOLOID
Definition: pg_type.h:288
FormData_pg_operator* Form_pg_operator
Definition: pg_operator.h:57
#define CharGetDatum(X)
Definition: postgres.h:422
Oid AlterOpFamily(AlterOpFamilyStmt *stmt)
Definition: opclasscmds.c:763
ObjectAddress DefineOpFamily(CreateOpFamilyStmt *stmt)
Definition: opclasscmds.c:718
uint16 amstrategies
Definition: amapi.h:167
static Datum values[MAXATTR]
Definition: bootstrap.c:163
#define Natts_pg_amproc
Definition: pg_amproc.h:65
tuple list
Definition: sort-test.py:11
FormData_pg_am * Form_pg_am
Definition: pg_am.h:46
#define Anum_pg_amop_amopopr
Definition: pg_amop.h:89
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define SearchSysCache3(cacheId, key1, key2, key3)
Definition: syscache.h:160
#define Anum_pg_amop_amopfamily
Definition: pg_amop.h:84
#define NameStr(name)
Definition: c.h:499
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define AMOP_ORDER
Definition: pg_amop.h:70
#define elog
Definition: elog.h:219
#define HeapTupleGetOid(tuple)
Definition: htup_details.h:695
static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
Definition: opclasscmds.c:1118
static void storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, Oid opclassoid, List *operators, bool isAdd)
Definition: opclasscmds.c:1255
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:68
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:31
#define AccessMethodProcedureRelationId
Definition: pg_amproc.h:43
Definition: pg_list.h:45
#define BTEqualStrategyNumber
Definition: stratnum.h:31
bool pg_proc_ownercheck(Oid proc_oid, Oid roleid)
Definition: aclchk.c:4624
void IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod, Oid opfnamespace)
Definition: opclasscmds.c:1672
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
Definition: parse_type.c:274
#define Anum_pg_amop_amopstrategy
Definition: pg_amop.h:87