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