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