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