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