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