1 /*-------------------------------------------------------------------------
5 * Routines for opclass (and opfamily) manipulation commands
7 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/backend/commands/opclasscmds.c
14 *-------------------------------------------------------------------------
20 #include "access/genam.h"
21 #include "access/heapam.h"
22 #include "access/nbtree.h"
23 #include "access/sysattr.h"
24 #include "catalog/dependency.h"
25 #include "catalog/indexing.h"
26 #include "catalog/objectaccess.h"
27 #include "catalog/pg_amop.h"
28 #include "catalog/pg_amproc.h"
29 #include "catalog/pg_namespace.h"
30 #include "catalog/pg_opclass.h"
31 #include "catalog/pg_operator.h"
32 #include "catalog/pg_opfamily.h"
33 #include "catalog/pg_proc.h"
34 #include "catalog/pg_type.h"
35 #include "commands/alter.h"
36 #include "commands/defrem.h"
37 #include "miscadmin.h"
38 #include "parser/parse_func.h"
39 #include "parser/parse_oper.h"
40 #include "parser/parse_type.h"
41 #include "utils/builtins.h"
42 #include "utils/fmgroids.h"
43 #include "utils/lsyscache.h"
44 #include "utils/rel.h"
45 #include "utils/syscache.h"
46 #include "utils/tqual.h"
50 * We use lists of this struct type to keep track of both operators and
51 * procedures while building or adding to an opfamily.
55 Oid object; /* operator or support proc's OID */
56 int number; /* strategy or support proc number */
57 Oid lefttype; /* lefttype */
58 Oid righttype; /* righttype */
59 Oid sortfamily; /* ordering operator's sort opfamily, or 0 */
63 static void AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
64 int maxOpNumber, int maxProcNumber,
66 static void AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
67 int maxOpNumber, int maxProcNumber,
69 static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
70 static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
71 static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
72 static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc);
73 static void storeOperators(List *opfamilyname, Oid amoid,
74 Oid opfamilyoid, Oid opclassoid,
75 List *operators, bool isAdd);
76 static void storeProcedures(List *opfamilyname, Oid amoid,
77 Oid opfamilyoid, Oid opclassoid,
78 List *procedures, bool isAdd);
79 static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
81 static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
83 static void AlterOpClassOwner_internal(Relation rel, HeapTuple tuple,
85 static void AlterOpFamilyOwner_internal(Relation rel, HeapTuple tuple,
91 * Look up an existing opfamily by name.
93 * Returns a syscache tuple reference, or NULL if not found.
96 OpFamilyCacheLookup(Oid amID, List *opfamilyname, bool missing_ok)
102 /* deconstruct the name list */
103 DeconstructQualifiedName(opfamilyname, &schemaname, &opfname);
107 /* Look in specific schema only */
110 namespaceId = LookupExplicitNamespace(schemaname);
111 htup = SearchSysCache3(OPFAMILYAMNAMENSP,
112 ObjectIdGetDatum(amID),
113 PointerGetDatum(opfname),
114 ObjectIdGetDatum(namespaceId));
118 /* Unqualified opfamily name, so search the search path */
119 Oid opfID = OpfamilynameGetOpfid(amID, opfname);
121 if (!OidIsValid(opfID))
124 htup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfID));
127 if (!HeapTupleIsValid(htup) && !missing_ok)
131 amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
132 if (!HeapTupleIsValid(amtup))
133 elog(ERROR, "cache lookup failed for access method %u", amID);
135 (errcode(ERRCODE_UNDEFINED_OBJECT),
136 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
137 NameListToString(opfamilyname),
138 NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
146 * find an opfamily OID by possibly qualified name
148 * If not found, returns InvalidOid if missing_ok, else throws error.
151 get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok)
156 htup = OpFamilyCacheLookup(amID, opfamilyname, missing_ok);
157 if (!HeapTupleIsValid(htup))
159 opfID = HeapTupleGetOid(htup);
160 ReleaseSysCache(htup);
167 * Look up an existing opclass by name.
169 * Returns a syscache tuple reference, or NULL if not found.
172 OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok)
178 /* deconstruct the name list */
179 DeconstructQualifiedName(opclassname, &schemaname, &opcname);
183 /* Look in specific schema only */
186 namespaceId = LookupExplicitNamespace(schemaname);
187 htup = SearchSysCache3(CLAAMNAMENSP,
188 ObjectIdGetDatum(amID),
189 PointerGetDatum(opcname),
190 ObjectIdGetDatum(namespaceId));
194 /* Unqualified opclass name, so search the search path */
195 Oid opcID = OpclassnameGetOpcid(amID, opcname);
197 if (!OidIsValid(opcID))
200 htup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcID));
203 if (!HeapTupleIsValid(htup) && !missing_ok)
207 amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
208 if (!HeapTupleIsValid(amtup))
209 elog(ERROR, "cache lookup failed for access method %u", amID);
211 (errcode(ERRCODE_UNDEFINED_OBJECT),
212 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
213 NameListToString(opclassname),
214 NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
222 * find an opclass OID by possibly qualified name
224 * If not found, returns InvalidOid if missing_ok, else throws error.
227 get_opclass_oid(Oid amID, List *opclassname, bool missing_ok)
232 htup = OpClassCacheLookup(amID, opclassname, missing_ok);
233 if (!HeapTupleIsValid(htup))
235 opcID = HeapTupleGetOid(htup);
236 ReleaseSysCache(htup);
243 * Internal routine to make the catalog entry for a new operator family.
245 * Caller must have done permissions checks etc. already.
248 CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
253 Datum values[Natts_pg_opfamily];
254 bool nulls[Natts_pg_opfamily];
256 ObjectAddress myself,
259 rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
262 * Make sure there is no existing opfamily of this name (this is just to
263 * give a more friendly error message than "duplicate key").
265 if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
266 ObjectIdGetDatum(amoid),
267 CStringGetDatum(opfname),
268 ObjectIdGetDatum(namespaceoid)))
270 (errcode(ERRCODE_DUPLICATE_OBJECT),
271 errmsg("operator family \"%s\" for access method \"%s\" already exists",
275 * Okay, let's create the pg_opfamily entry.
277 memset(values, 0, sizeof(values));
278 memset(nulls, false, sizeof(nulls));
280 values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
281 namestrcpy(&opfName, opfname);
282 values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
283 values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
284 values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());
286 tup = heap_form_tuple(rel->rd_att, values, nulls);
288 opfamilyoid = simple_heap_insert(rel, tup);
290 CatalogUpdateIndexes(rel, tup);
295 * Create dependencies for the opfamily proper. Note: we do not create a
296 * dependency link to the AM, because we don't currently support DROP
299 myself.classId = OperatorFamilyRelationId;
300 myself.objectId = opfamilyoid;
301 myself.objectSubId = 0;
303 /* dependency on namespace */
304 referenced.classId = NamespaceRelationId;
305 referenced.objectId = namespaceoid;
306 referenced.objectSubId = 0;
307 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
309 /* dependency on owner */
310 recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
312 /* dependency on extension */
313 recordDependencyOnCurrentExtension(&myself, false);
315 /* Post creation hook for new operator family */
316 InvokeObjectAccessHook(OAT_POST_CREATE,
317 OperatorFamilyRelationId, opfamilyoid, 0);
319 heap_close(rel, RowExclusiveLock);
326 * Define a new index operator class.
329 DefineOpClass(CreateOpClassStmt *stmt)
331 char *opcname; /* name of opclass we're creating */
332 Oid amoid, /* our AM's oid */
333 typeoid, /* indexable datatype oid */
334 storageoid, /* storage datatype oid, if any */
335 namespaceoid, /* namespace to create opclass in */
336 opfamilyoid, /* oid of containing opfamily */
337 opclassoid; /* oid of opclass we create */
338 int maxOpNumber, /* amstrategies value */
339 maxProcNumber; /* amsupport value */
340 bool amstorage; /* amstorage flag */
341 List *operators; /* OpFamilyMember list for operators */
342 List *procedures; /* OpFamilyMember list for support procs */
347 Datum values[Natts_pg_opclass];
348 bool nulls[Natts_pg_opclass];
351 ObjectAddress myself,
354 /* Convert list of names to a name and namespace */
355 namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname,
358 /* Check we have creation rights in target namespace */
359 aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
360 if (aclresult != ACLCHECK_OK)
361 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
362 get_namespace_name(namespaceoid));
364 /* Get necessary info about access method */
365 tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
366 if (!HeapTupleIsValid(tup))
368 (errcode(ERRCODE_UNDEFINED_OBJECT),
369 errmsg("access method \"%s\" does not exist",
372 amoid = HeapTupleGetOid(tup);
373 pg_am = (Form_pg_am) GETSTRUCT(tup);
374 maxOpNumber = pg_am->amstrategies;
375 /* if amstrategies is zero, just enforce that op numbers fit in int16 */
376 if (maxOpNumber <= 0)
377 maxOpNumber = SHRT_MAX;
378 maxProcNumber = pg_am->amsupport;
379 amstorage = pg_am->amstorage;
381 /* XXX Should we make any privilege check against the AM? */
383 ReleaseSysCache(tup);
386 * The question of appropriate permissions for CREATE OPERATOR CLASS is
387 * interesting. Creating an opclass is tantamount to granting public
388 * execute access on the functions involved, since the index machinery
389 * generally does not check access permission before using the functions.
390 * A minimum expectation therefore is that the caller have execute
391 * privilege with grant option. Since we don't have a way to make the
392 * opclass go away if the grant option is revoked, we choose instead to
393 * require ownership of the functions. It's also not entirely clear what
394 * permissions should be required on the datatype, but ownership seems
395 * like a safe choice.
397 * Currently, we require superuser privileges to create an opclass. This
398 * seems necessary because we have no way to validate that the offered set
399 * of operators and functions are consistent with the AM's expectations.
400 * It would be nice to provide such a check someday, if it can be done
401 * without solving the halting problem :-(
403 * XXX re-enable NOT_USED code sections below if you remove this test.
407 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
408 errmsg("must be superuser to create an operator class")));
410 /* Look up the datatype */
411 typeoid = typenameTypeId(NULL, stmt->datatype);
414 /* XXX this is unnecessary given the superuser check above */
415 /* Check we have ownership of the datatype */
416 if (!pg_type_ownercheck(typeoid, GetUserId()))
417 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
418 format_type_be(typeoid));
422 * Look up the containing operator family, or create one if FAMILY option
423 * was omitted and there's not a match already.
425 if (stmt->opfamilyname)
427 opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
431 /* Lookup existing family of same name and namespace */
432 tup = SearchSysCache3(OPFAMILYAMNAMENSP,
433 ObjectIdGetDatum(amoid),
434 PointerGetDatum(opcname),
435 ObjectIdGetDatum(namespaceoid));
436 if (HeapTupleIsValid(tup))
438 opfamilyoid = HeapTupleGetOid(tup);
441 * XXX given the superuser check above, there's no need for an
442 * ownership check here
444 ReleaseSysCache(tup);
449 * Create it ... again no need for more permissions ...
451 opfamilyoid = CreateOpFamily(stmt->amname, opcname,
452 namespaceoid, amoid);
459 /* Storage datatype is optional */
460 storageoid = InvalidOid;
463 * Scan the "items" list to obtain additional info.
465 foreach(l, stmt->items)
467 CreateOpClassItem *item = lfirst(l);
471 OpFamilyMember *member;
473 Assert(IsA(item, CreateOpClassItem));
474 switch (item->itemtype)
476 case OPCLASS_ITEM_OPERATOR:
477 if (item->number <= 0 || item->number > maxOpNumber)
479 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
480 errmsg("invalid operator number %d,"
481 " must be between 1 and %d",
482 item->number, maxOpNumber)));
483 if (item->args != NIL)
485 TypeName *typeName1 = (TypeName *) linitial(item->args);
486 TypeName *typeName2 = (TypeName *) lsecond(item->args);
488 operOid = LookupOperNameTypeNames(NULL, item->name,
489 typeName1, typeName2,
494 /* Default to binary op on input datatype */
495 operOid = LookupOperName(NULL, item->name,
500 if (item->order_family)
501 sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
505 sortfamilyOid = InvalidOid;
508 /* XXX this is unnecessary given the superuser check above */
509 /* Caller must own operator and its underlying function */
510 if (!pg_oper_ownercheck(operOid, GetUserId()))
511 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
512 get_opname(operOid));
513 funcOid = get_opcode(operOid);
514 if (!pg_proc_ownercheck(funcOid, GetUserId()))
515 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
516 get_func_name(funcOid));
520 member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
521 member->object = operOid;
522 member->number = item->number;
523 member->sortfamily = sortfamilyOid;
524 assignOperTypes(member, amoid, typeoid);
525 addFamilyMember(&operators, member, false);
527 case OPCLASS_ITEM_FUNCTION:
528 if (item->number <= 0 || item->number > maxProcNumber)
530 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
531 errmsg("invalid procedure number %d,"
532 " must be between 1 and %d",
533 item->number, maxProcNumber)));
534 funcOid = LookupFuncNameTypeNames(item->name, item->args,
537 /* XXX this is unnecessary given the superuser check above */
538 /* Caller must own function */
539 if (!pg_proc_ownercheck(funcOid, GetUserId()))
540 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
541 get_func_name(funcOid));
545 member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
546 member->object = funcOid;
547 member->number = item->number;
549 /* allow overriding of the function's actual arg types */
550 if (item->class_args)
551 processTypesSpec(item->class_args,
552 &member->lefttype, &member->righttype);
554 assignProcTypes(member, amoid, typeoid);
555 addFamilyMember(&procedures, member, true);
557 case OPCLASS_ITEM_STORAGETYPE:
558 if (OidIsValid(storageoid))
560 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
561 errmsg("storage type specified more than once")));
562 storageoid = typenameTypeId(NULL, item->storedtype);
565 /* XXX this is unnecessary given the superuser check above */
566 /* Check we have ownership of the datatype */
567 if (!pg_type_ownercheck(storageoid, GetUserId()))
568 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
569 format_type_be(storageoid));
573 elog(ERROR, "unrecognized item type: %d", item->itemtype);
579 * If storagetype is specified, make sure it's legal.
581 if (OidIsValid(storageoid))
583 /* Just drop the spec if same as column datatype */
584 if (storageoid == typeoid)
585 storageoid = InvalidOid;
588 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
589 errmsg("storage type cannot be different from data type for access method \"%s\"",
593 rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
596 * Make sure there is no existing opclass of this name (this is just to
597 * give a more friendly error message than "duplicate key").
599 if (SearchSysCacheExists3(CLAAMNAMENSP,
600 ObjectIdGetDatum(amoid),
601 CStringGetDatum(opcname),
602 ObjectIdGetDatum(namespaceoid)))
604 (errcode(ERRCODE_DUPLICATE_OBJECT),
605 errmsg("operator class \"%s\" for access method \"%s\" already exists",
606 opcname, stmt->amname)));
609 * If we are creating a default opclass, check there isn't one already.
610 * (Note we do not restrict this test to visible opclasses; this ensures
611 * that typcache.c can find unique solutions to its questions.)
618 ScanKeyInit(&skey[0],
619 Anum_pg_opclass_opcmethod,
620 BTEqualStrategyNumber, F_OIDEQ,
621 ObjectIdGetDatum(amoid));
623 scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
624 SnapshotNow, 1, skey);
626 while (HeapTupleIsValid(tup = systable_getnext(scan)))
628 Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
630 if (opclass->opcintype == typeoid && opclass->opcdefault)
632 (errcode(ERRCODE_DUPLICATE_OBJECT),
633 errmsg("could not make operator class \"%s\" be default for type %s",
635 TypeNameToString(stmt->datatype)),
636 errdetail("Operator class \"%s\" already is the default.",
637 NameStr(opclass->opcname))));
640 systable_endscan(scan);
644 * Okay, let's create the pg_opclass entry.
646 memset(values, 0, sizeof(values));
647 memset(nulls, false, sizeof(nulls));
649 values[Anum_pg_opclass_opcmethod - 1] = ObjectIdGetDatum(amoid);
650 namestrcpy(&opcName, opcname);
651 values[Anum_pg_opclass_opcname - 1] = NameGetDatum(&opcName);
652 values[Anum_pg_opclass_opcnamespace - 1] = ObjectIdGetDatum(namespaceoid);
653 values[Anum_pg_opclass_opcowner - 1] = ObjectIdGetDatum(GetUserId());
654 values[Anum_pg_opclass_opcfamily - 1] = ObjectIdGetDatum(opfamilyoid);
655 values[Anum_pg_opclass_opcintype - 1] = ObjectIdGetDatum(typeoid);
656 values[Anum_pg_opclass_opcdefault - 1] = BoolGetDatum(stmt->isDefault);
657 values[Anum_pg_opclass_opckeytype - 1] = ObjectIdGetDatum(storageoid);
659 tup = heap_form_tuple(rel->rd_att, values, nulls);
661 opclassoid = simple_heap_insert(rel, tup);
663 CatalogUpdateIndexes(rel, tup);
668 * Now add tuples to pg_amop and pg_amproc tying in the operators and
669 * functions. Dependencies on them are inserted, too.
671 storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
672 opclassoid, operators, false);
673 storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
674 opclassoid, procedures, false);
677 * Create dependencies for the opclass proper. Note: we do not create a
678 * dependency link to the AM, because we don't currently support DROP
681 myself.classId = OperatorClassRelationId;
682 myself.objectId = opclassoid;
683 myself.objectSubId = 0;
685 /* dependency on namespace */
686 referenced.classId = NamespaceRelationId;
687 referenced.objectId = namespaceoid;
688 referenced.objectSubId = 0;
689 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
691 /* dependency on opfamily */
692 referenced.classId = OperatorFamilyRelationId;
693 referenced.objectId = opfamilyoid;
694 referenced.objectSubId = 0;
695 recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
697 /* dependency on indexed datatype */
698 referenced.classId = TypeRelationId;
699 referenced.objectId = typeoid;
700 referenced.objectSubId = 0;
701 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
703 /* dependency on storage datatype */
704 if (OidIsValid(storageoid))
706 referenced.classId = TypeRelationId;
707 referenced.objectId = storageoid;
708 referenced.objectSubId = 0;
709 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
712 /* dependency on owner */
713 recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
715 /* dependency on extension */
716 recordDependencyOnCurrentExtension(&myself, false);
718 /* Post creation hook for new operator class */
719 InvokeObjectAccessHook(OAT_POST_CREATE,
720 OperatorClassRelationId, opclassoid, 0);
722 heap_close(rel, RowExclusiveLock);
728 * Define a new index operator family.
731 DefineOpFamily(CreateOpFamilyStmt *stmt)
733 char *opfname; /* name of opfamily we're creating */
734 Oid amoid, /* our AM's oid */
735 namespaceoid; /* namespace to create opfamily in */
738 /* Convert list of names to a name and namespace */
739 namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
742 /* Check we have creation rights in target namespace */
743 aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
744 if (aclresult != ACLCHECK_OK)
745 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
746 get_namespace_name(namespaceoid));
748 /* Get access method OID, throwing an error if it doesn't exist. */
749 amoid = get_am_oid(stmt->amname, false);
751 /* XXX Should we make any privilege check against the AM? */
754 * Currently, we require superuser privileges to create an opfamily. See
755 * comments in DefineOpClass.
759 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
760 errmsg("must be superuser to create an operator family")));
762 /* Insert pg_opfamily catalog entry */
763 (void) CreateOpFamily(stmt->amname, opfname, namespaceoid, amoid);
769 * Add or remove operators/procedures within an existing operator family.
771 * Note: this implements only ALTER OPERATOR FAMILY ... ADD/DROP. Some
772 * other commands called ALTER OPERATOR FAMILY exist, but go through
773 * different code paths.
776 AlterOpFamily(AlterOpFamilyStmt *stmt)
778 Oid amoid, /* our AM's oid */
779 opfamilyoid; /* oid of opfamily */
780 int maxOpNumber, /* amstrategies value */
781 maxProcNumber; /* amsupport value */
785 /* Get necessary info about access method */
786 tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
787 if (!HeapTupleIsValid(tup))
789 (errcode(ERRCODE_UNDEFINED_OBJECT),
790 errmsg("access method \"%s\" does not exist",
793 amoid = HeapTupleGetOid(tup);
794 pg_am = (Form_pg_am) GETSTRUCT(tup);
795 maxOpNumber = pg_am->amstrategies;
796 /* if amstrategies is zero, just enforce that op numbers fit in int16 */
797 if (maxOpNumber <= 0)
798 maxOpNumber = SHRT_MAX;
799 maxProcNumber = pg_am->amsupport;
801 /* XXX Should we make any privilege check against the AM? */
803 ReleaseSysCache(tup);
805 /* Look up the opfamily */
806 opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
809 * Currently, we require superuser privileges to alter an opfamily.
811 * XXX re-enable NOT_USED code sections below if you remove this test.
815 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
816 errmsg("must be superuser to alter an operator family")));
819 * ADD and DROP cases need separate code from here on down.
822 AlterOpFamilyDrop(stmt->opfamilyname, amoid, opfamilyoid,
823 maxOpNumber, maxProcNumber,
826 AlterOpFamilyAdd(stmt->opfamilyname, amoid, opfamilyoid,
827 maxOpNumber, maxProcNumber,
832 * ADD part of ALTER OP FAMILY
835 AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
836 int maxOpNumber, int maxProcNumber,
839 List *operators; /* OpFamilyMember list for operators */
840 List *procedures; /* OpFamilyMember list for support procs */
847 * Scan the "items" list to obtain additional info.
851 CreateOpClassItem *item = lfirst(l);
855 OpFamilyMember *member;
857 Assert(IsA(item, CreateOpClassItem));
858 switch (item->itemtype)
860 case OPCLASS_ITEM_OPERATOR:
861 if (item->number <= 0 || item->number > maxOpNumber)
863 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
864 errmsg("invalid operator number %d,"
865 " must be between 1 and %d",
866 item->number, maxOpNumber)));
867 if (item->args != NIL)
869 TypeName *typeName1 = (TypeName *) linitial(item->args);
870 TypeName *typeName2 = (TypeName *) lsecond(item->args);
872 operOid = LookupOperNameTypeNames(NULL, item->name,
873 typeName1, typeName2,
879 (errcode(ERRCODE_SYNTAX_ERROR),
880 errmsg("operator argument types must be specified in ALTER OPERATOR FAMILY")));
881 operOid = InvalidOid; /* keep compiler quiet */
884 if (item->order_family)
885 sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
889 sortfamilyOid = InvalidOid;
892 /* XXX this is unnecessary given the superuser check above */
893 /* Caller must own operator and its underlying function */
894 if (!pg_oper_ownercheck(operOid, GetUserId()))
895 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
896 get_opname(operOid));
897 funcOid = get_opcode(operOid);
898 if (!pg_proc_ownercheck(funcOid, GetUserId()))
899 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
900 get_func_name(funcOid));
904 member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
905 member->object = operOid;
906 member->number = item->number;
907 member->sortfamily = sortfamilyOid;
908 assignOperTypes(member, amoid, InvalidOid);
909 addFamilyMember(&operators, member, false);
911 case OPCLASS_ITEM_FUNCTION:
912 if (item->number <= 0 || item->number > maxProcNumber)
914 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
915 errmsg("invalid procedure number %d,"
916 " must be between 1 and %d",
917 item->number, maxProcNumber)));
918 funcOid = LookupFuncNameTypeNames(item->name, item->args,
921 /* XXX this is unnecessary given the superuser check above */
922 /* Caller must own function */
923 if (!pg_proc_ownercheck(funcOid, GetUserId()))
924 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
925 get_func_name(funcOid));
929 member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
930 member->object = funcOid;
931 member->number = item->number;
933 /* allow overriding of the function's actual arg types */
934 if (item->class_args)
935 processTypesSpec(item->class_args,
936 &member->lefttype, &member->righttype);
938 assignProcTypes(member, amoid, InvalidOid);
939 addFamilyMember(&procedures, member, true);
941 case OPCLASS_ITEM_STORAGETYPE:
943 (errcode(ERRCODE_SYNTAX_ERROR),
944 errmsg("STORAGE cannot be specified in ALTER OPERATOR FAMILY")));
947 elog(ERROR, "unrecognized item type: %d", item->itemtype);
953 * Add tuples to pg_amop and pg_amproc tying in the operators and
954 * functions. Dependencies on them are inserted, too.
956 storeOperators(opfamilyname, amoid, opfamilyoid,
957 InvalidOid, operators, true);
958 storeProcedures(opfamilyname, amoid, opfamilyoid,
959 InvalidOid, procedures, true);
963 * DROP part of ALTER OP FAMILY
966 AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
967 int maxOpNumber, int maxProcNumber,
970 List *operators; /* OpFamilyMember list for operators */
971 List *procedures; /* OpFamilyMember list for support procs */
978 * Scan the "items" list to obtain additional info.
982 CreateOpClassItem *item = lfirst(l);
985 OpFamilyMember *member;
987 Assert(IsA(item, CreateOpClassItem));
988 switch (item->itemtype)
990 case OPCLASS_ITEM_OPERATOR:
991 if (item->number <= 0 || item->number > maxOpNumber)
993 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
994 errmsg("invalid operator number %d,"
995 " must be between 1 and %d",
996 item->number, maxOpNumber)));
997 processTypesSpec(item->args, &lefttype, &righttype);
999 member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
1000 member->number = item->number;
1001 member->lefttype = lefttype;
1002 member->righttype = righttype;
1003 addFamilyMember(&operators, member, false);
1005 case OPCLASS_ITEM_FUNCTION:
1006 if (item->number <= 0 || item->number > maxProcNumber)
1008 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1009 errmsg("invalid procedure number %d,"
1010 " must be between 1 and %d",
1011 item->number, maxProcNumber)));
1012 processTypesSpec(item->args, &lefttype, &righttype);
1014 member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
1015 member->number = item->number;
1016 member->lefttype = lefttype;
1017 member->righttype = righttype;
1018 addFamilyMember(&procedures, member, true);
1020 case OPCLASS_ITEM_STORAGETYPE:
1021 /* grammar prevents this from appearing */
1023 elog(ERROR, "unrecognized item type: %d", item->itemtype);
1029 * Remove tuples from pg_amop and pg_amproc.
1031 dropOperators(opfamilyname, amoid, opfamilyoid, operators);
1032 dropProcedures(opfamilyname, amoid, opfamilyoid, procedures);
1037 * Deal with explicit arg types used in ALTER ADD/DROP
1040 processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
1044 Assert(args != NIL);
1046 typeName = (TypeName *) linitial(args);
1047 *lefttype = typenameTypeId(NULL, typeName);
1049 if (list_length(args) > 1)
1051 typeName = (TypeName *) lsecond(args);
1052 *righttype = typenameTypeId(NULL, typeName);
1055 *righttype = *lefttype;
1057 if (list_length(args) > 2)
1059 (errcode(ERRCODE_SYNTAX_ERROR),
1060 errmsg("one or two argument types must be specified")));
1065 * Determine the lefttype/righttype to assign to an operator,
1066 * and do any validity checking we can manage.
1069 assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
1072 Form_pg_operator opform;
1074 /* Fetch the operator definition */
1075 optup = SearchSysCache1(OPEROID, ObjectIdGetDatum(member->object));
1077 elog(ERROR, "cache lookup failed for operator %u", member->object);
1078 opform = (Form_pg_operator) GETSTRUCT(optup);
1081 * Opfamily operators must be binary.
1083 if (opform->oprkind != 'b')
1085 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1086 errmsg("index operators must be binary")));
1088 if (OidIsValid(member->sortfamily))
1091 * Ordering op, check index supports that. (We could perhaps also
1092 * check that the operator returns a type supported by the sortfamily,
1093 * but that seems more trouble than it's worth here. If it does not,
1094 * the operator will never be matchable to any ORDER BY clause, but no
1095 * worse consequences can ensue. Also, trying to check that would
1096 * create an ordering hazard during dump/reload: it's possible that
1097 * the family has been created but not yet populated with the required
1103 amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
1105 elog(ERROR, "cache lookup failed for access method %u", amoid);
1106 pg_am = (Form_pg_am) GETSTRUCT(amtup);
1108 if (!pg_am->amcanorderbyop)
1110 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1111 errmsg("access method \"%s\" does not support ordering operators",
1112 NameStr(pg_am->amname))));
1114 ReleaseSysCache(amtup);
1119 * Search operators must return boolean.
1121 if (opform->oprresult != BOOLOID)
1123 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1124 errmsg("index search operators must return boolean")));
1128 * If lefttype/righttype isn't specified, use the operator's input types
1130 if (!OidIsValid(member->lefttype))
1131 member->lefttype = opform->oprleft;
1132 if (!OidIsValid(member->righttype))
1133 member->righttype = opform->oprright;
1135 ReleaseSysCache(optup);
1139 * Determine the lefttype/righttype to assign to a support procedure,
1140 * and do any validity checking we can manage.
1143 assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
1146 Form_pg_proc procform;
1148 /* Fetch the procedure definition */
1149 proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(member->object));
1150 if (proctup == NULL)
1151 elog(ERROR, "cache lookup failed for function %u", member->object);
1152 procform = (Form_pg_proc) GETSTRUCT(proctup);
1155 * btree comparison procs must be 2-arg procs returning int4, while btree
1156 * sortsupport procs must take internal and return void. hash support
1157 * procs must be 1-arg procs returning int4. Otherwise we don't know.
1159 if (amoid == BTREE_AM_OID)
1161 if (member->number == BTORDER_PROC)
1163 if (procform->pronargs != 2)
1165 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1166 errmsg("btree comparison procedures must have two arguments")));
1167 if (procform->prorettype != INT4OID)
1169 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1170 errmsg("btree comparison procedures must return integer")));
1173 * If lefttype/righttype isn't specified, use the proc's input
1176 if (!OidIsValid(member->lefttype))
1177 member->lefttype = procform->proargtypes.values[0];
1178 if (!OidIsValid(member->righttype))
1179 member->righttype = procform->proargtypes.values[1];
1181 else if (member->number == BTSORTSUPPORT_PROC)
1183 if (procform->pronargs != 1 ||
1184 procform->proargtypes.values[0] != INTERNALOID)
1186 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1187 errmsg("btree sort support procedures must accept type \"internal\"")));
1188 if (procform->prorettype != VOIDOID)
1190 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1191 errmsg("btree sort support procedures must return void")));
1194 * Can't infer lefttype/righttype from proc, so use default rule
1198 else if (amoid == HASH_AM_OID)
1200 if (procform->pronargs != 1)
1202 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1203 errmsg("hash procedures must have one argument")));
1204 if (procform->prorettype != INT4OID)
1206 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1207 errmsg("hash procedures must return integer")));
1210 * If lefttype/righttype isn't specified, use the proc's input type
1212 if (!OidIsValid(member->lefttype))
1213 member->lefttype = procform->proargtypes.values[0];
1214 if (!OidIsValid(member->righttype))
1215 member->righttype = procform->proargtypes.values[0];
1219 * The default in CREATE OPERATOR CLASS is to use the class' opcintype as
1220 * lefttype and righttype. In CREATE or ALTER OPERATOR FAMILY, opcintype
1221 * isn't available, so make the user specify the types.
1223 if (!OidIsValid(member->lefttype))
1224 member->lefttype = typeoid;
1225 if (!OidIsValid(member->righttype))
1226 member->righttype = typeoid;
1228 if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
1230 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1231 errmsg("associated data types must be specified for index support procedure")));
1233 ReleaseSysCache(proctup);
1237 * Add a new family member to the appropriate list, after checking for
1238 * duplicated strategy or proc number.
1241 addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
1247 OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
1249 if (old->number == member->number &&
1250 old->lefttype == member->lefttype &&
1251 old->righttype == member->righttype)
1255 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1256 errmsg("procedure number %d for (%s,%s) appears more than once",
1258 format_type_be(member->lefttype),
1259 format_type_be(member->righttype))));
1262 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1263 errmsg("operator number %d for (%s,%s) appears more than once",
1265 format_type_be(member->lefttype),
1266 format_type_be(member->righttype))));
1269 *list = lappend(*list, member);
1273 * Dump the operators to pg_amop
1275 * We also make dependency entries in pg_depend for the opfamily entries.
1276 * If opclassoid is valid then make an INTERNAL dependency on that opclass,
1277 * else make an AUTO dependency on the opfamily.
1280 storeOperators(List *opfamilyname, Oid amoid,
1281 Oid opfamilyoid, Oid opclassoid,
1282 List *operators, bool isAdd)
1285 Datum values[Natts_pg_amop];
1286 bool nulls[Natts_pg_amop];
1289 ObjectAddress myself,
1293 rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
1295 foreach(l, operators)
1297 OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1301 * If adding to an existing family, check for conflict with an
1302 * existing pg_amop entry (just to give a nicer error message)
1305 SearchSysCacheExists4(AMOPSTRATEGY,
1306 ObjectIdGetDatum(opfamilyoid),
1307 ObjectIdGetDatum(op->lefttype),
1308 ObjectIdGetDatum(op->righttype),
1309 Int16GetDatum(op->number)))
1311 (errcode(ERRCODE_DUPLICATE_OBJECT),
1312 errmsg("operator %d(%s,%s) already exists in operator family \"%s\"",
1314 format_type_be(op->lefttype),
1315 format_type_be(op->righttype),
1316 NameListToString(opfamilyname))));
1318 oppurpose = OidIsValid(op->sortfamily) ? AMOP_ORDER : AMOP_SEARCH;
1320 /* Create the pg_amop entry */
1321 memset(values, 0, sizeof(values));
1322 memset(nulls, false, sizeof(nulls));
1324 values[Anum_pg_amop_amopfamily - 1] = ObjectIdGetDatum(opfamilyoid);
1325 values[Anum_pg_amop_amoplefttype - 1] = ObjectIdGetDatum(op->lefttype);
1326 values[Anum_pg_amop_amoprighttype - 1] = ObjectIdGetDatum(op->righttype);
1327 values[Anum_pg_amop_amopstrategy - 1] = Int16GetDatum(op->number);
1328 values[Anum_pg_amop_amoppurpose - 1] = CharGetDatum(oppurpose);
1329 values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
1330 values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);
1331 values[Anum_pg_amop_amopsortfamily - 1] = ObjectIdGetDatum(op->sortfamily);
1333 tup = heap_form_tuple(rel->rd_att, values, nulls);
1335 entryoid = simple_heap_insert(rel, tup);
1337 CatalogUpdateIndexes(rel, tup);
1339 heap_freetuple(tup);
1341 /* Make its dependencies */
1342 myself.classId = AccessMethodOperatorRelationId;
1343 myself.objectId = entryoid;
1344 myself.objectSubId = 0;
1346 referenced.classId = OperatorRelationId;
1347 referenced.objectId = op->object;
1348 referenced.objectSubId = 0;
1350 if (OidIsValid(opclassoid))
1352 /* if contained in an opclass, use a NORMAL dep on operator */
1353 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1355 /* ... and an INTERNAL dep on the opclass */
1356 referenced.classId = OperatorClassRelationId;
1357 referenced.objectId = opclassoid;
1358 referenced.objectSubId = 0;
1359 recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1363 /* if "loose" in the opfamily, use a AUTO dep on operator */
1364 recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1366 /* ... and an AUTO dep on the opfamily */
1367 referenced.classId = OperatorFamilyRelationId;
1368 referenced.objectId = opfamilyoid;
1369 referenced.objectSubId = 0;
1370 recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1373 /* A search operator also needs a dep on the referenced opfamily */
1374 if (OidIsValid(op->sortfamily))
1376 referenced.classId = OperatorFamilyRelationId;
1377 referenced.objectId = op->sortfamily;
1378 referenced.objectSubId = 0;
1379 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1383 heap_close(rel, RowExclusiveLock);
1387 * Dump the procedures (support routines) to pg_amproc
1389 * We also make dependency entries in pg_depend for the opfamily entries.
1390 * If opclassoid is valid then make an INTERNAL dependency on that opclass,
1391 * else make an AUTO dependency on the opfamily.
1394 storeProcedures(List *opfamilyname, Oid amoid,
1395 Oid opfamilyoid, Oid opclassoid,
1396 List *procedures, bool isAdd)
1399 Datum values[Natts_pg_amproc];
1400 bool nulls[Natts_pg_amproc];
1403 ObjectAddress myself,
1407 rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
1409 foreach(l, procedures)
1411 OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
1414 * If adding to an existing family, check for conflict with an
1415 * existing pg_amproc entry (just to give a nicer error message)
1418 SearchSysCacheExists4(AMPROCNUM,
1419 ObjectIdGetDatum(opfamilyoid),
1420 ObjectIdGetDatum(proc->lefttype),
1421 ObjectIdGetDatum(proc->righttype),
1422 Int16GetDatum(proc->number)))
1424 (errcode(ERRCODE_DUPLICATE_OBJECT),
1425 errmsg("function %d(%s,%s) already exists in operator family \"%s\"",
1427 format_type_be(proc->lefttype),
1428 format_type_be(proc->righttype),
1429 NameListToString(opfamilyname))));
1431 /* Create the pg_amproc entry */
1432 memset(values, 0, sizeof(values));
1433 memset(nulls, false, sizeof(nulls));
1435 values[Anum_pg_amproc_amprocfamily - 1] = ObjectIdGetDatum(opfamilyoid);
1436 values[Anum_pg_amproc_amproclefttype - 1] = ObjectIdGetDatum(proc->lefttype);
1437 values[Anum_pg_amproc_amprocrighttype - 1] = ObjectIdGetDatum(proc->righttype);
1438 values[Anum_pg_amproc_amprocnum - 1] = Int16GetDatum(proc->number);
1439 values[Anum_pg_amproc_amproc - 1] = ObjectIdGetDatum(proc->object);
1441 tup = heap_form_tuple(rel->rd_att, values, nulls);
1443 entryoid = simple_heap_insert(rel, tup);
1445 CatalogUpdateIndexes(rel, tup);
1447 heap_freetuple(tup);
1449 /* Make its dependencies */
1450 myself.classId = AccessMethodProcedureRelationId;
1451 myself.objectId = entryoid;
1452 myself.objectSubId = 0;
1454 referenced.classId = ProcedureRelationId;
1455 referenced.objectId = proc->object;
1456 referenced.objectSubId = 0;
1458 if (OidIsValid(opclassoid))
1460 /* if contained in an opclass, use a NORMAL dep on procedure */
1461 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1463 /* ... and an INTERNAL dep on the opclass */
1464 referenced.classId = OperatorClassRelationId;
1465 referenced.objectId = opclassoid;
1466 referenced.objectSubId = 0;
1467 recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1471 /* if "loose" in the opfamily, use a AUTO dep on procedure */
1472 recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1474 /* ... and an AUTO dep on the opfamily */
1475 referenced.classId = OperatorFamilyRelationId;
1476 referenced.objectId = opfamilyoid;
1477 referenced.objectSubId = 0;
1478 recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1482 heap_close(rel, RowExclusiveLock);
1487 * Remove operator entries from an opfamily.
1489 * Note: this is only allowed for "loose" members of an opfamily, hence
1490 * behavior is always RESTRICT.
1493 dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
1498 foreach(l, operators)
1500 OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1502 ObjectAddress object;
1504 amopid = GetSysCacheOid4(AMOPSTRATEGY,
1505 ObjectIdGetDatum(opfamilyoid),
1506 ObjectIdGetDatum(op->lefttype),
1507 ObjectIdGetDatum(op->righttype),
1508 Int16GetDatum(op->number));
1509 if (!OidIsValid(amopid))
1511 (errcode(ERRCODE_UNDEFINED_OBJECT),
1512 errmsg("operator %d(%s,%s) does not exist in operator family \"%s\"",
1514 format_type_be(op->lefttype),
1515 format_type_be(op->righttype),
1516 NameListToString(opfamilyname))));
1518 object.classId = AccessMethodOperatorRelationId;
1519 object.objectId = amopid;
1520 object.objectSubId = 0;
1522 performDeletion(&object, DROP_RESTRICT);
1527 * Remove procedure entries from an opfamily.
1529 * Note: this is only allowed for "loose" members of an opfamily, hence
1530 * behavior is always RESTRICT.
1533 dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
1538 foreach(l, procedures)
1540 OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1542 ObjectAddress object;
1544 amprocid = GetSysCacheOid4(AMPROCNUM,
1545 ObjectIdGetDatum(opfamilyoid),
1546 ObjectIdGetDatum(op->lefttype),
1547 ObjectIdGetDatum(op->righttype),
1548 Int16GetDatum(op->number));
1549 if (!OidIsValid(amprocid))
1551 (errcode(ERRCODE_UNDEFINED_OBJECT),
1552 errmsg("function %d(%s,%s) does not exist in operator family \"%s\"",
1554 format_type_be(op->lefttype),
1555 format_type_be(op->righttype),
1556 NameListToString(opfamilyname))));
1558 object.classId = AccessMethodProcedureRelationId;
1559 object.objectId = amprocid;
1560 object.objectSubId = 0;
1562 performDeletion(&object, DROP_RESTRICT);
1567 * Deletion subroutines for use by dependency.c.
1570 RemoveOpFamilyById(Oid opfamilyOid)
1575 rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
1577 tup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyOid));
1578 if (!HeapTupleIsValid(tup)) /* should not happen */
1579 elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid);
1581 simple_heap_delete(rel, &tup->t_self);
1583 ReleaseSysCache(tup);
1585 heap_close(rel, RowExclusiveLock);
1589 RemoveOpClassById(Oid opclassOid)
1594 rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
1596 tup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassOid));
1597 if (!HeapTupleIsValid(tup)) /* should not happen */
1598 elog(ERROR, "cache lookup failed for opclass %u", opclassOid);
1600 simple_heap_delete(rel, &tup->t_self);
1602 ReleaseSysCache(tup);
1604 heap_close(rel, RowExclusiveLock);
1608 RemoveAmOpEntryById(Oid entryOid)
1612 ScanKeyData skey[1];
1615 ScanKeyInit(&skey[0],
1616 ObjectIdAttributeNumber,
1617 BTEqualStrategyNumber, F_OIDEQ,
1618 ObjectIdGetDatum(entryOid));
1620 rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
1622 scan = systable_beginscan(rel, AccessMethodOperatorOidIndexId, true,
1623 SnapshotNow, 1, skey);
1625 /* we expect exactly one match */
1626 tup = systable_getnext(scan);
1627 if (!HeapTupleIsValid(tup))
1628 elog(ERROR, "could not find tuple for amop entry %u", entryOid);
1630 simple_heap_delete(rel, &tup->t_self);
1632 systable_endscan(scan);
1633 heap_close(rel, RowExclusiveLock);
1637 RemoveAmProcEntryById(Oid entryOid)
1641 ScanKeyData skey[1];
1644 ScanKeyInit(&skey[0],
1645 ObjectIdAttributeNumber,
1646 BTEqualStrategyNumber, F_OIDEQ,
1647 ObjectIdGetDatum(entryOid));
1649 rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
1651 scan = systable_beginscan(rel, AccessMethodProcedureOidIndexId, true,
1652 SnapshotNow, 1, skey);
1654 /* we expect exactly one match */
1655 tup = systable_getnext(scan);
1656 if (!HeapTupleIsValid(tup))
1657 elog(ERROR, "could not find tuple for amproc entry %u", entryOid);
1659 simple_heap_delete(rel, &tup->t_self);
1661 systable_endscan(scan);
1662 heap_close(rel, RowExclusiveLock);
1670 RenameOpClass(List *name, const char *access_method, const char *newname)
1678 AclResult aclresult;
1680 amOid = get_am_oid(access_method, false);
1682 rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
1684 /* Look up the opclass. */
1685 origtup = OpClassCacheLookup(amOid, name, false);
1686 tup = heap_copytuple(origtup);
1687 ReleaseSysCache(origtup);
1688 opcOid = HeapTupleGetOid(tup);
1689 namespaceOid = ((Form_pg_opclass) GETSTRUCT(tup))->opcnamespace;
1691 /* make sure the new name doesn't exist */
1692 if (SearchSysCacheExists3(CLAAMNAMENSP,
1693 ObjectIdGetDatum(amOid),
1694 CStringGetDatum(newname),
1695 ObjectIdGetDatum(namespaceOid)))
1698 (errcode(ERRCODE_DUPLICATE_OBJECT),
1699 errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
1700 newname, access_method,
1701 get_namespace_name(namespaceOid))));
1705 if (!pg_opclass_ownercheck(opcOid, GetUserId()))
1706 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
1707 NameListToString(name));
1709 /* must have CREATE privilege on namespace */
1710 aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
1711 if (aclresult != ACLCHECK_OK)
1712 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1713 get_namespace_name(namespaceOid));
1716 namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname);
1717 simple_heap_update(rel, &tup->t_self, tup);
1718 CatalogUpdateIndexes(rel, tup);
1720 heap_close(rel, NoLock);
1721 heap_freetuple(tup);
1728 RenameOpFamily(List *name, const char *access_method, const char *newname)
1737 AclResult aclresult;
1739 amOid = get_am_oid(access_method, false);
1741 rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
1744 * Look up the opfamily
1746 DeconstructQualifiedName(name, &schemaname, &opfname);
1750 namespaceOid = LookupExplicitNamespace(schemaname);
1752 tup = SearchSysCacheCopy3(OPFAMILYAMNAMENSP,
1753 ObjectIdGetDatum(amOid),
1754 PointerGetDatum(opfname),
1755 ObjectIdGetDatum(namespaceOid));
1756 if (!HeapTupleIsValid(tup))
1758 (errcode(ERRCODE_UNDEFINED_OBJECT),
1759 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
1760 opfname, access_method)));
1762 opfOid = HeapTupleGetOid(tup);
1766 opfOid = OpfamilynameGetOpfid(amOid, opfname);
1767 if (!OidIsValid(opfOid))
1769 (errcode(ERRCODE_UNDEFINED_OBJECT),
1770 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
1771 opfname, access_method)));
1773 tup = SearchSysCacheCopy1(OPFAMILYOID, ObjectIdGetDatum(opfOid));
1774 if (!HeapTupleIsValid(tup)) /* should not happen */
1775 elog(ERROR, "cache lookup failed for opfamily %u", opfOid);
1777 namespaceOid = ((Form_pg_opfamily) GETSTRUCT(tup))->opfnamespace;
1780 /* make sure the new name doesn't exist */
1781 if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
1782 ObjectIdGetDatum(amOid),
1783 CStringGetDatum(newname),
1784 ObjectIdGetDatum(namespaceOid)))
1787 (errcode(ERRCODE_DUPLICATE_OBJECT),
1788 errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
1789 newname, access_method,
1790 get_namespace_name(namespaceOid))));
1794 if (!pg_opfamily_ownercheck(opfOid, GetUserId()))
1795 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
1796 NameListToString(name));
1798 /* must have CREATE privilege on namespace */
1799 aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
1800 if (aclresult != ACLCHECK_OK)
1801 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1802 get_namespace_name(namespaceOid));
1805 namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname);
1806 simple_heap_update(rel, &tup->t_self, tup);
1807 CatalogUpdateIndexes(rel, tup);
1809 heap_close(rel, NoLock);
1810 heap_freetuple(tup);
1814 * Change opclass owner by name
1817 AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
1824 amOid = get_am_oid(access_method, false);
1826 rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
1828 /* Look up the opclass. */
1829 origtup = OpClassCacheLookup(amOid, name, false);
1830 tup = heap_copytuple(origtup);
1831 ReleaseSysCache(origtup);
1833 AlterOpClassOwner_internal(rel, tup, newOwnerId);
1835 heap_freetuple(tup);
1836 heap_close(rel, NoLock);
1840 * Change operator class owner, specified by OID
1843 AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId)
1848 rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
1850 tup = SearchSysCacheCopy1(CLAOID, ObjectIdGetDatum(opclassOid));
1851 if (!HeapTupleIsValid(tup))
1852 elog(ERROR, "cache lookup failed for opclass %u", opclassOid);
1854 AlterOpClassOwner_internal(rel, tup, newOwnerId);
1856 heap_freetuple(tup);
1857 heap_close(rel, NoLock);
1861 * The first parameter is pg_opclass, opened and suitably locked. The second
1862 * parameter is a copy of the tuple from pg_opclass we want to modify.
1865 AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
1868 AclResult aclresult;
1869 Form_pg_opclass opcForm;
1871 Assert(tup->t_tableOid == OperatorClassRelationId);
1872 Assert(RelationGetRelid(rel) == OperatorClassRelationId);
1874 opcForm = (Form_pg_opclass) GETSTRUCT(tup);
1876 namespaceOid = opcForm->opcnamespace;
1879 * If the new owner is the same as the existing owner, consider the
1880 * command to have succeeded. This is for dump restoration purposes.
1882 if (opcForm->opcowner != newOwnerId)
1884 /* Superusers can always do it */
1887 /* Otherwise, must be owner of the existing object */
1888 if (!pg_opclass_ownercheck(HeapTupleGetOid(tup), GetUserId()))
1889 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
1890 NameStr(opcForm->opcname));
1892 /* Must be able to become new owner */
1893 check_is_member_of_role(GetUserId(), newOwnerId);
1895 /* New owner must have CREATE privilege on namespace */
1896 aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
1898 if (aclresult != ACLCHECK_OK)
1899 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1900 get_namespace_name(namespaceOid));
1904 * Modify the owner --- okay to scribble on tup because it's a copy
1906 opcForm->opcowner = newOwnerId;
1908 simple_heap_update(rel, &tup->t_self, tup);
1910 CatalogUpdateIndexes(rel, tup);
1912 /* Update owner dependency reference */
1913 changeDependencyOnOwner(OperatorClassRelationId, HeapTupleGetOid(tup),
1919 * ALTER OPERATOR CLASS any_name USING access_method SET SCHEMA name
1922 AlterOpClassNamespace(List *name, char *access_method, const char *newschema)
1929 amOid = get_am_oid(access_method, false);
1931 rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
1933 /* Look up the opclass */
1934 opclassOid = get_opclass_oid(amOid, name, false);
1936 /* get schema OID */
1937 nspOid = LookupCreationNamespace(newschema);
1939 AlterObjectNamespace(rel, CLAOID, -1,
1941 Anum_pg_opclass_opcname,
1942 Anum_pg_opclass_opcnamespace,
1943 Anum_pg_opclass_opcowner,
1946 heap_close(rel, RowExclusiveLock);
1950 AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid)
1955 rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
1958 AlterObjectNamespace(rel, CLAOID, -1,
1959 opclassOid, newNspOid,
1960 Anum_pg_opclass_opcname,
1961 Anum_pg_opclass_opcnamespace,
1962 Anum_pg_opclass_opcowner,
1965 heap_close(rel, RowExclusiveLock);
1971 * Change opfamily owner by name
1974 AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId)
1982 amOid = get_am_oid(access_method, false);
1984 rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
1987 * Look up the opfamily
1989 DeconstructQualifiedName(name, &schemaname, &opfname);
1995 namespaceOid = LookupExplicitNamespace(schemaname);
1997 tup = SearchSysCacheCopy3(OPFAMILYAMNAMENSP,
1998 ObjectIdGetDatum(amOid),
1999 PointerGetDatum(opfname),
2000 ObjectIdGetDatum(namespaceOid));
2001 if (!HeapTupleIsValid(tup))
2003 (errcode(ERRCODE_UNDEFINED_OBJECT),
2004 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
2005 opfname, access_method)));
2011 opfOid = OpfamilynameGetOpfid(amOid, opfname);
2012 if (!OidIsValid(opfOid))
2014 (errcode(ERRCODE_UNDEFINED_OBJECT),
2015 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
2016 opfname, access_method)));
2018 tup = SearchSysCacheCopy1(OPFAMILYOID, ObjectIdGetDatum(opfOid));
2019 if (!HeapTupleIsValid(tup)) /* should not happen */
2020 elog(ERROR, "cache lookup failed for opfamily %u", opfOid);
2023 AlterOpFamilyOwner_internal(rel, tup, newOwnerId);
2025 heap_freetuple(tup);
2026 heap_close(rel, NoLock);
2030 * Change operator family owner, specified by OID
2033 AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId)
2038 rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
2040 tup = SearchSysCacheCopy1(OPFAMILYOID, ObjectIdGetDatum(opfamilyOid));
2041 if (!HeapTupleIsValid(tup))
2042 elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid);
2044 AlterOpFamilyOwner_internal(rel, tup, newOwnerId);
2046 heap_freetuple(tup);
2047 heap_close(rel, NoLock);
2051 * The first parameter is pg_opfamily, opened and suitably locked. The second
2052 * parameter is a copy of the tuple from pg_opfamily we want to modify.
2055 AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
2058 AclResult aclresult;
2059 Form_pg_opfamily opfForm;
2061 Assert(tup->t_tableOid == OperatorFamilyRelationId);
2062 Assert(RelationGetRelid(rel) == OperatorFamilyRelationId);
2064 opfForm = (Form_pg_opfamily) GETSTRUCT(tup);
2066 namespaceOid = opfForm->opfnamespace;
2069 * If the new owner is the same as the existing owner, consider the
2070 * command to have succeeded. This is for dump restoration purposes.
2072 if (opfForm->opfowner != newOwnerId)
2074 /* Superusers can always do it */
2077 /* Otherwise, must be owner of the existing object */
2078 if (!pg_opfamily_ownercheck(HeapTupleGetOid(tup), GetUserId()))
2079 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
2080 NameStr(opfForm->opfname));
2082 /* Must be able to become new owner */
2083 check_is_member_of_role(GetUserId(), newOwnerId);
2085 /* New owner must have CREATE privilege on namespace */
2086 aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
2088 if (aclresult != ACLCHECK_OK)
2089 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
2090 get_namespace_name(namespaceOid));
2094 * Modify the owner --- okay to scribble on tup because it's a copy
2096 opfForm->opfowner = newOwnerId;
2098 simple_heap_update(rel, &tup->t_self, tup);
2100 CatalogUpdateIndexes(rel, tup);
2102 /* Update owner dependency reference */
2103 changeDependencyOnOwner(OperatorFamilyRelationId, HeapTupleGetOid(tup),
2109 * get_am_oid - given an access method name, look up the OID
2111 * If missing_ok is false, throw an error if access method not found. If
2112 * true, just return InvalidOid.
2115 get_am_oid(const char *amname, bool missing_ok)
2119 oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
2120 if (!OidIsValid(oid) && !missing_ok)
2122 (errcode(ERRCODE_UNDEFINED_OBJECT),
2123 errmsg("access method \"%s\" does not exist", amname)));
2128 * ALTER OPERATOR FAMILY any_name USING access_method SET SCHEMA name
2131 AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema)
2138 amOid = get_am_oid(access_method, false);
2140 rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
2142 /* Look up the opfamily */
2143 opfamilyOid = get_opfamily_oid(amOid, name, false);
2145 /* get schema OID */
2146 nspOid = LookupCreationNamespace(newschema);
2148 AlterObjectNamespace(rel, OPFAMILYOID, -1,
2149 opfamilyOid, nspOid,
2150 Anum_pg_opfamily_opfname,
2151 Anum_pg_opfamily_opfnamespace,
2152 Anum_pg_opfamily_opfowner,
2155 heap_close(rel, RowExclusiveLock);
2159 AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid)
2164 rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
2167 AlterObjectNamespace(rel, OPFAMILYOID, -1,
2168 opfamilyOid, newNspOid,
2169 Anum_pg_opfamily_opfname,
2170 Anum_pg_opfamily_opfnamespace,
2171 Anum_pg_opfamily_opfowner,
2174 heap_close(rel, RowExclusiveLock);