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