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