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