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