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