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