Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pg_operator.c
4 : : * routines to support manipulation of the pg_operator relation
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/catalog/pg_operator.c
12 : : *
13 : : * NOTES
14 : : * these routines moved here from commands/define.c and somewhat cleaned up.
15 : : *
16 : : *-------------------------------------------------------------------------
17 : : */
18 : : #include "postgres.h"
19 : :
20 : : #include "access/htup_details.h"
21 : : #include "access/table.h"
22 : : #include "access/xact.h"
23 : : #include "catalog/catalog.h"
24 : : #include "catalog/dependency.h"
25 : : #include "catalog/indexing.h"
26 : : #include "catalog/namespace.h"
27 : : #include "catalog/objectaccess.h"
28 : : #include "catalog/pg_namespace.h"
29 : : #include "catalog/pg_operator.h"
30 : : #include "catalog/pg_proc.h"
31 : : #include "catalog/pg_type.h"
32 : : #include "miscadmin.h"
33 : : #include "parser/parse_oper.h"
34 : : #include "utils/acl.h"
35 : : #include "utils/builtins.h"
36 : : #include "utils/lsyscache.h"
37 : : #include "utils/rel.h"
38 : : #include "utils/syscache.h"
39 : :
40 : :
41 : : static Oid OperatorGet(const char *operatorName,
42 : : Oid operatorNamespace,
43 : : Oid leftObjectId,
44 : : Oid rightObjectId,
45 : : bool *defined);
46 : :
47 : : static Oid OperatorShellMake(const char *operatorName,
48 : : Oid operatorNamespace,
49 : : Oid leftTypeId,
50 : : Oid rightTypeId);
51 : :
52 : : static Oid get_other_operator(List *otherOp,
53 : : Oid otherLeftTypeId, Oid otherRightTypeId,
54 : : const char *operatorName, Oid operatorNamespace,
55 : : Oid leftTypeId, Oid rightTypeId);
56 : :
57 : :
58 : : /*
59 : : * Check whether a proposed operator name is legal
60 : : *
61 : : * This had better match the behavior of parser/scan.l!
62 : : *
63 : : * We need this because the parser is not smart enough to check that
64 : : * the arguments of CREATE OPERATOR's COMMUTATOR, NEGATOR, etc clauses
65 : : * are operator names rather than some other lexical entity.
66 : : */
67 : : static bool
8210 tgl@sss.pgh.pa.us 68 :CBC 1028 : validOperatorName(const char *name)
69 : : {
8207 bruce@momjian.us 70 : 1028 : size_t len = strlen(name);
71 : :
72 : : /* Can't be empty or too long */
8210 tgl@sss.pgh.pa.us 73 [ + - - + ]: 1028 : if (len == 0 || len >= NAMEDATALEN)
8210 tgl@sss.pgh.pa.us 74 :UBC 0 : return false;
75 : :
76 : : /* Can't contain any invalid characters */
77 : : /* Test string here should match op_chars in scan.l */
7573 tgl@sss.pgh.pa.us 78 [ - + ]:CBC 1028 : if (strspn(name, "~!@#^&|`?+-*/%<>=") != len)
8210 tgl@sss.pgh.pa.us 79 :UBC 0 : return false;
80 : :
81 : : /* Can't contain slash-star or dash-dash (comment starts) */
8210 tgl@sss.pgh.pa.us 82 [ + - - + ]:CBC 1028 : if (strstr(name, "/*") || strstr(name, "--"))
8210 tgl@sss.pgh.pa.us 83 :UBC 0 : return false;
84 : :
85 : : /*
86 : : * For SQL standard compatibility, '+' and '-' cannot be the last char of
87 : : * a multi-char operator unless the operator contains chars that are not
88 : : * in SQL operators. The idea is to lex '=-' as two operators, but not to
89 : : * forbid operator names like '?-' that could not be sequences of standard
90 : : * SQL operators.
91 : : */
8210 tgl@sss.pgh.pa.us 92 [ + + ]:CBC 1028 : if (len > 1 &&
8207 bruce@momjian.us 93 [ + - ]: 697 : (name[len - 1] == '+' ||
94 [ + + ]: 697 : name[len - 1] == '-'))
95 : : {
96 : : int ic;
97 : :
98 [ + - ]: 8 : for (ic = len - 2; ic >= 0; ic--)
99 : : {
7573 tgl@sss.pgh.pa.us 100 [ + + ]: 8 : if (strchr("~!@#^&|`?%", name[ic]))
8210 101 : 4 : break;
102 : : }
103 [ - + ]: 4 : if (ic < 0)
8210 tgl@sss.pgh.pa.us 104 :UBC 0 : return false; /* nope, not valid */
105 : : }
106 : :
107 : : /* != isn't valid either, because parser will convert it to <> */
8210 tgl@sss.pgh.pa.us 108 [ - + ]:CBC 1028 : if (strcmp(name, "!=") == 0)
8210 tgl@sss.pgh.pa.us 109 :UBC 0 : return false;
110 : :
8210 tgl@sss.pgh.pa.us 111 :CBC 1028 : return true;
112 : : }
113 : :
114 : :
115 : : /*
116 : : * OperatorGet
117 : : *
118 : : * finds an operator given an exact specification (name, namespace,
119 : : * left and right type IDs).
120 : : *
121 : : * *defined is set true if defined (not a shell)
122 : : */
123 : : static Oid
8052 124 : 753 : OperatorGet(const char *operatorName,
125 : : Oid operatorNamespace,
126 : : Oid leftObjectId,
127 : : Oid rightObjectId,
128 : : bool *defined)
129 : : {
130 : : HeapTuple tup;
131 : : Oid operatorObjectId;
132 : :
5173 rhaas@postgresql.org 133 : 753 : tup = SearchSysCache4(OPERNAMENSP,
134 : : PointerGetDatum(operatorName),
135 : : ObjectIdGetDatum(leftObjectId),
136 : : ObjectIdGetDatum(rightObjectId),
137 : : ObjectIdGetDatum(operatorNamespace));
9135 tgl@sss.pgh.pa.us 138 [ + + ]: 753 : if (HeapTupleIsValid(tup))
139 : : {
1972 andres@anarazel.de 140 : 261 : Form_pg_operator oprform = (Form_pg_operator) GETSTRUCT(tup);
141 : :
142 : 261 : operatorObjectId = oprform->oid;
143 : 261 : *defined = RegProcedureIsValid(oprform->oprcode);
8034 tgl@sss.pgh.pa.us 144 : 261 : ReleaseSysCache(tup);
145 : : }
146 : : else
147 : : {
9135 148 : 492 : operatorObjectId = InvalidOid;
149 : 492 : *defined = false;
150 : : }
151 : :
8034 152 : 753 : return operatorObjectId;
153 : : }
154 : :
155 : : /*
156 : : * OperatorLookup
157 : : *
158 : : * looks up an operator given a possibly-qualified name and
159 : : * left and right type IDs.
160 : : *
161 : : * *defined is set true if defined (not a shell)
162 : : */
163 : : Oid
164 : 855 : OperatorLookup(List *operatorName,
165 : : Oid leftObjectId,
166 : : Oid rightObjectId,
167 : : bool *defined)
168 : : {
169 : : Oid operatorObjectId;
170 : : RegProcedure oprcode;
171 : :
6606 172 : 855 : operatorObjectId = LookupOperName(NULL, operatorName,
173 : : leftObjectId, rightObjectId,
174 : : true, -1);
8034 175 [ + + ]: 855 : if (!OidIsValid(operatorObjectId))
176 : : {
177 : 368 : *defined = false;
178 : 368 : return InvalidOid;
179 : : }
180 : :
181 : 487 : oprcode = get_opcode(operatorObjectId);
182 : 487 : *defined = RegProcedureIsValid(oprcode);
183 : :
9202 bruce@momjian.us 184 : 487 : return operatorObjectId;
185 : : }
186 : :
187 : :
188 : : /*
189 : : * OperatorShellMake
190 : : * Make a "shell" entry for a not-yet-existing operator.
191 : : */
192 : : static Oid
8052 tgl@sss.pgh.pa.us 193 : 275 : OperatorShellMake(const char *operatorName,
194 : : Oid operatorNamespace,
195 : : Oid leftTypeId,
196 : : Oid rightTypeId)
197 : : {
198 : : Relation pg_operator_desc;
199 : : Oid operatorObjectId;
200 : : int i;
201 : : HeapTuple tup;
202 : : Datum values[Natts_pg_operator];
203 : : bool nulls[Natts_pg_operator];
204 : : NameData oname;
205 : : TupleDesc tupDesc;
206 : :
207 : : /*
208 : : * validate operator name
209 : : */
8210 210 [ - + ]: 275 : if (!validOperatorName(operatorName))
7573 tgl@sss.pgh.pa.us 211 [ # # ]:UBC 0 : ereport(ERROR,
212 : : (errcode(ERRCODE_INVALID_NAME),
213 : : errmsg("\"%s\" is not a valid operator name",
214 : : operatorName)));
215 : :
216 : : /*
217 : : * open pg_operator
218 : : */
1910 andres@anarazel.de 219 :CBC 275 : pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock);
1972 220 : 275 : tupDesc = pg_operator_desc->rd_att;
221 : :
222 : : /*
223 : : * initialize our *nulls and *values arrays
224 : : */
9716 bruce@momjian.us 225 [ + + ]: 4400 : for (i = 0; i < Natts_pg_operator; ++i)
226 : : {
5642 tgl@sss.pgh.pa.us 227 : 4125 : nulls[i] = false;
2489 228 : 4125 : values[i] = (Datum) NULL; /* redundant, but safe */
229 : : }
230 : :
231 : : /*
232 : : * initialize values[] with the operator name and input data types. Note
233 : : * that oprcode is set to InvalidOid, indicating it's a shell.
234 : : */
1972 andres@anarazel.de 235 : 275 : operatorObjectId = GetNewOidWithIndex(pg_operator_desc, OperatorOidIndexId,
236 : : Anum_pg_operator_oid);
237 : 275 : values[Anum_pg_operator_oid - 1] = ObjectIdGetDatum(operatorObjectId);
9510 scrappy@hub.org 238 : 275 : namestrcpy(&oname, operatorName);
4686 tgl@sss.pgh.pa.us 239 : 275 : values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname);
240 : 275 : values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace);
241 : 275 : values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId());
1305 242 [ + - ]: 275 : values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? 'b' : 'l');
4686 243 : 275 : values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(false);
244 : 275 : values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(false);
245 : 275 : values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId);
246 : 275 : values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId);
247 : 275 : values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(InvalidOid);
248 : 275 : values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(InvalidOid);
249 : 275 : values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(InvalidOid);
250 : 275 : values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(InvalidOid);
251 : 275 : values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
252 : 275 : values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid);
253 : :
254 : : /*
255 : : * create a new operator tuple
256 : : */
5642 257 : 275 : tup = heap_form_tuple(tupDesc, values, nulls);
258 : :
259 : : /*
260 : : * insert our "shell" operator tuple
261 : : */
1972 andres@anarazel.de 262 : 275 : CatalogTupleInsert(pg_operator_desc, tup);
263 : :
264 : : /* Add dependencies for the entry */
971 tgl@sss.pgh.pa.us 265 : 275 : makeOperatorDependencies(tup, true, false);
266 : :
8886 JanWieck@Yahoo.com 267 : 275 : heap_freetuple(tup);
268 : :
269 : : /* Post creation hook for new shell operator */
4057 rhaas@postgresql.org 270 [ - + ]: 275 : InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0);
271 : :
272 : : /*
273 : : * Make sure the tuple is visible for subsequent lookups/updates.
274 : : */
5720 tgl@sss.pgh.pa.us 275 : 275 : CommandCounterIncrement();
276 : :
277 : : /*
278 : : * close the operator relation and return the oid.
279 : : */
1910 andres@anarazel.de 280 : 275 : table_close(pg_operator_desc, RowExclusiveLock);
281 : :
9202 bruce@momjian.us 282 : 275 : return operatorObjectId;
283 : : }
284 : :
285 : : /*
286 : : * OperatorCreate
287 : : *
288 : : * "X" indicates an optional argument (i.e. one that can be NULL or 0)
289 : : * operatorName name for new operator
290 : : * operatorNamespace namespace for new operator
291 : : * leftTypeId X left type ID
292 : : * rightTypeId X right type ID
293 : : * procedureId procedure ID for operator
294 : : * commutatorName X commutator operator
295 : : * negatorName X negator operator
296 : : * restrictionId X restriction selectivity procedure ID
297 : : * joinId X join selectivity procedure ID
298 : : * canMerge merge join can be used with this operator
299 : : * canHash hash join can be used with this operator
300 : : *
301 : : * The caller should have validated properties and permissions for the
302 : : * objects passed as OID references. We must handle the commutator and
303 : : * negator operator references specially, however, since those need not
304 : : * exist beforehand.
305 : : *
306 : : * This routine gets complicated because it allows the user to
307 : : * specify operators that do not exist. For example, if operator
308 : : * "op" is being defined, the negator operator "negop" and the
309 : : * commutator "commop" can also be defined without specifying
310 : : * any information other than their names. Since in order to
311 : : * add "op" to the PG_OPERATOR catalog, all the Oid's for these
312 : : * operators must be placed in the fields of "op", a forward
313 : : * declaration is done on the commutator and negator operators.
314 : : * This is called creating a shell, and its main effect is to
315 : : * create a tuple in the PG_OPERATOR catalog with minimal
316 : : * information about the operator (just its name and types).
317 : : * Forward declaration is used only for this purpose, it is
318 : : * not available to the user as it is for type definition.
319 : : */
320 : : ObjectAddress
8034 tgl@sss.pgh.pa.us 321 : 753 : OperatorCreate(const char *operatorName,
322 : : Oid operatorNamespace,
323 : : Oid leftTypeId,
324 : : Oid rightTypeId,
325 : : Oid procedureId,
326 : : List *commutatorName,
327 : : List *negatorName,
328 : : Oid restrictionId,
329 : : Oid joinId,
330 : : bool canMerge,
331 : : bool canHash)
332 : : {
333 : : Relation pg_operator_desc;
334 : : HeapTuple tup;
335 : : bool isUpdate;
336 : : bool nulls[Natts_pg_operator];
337 : : bool replaces[Natts_pg_operator];
338 : : Datum values[Natts_pg_operator];
339 : : Oid operatorObjectId;
340 : : bool operatorAlreadyDefined;
341 : : Oid operResultType;
342 : : Oid commutatorId,
343 : : negatorId;
9135 344 : 753 : bool selfCommutator = false;
345 : : NameData oname;
346 : : int i;
347 : : ObjectAddress address;
348 : :
349 : : /*
350 : : * Sanity checks
351 : : */
8052 352 [ - + ]: 753 : if (!validOperatorName(operatorName))
7573 tgl@sss.pgh.pa.us 353 [ # # ]:UBC 0 : ereport(ERROR,
354 : : (errcode(ERRCODE_INVALID_NAME),
355 : : errmsg("\"%s\" is not a valid operator name",
356 : : operatorName)));
357 : :
5720 tgl@sss.pgh.pa.us 358 :CBC 753 : operResultType = get_func_rettype(procedureId);
359 : :
177 tgl@sss.pgh.pa.us 360 :GNC 753 : OperatorValidateParams(leftTypeId,
361 : : rightTypeId,
362 : : operResultType,
363 : : commutatorName != NIL,
364 : : negatorName != NIL,
365 : : OidIsValid(restrictionId),
366 : : OidIsValid(joinId),
367 : : canMerge,
368 : : canHash);
369 : :
9716 bruce@momjian.us 370 :CBC 753 : operatorObjectId = OperatorGet(operatorName,
371 : : operatorNamespace,
372 : : leftTypeId,
373 : : rightTypeId,
374 : : &operatorAlreadyDefined);
375 : :
9135 tgl@sss.pgh.pa.us 376 [ - + ]: 753 : if (operatorAlreadyDefined)
7573 tgl@sss.pgh.pa.us 377 [ # # ]:UBC 0 : ereport(ERROR,
378 : : (errcode(ERRCODE_DUPLICATE_FUNCTION),
379 : : errmsg("operator %s already exists",
380 : : operatorName)));
381 : :
382 : : /*
383 : : * At this point, if operatorObjectId is not InvalidOid then we are
384 : : * filling in a previously-created shell. Insist that the user own any
385 : : * such shell.
386 : : */
5720 tgl@sss.pgh.pa.us 387 [ + + ]:CBC 753 : if (OidIsValid(operatorObjectId) &&
518 peter@eisentraut.org 388 [ - + ]: 261 : !object_ownercheck(OperatorRelationId, operatorObjectId, GetUserId()))
2325 peter_e@gmx.net 389 :UBC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
390 : : operatorName);
391 : :
392 : : /*
393 : : * Set up the other operators. If they do not currently exist, create
394 : : * shells in order to get ObjectId's.
395 : : */
396 : :
8034 tgl@sss.pgh.pa.us 397 [ + + ]:CBC 753 : if (commutatorName)
398 : : {
399 : : /* commutator has reversed arg types */
400 : 493 : commutatorId = get_other_operator(commutatorName,
401 : : rightTypeId, leftTypeId,
402 : : operatorName, operatorNamespace,
403 : : leftTypeId, rightTypeId);
404 : :
405 : : /* Permission check: must own other operator */
5720 406 [ + + ]: 493 : if (OidIsValid(commutatorId) &&
518 peter@eisentraut.org 407 [ - + ]: 403 : !object_ownercheck(OperatorRelationId, commutatorId, GetUserId()))
2325 peter_e@gmx.net 408 :UBC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
5720 tgl@sss.pgh.pa.us 409 : 0 : NameListToString(commutatorName));
410 : :
411 : : /*
412 : : * If self-linkage to the new operator is requested, we'll fix it
413 : : * below. (In case of self-linkage to an existing shell operator, we
414 : : * need do nothing special.)
415 : : */
8034 tgl@sss.pgh.pa.us 416 [ + + ]:CBC 493 : if (!OidIsValid(commutatorId))
417 : 90 : selfCommutator = true;
418 : : }
419 : : else
420 : 260 : commutatorId = InvalidOid;
421 : :
422 [ + + ]: 753 : if (negatorName)
423 : : {
424 : : /* negator has same arg types */
425 : 335 : negatorId = get_other_operator(negatorName,
426 : : leftTypeId, rightTypeId,
427 : : operatorName, operatorNamespace,
428 : : leftTypeId, rightTypeId);
429 : :
430 : : /* Permission check: must own other operator */
5720 431 [ + + ]: 335 : if (OidIsValid(negatorId) &&
518 peter@eisentraut.org 432 [ - + ]: 332 : !object_ownercheck(OperatorRelationId, negatorId, GetUserId()))
2325 peter_e@gmx.net 433 :UBC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
5720 tgl@sss.pgh.pa.us 434 : 0 : NameListToString(negatorName));
435 : :
436 : : /*
437 : : * Prevent self negation, as it doesn't make sense. It's self
438 : : * negation if result is InvalidOid (negator would be the same
439 : : * operator but it doesn't exist yet) or operatorObjectId (we are
440 : : * replacing a shell that would need to be its own negator).
441 : : */
177 tgl@sss.pgh.pa.us 442 [ + + + + ]:GNC 335 : if (!OidIsValid(negatorId) || negatorId == operatorObjectId)
443 [ + - ]: 6 : ereport(ERROR,
444 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
445 : : errmsg("operator cannot be its own negator")));
446 : : }
447 : : else
8034 tgl@sss.pgh.pa.us 448 :CBC 418 : negatorId = InvalidOid;
449 : :
450 : : /*
451 : : * set up values in the operator tuple
452 : : */
453 : :
5720 454 [ + + ]: 11952 : for (i = 0; i < Natts_pg_operator; ++i)
455 : : {
456 : 11205 : values[i] = (Datum) NULL;
5642 457 : 11205 : replaces[i] = true;
458 : 11205 : nulls[i] = false;
459 : : }
460 : :
5720 461 : 747 : namestrcpy(&oname, operatorName);
4686 462 : 747 : values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname);
463 : 747 : values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace);
464 : 747 : values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId());
1305 465 [ + + ]: 747 : values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? 'b' : 'l');
4686 466 : 747 : values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge);
467 : 747 : values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash);
468 : 747 : values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId);
469 : 747 : values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId);
470 : 747 : values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(operResultType);
471 : 747 : values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorId);
472 : 747 : values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorId);
473 : 747 : values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(procedureId);
474 : 747 : values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionId);
475 : 747 : values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinId);
476 : :
1910 andres@anarazel.de 477 : 747 : pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock);
478 : :
479 : : /*
480 : : * If we are replacing an operator shell, update; else insert
481 : : */
9716 bruce@momjian.us 482 [ + + ]: 747 : if (operatorObjectId)
483 : : {
3027 tgl@sss.pgh.pa.us 484 : 258 : isUpdate = true;
485 : :
5173 rhaas@postgresql.org 486 : 258 : tup = SearchSysCacheCopy1(OPEROID,
487 : : ObjectIdGetDatum(operatorObjectId));
8034 tgl@sss.pgh.pa.us 488 [ - + ]: 258 : if (!HeapTupleIsValid(tup))
7573 tgl@sss.pgh.pa.us 489 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator %u",
490 : : operatorObjectId);
491 : :
1972 andres@anarazel.de 492 :CBC 258 : replaces[Anum_pg_operator_oid - 1] = false;
5642 tgl@sss.pgh.pa.us 493 : 258 : tup = heap_modify_tuple(tup,
494 : : RelationGetDescr(pg_operator_desc),
495 : : values,
496 : : nulls,
497 : : replaces);
498 : :
2630 alvherre@alvh.no-ip. 499 : 258 : CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup);
500 : : }
501 : : else
502 : : {
3027 tgl@sss.pgh.pa.us 503 : 489 : isUpdate = false;
504 : :
1972 andres@anarazel.de 505 : 489 : operatorObjectId = GetNewOidWithIndex(pg_operator_desc,
506 : : OperatorOidIndexId,
507 : : Anum_pg_operator_oid);
508 : 489 : values[Anum_pg_operator_oid - 1] = ObjectIdGetDatum(operatorObjectId);
509 : :
3027 tgl@sss.pgh.pa.us 510 : 489 : tup = heap_form_tuple(RelationGetDescr(pg_operator_desc),
511 : : values, nulls);
512 : :
1972 andres@anarazel.de 513 : 489 : CatalogTupleInsert(pg_operator_desc, tup);
514 : : }
515 : :
516 : : /* Add dependencies for the entry */
971 tgl@sss.pgh.pa.us 517 : 747 : address = makeOperatorDependencies(tup, true, isUpdate);
518 : :
519 : : /*
520 : : * If a commutator and/or negator link is provided, update the other
521 : : * operator(s) to point at this one, if they don't already have a link.
522 : : * This supports an alternative style of operator definition wherein the
523 : : * user first defines one operator without giving negator or commutator,
524 : : * then defines the other operator of the pair with the proper commutator
525 : : * or negator attribute. That style doesn't require creation of a shell,
526 : : * and it's the only style that worked right before Postgres version 6.5.
527 : : * This code also takes care of the situation where the new operator is
528 : : * its own commutator.
529 : : */
9135 530 [ + + ]: 746 : if (selfCommutator)
531 : 90 : commutatorId = operatorObjectId;
532 : :
533 [ + + + + ]: 746 : if (OidIsValid(commutatorId) || OidIsValid(negatorId))
2942 534 : 528 : OperatorUpd(operatorObjectId, commutatorId, negatorId, false);
535 : :
536 : : /* Post creation hook for new operator */
177 tgl@sss.pgh.pa.us 537 [ - + ]:GNC 740 : InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0);
538 : :
539 : 740 : table_close(pg_operator_desc, RowExclusiveLock);
540 : :
3330 alvherre@alvh.no-ip. 541 :CBC 740 : return address;
542 : : }
543 : :
544 : : /*
545 : : * OperatorValidateParams
546 : : *
547 : : * Check that an operator with argument types leftTypeId and rightTypeId,
548 : : * returning operResultType, can have the attributes that are set to true.
549 : : * Raise an error for any disallowed attribute.
550 : : *
551 : : * Note: in ALTER OPERATOR, we only bother to pass "true" for attributes
552 : : * the command is trying to set, not those that may already be set.
553 : : * This is OK as long as the attribute checks are independent.
554 : : */
555 : : void
177 tgl@sss.pgh.pa.us 556 :GNC 1027 : OperatorValidateParams(Oid leftTypeId,
557 : : Oid rightTypeId,
558 : : Oid operResultType,
559 : : bool hasCommutator,
560 : : bool hasNegator,
561 : : bool hasRestrictionSelectivity,
562 : : bool hasJoinSelectivity,
563 : : bool canMerge,
564 : : bool canHash)
565 : : {
566 [ + + - + ]: 1027 : if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId)))
567 : : {
568 : : /* If it's not a binary op, these things mustn't be set: */
569 [ - + ]: 34 : if (hasCommutator)
177 tgl@sss.pgh.pa.us 570 [ # # ]:UNC 0 : ereport(ERROR,
571 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
572 : : errmsg("only binary operators can have commutators")));
177 tgl@sss.pgh.pa.us 573 [ - + ]:GNC 34 : if (hasJoinSelectivity)
177 tgl@sss.pgh.pa.us 574 [ # # ]:UNC 0 : ereport(ERROR,
575 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
576 : : errmsg("only binary operators can have join selectivity")));
177 tgl@sss.pgh.pa.us 577 [ - + ]:GNC 34 : if (canMerge)
177 tgl@sss.pgh.pa.us 578 [ # # ]:UNC 0 : ereport(ERROR,
579 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
580 : : errmsg("only binary operators can merge join")));
177 tgl@sss.pgh.pa.us 581 [ - + ]:GNC 34 : if (canHash)
177 tgl@sss.pgh.pa.us 582 [ # # ]:UNC 0 : ereport(ERROR,
583 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
584 : : errmsg("only binary operators can hash")));
585 : : }
586 : :
177 tgl@sss.pgh.pa.us 587 [ + + ]:GNC 1027 : if (operResultType != BOOLOID)
588 : : {
589 : : /* If it's not a boolean op, these things mustn't be set: */
590 [ - + ]: 153 : if (hasNegator)
177 tgl@sss.pgh.pa.us 591 [ # # ]:UNC 0 : ereport(ERROR,
592 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
593 : : errmsg("only boolean operators can have negators")));
177 tgl@sss.pgh.pa.us 594 [ - + ]:GNC 153 : if (hasRestrictionSelectivity)
177 tgl@sss.pgh.pa.us 595 [ # # ]:UNC 0 : ereport(ERROR,
596 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
597 : : errmsg("only boolean operators can have restriction selectivity")));
177 tgl@sss.pgh.pa.us 598 [ - + ]:GNC 153 : if (hasJoinSelectivity)
177 tgl@sss.pgh.pa.us 599 [ # # ]:UNC 0 : ereport(ERROR,
600 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
601 : : errmsg("only boolean operators can have join selectivity")));
177 tgl@sss.pgh.pa.us 602 [ - + ]:GNC 153 : if (canMerge)
177 tgl@sss.pgh.pa.us 603 [ # # ]:UNC 0 : ereport(ERROR,
604 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
605 : : errmsg("only boolean operators can merge join")));
177 tgl@sss.pgh.pa.us 606 [ - + ]:GNC 153 : if (canHash)
177 tgl@sss.pgh.pa.us 607 [ # # ]:UNC 0 : ereport(ERROR,
608 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
609 : : errmsg("only boolean operators can hash")));
610 : : }
177 tgl@sss.pgh.pa.us 611 :GNC 1027 : }
612 : :
613 : : /*
614 : : * Try to lookup another operator (commutator, etc); return its OID
615 : : *
616 : : * If not found, check to see if it would be the same operator we are trying
617 : : * to define; if so, return InvalidOid. (Caller must decide whether
618 : : * that is sensible.) If it is not the same operator, create a shell
619 : : * operator.
620 : : */
621 : : static Oid
8034 tgl@sss.pgh.pa.us 622 :CBC 828 : get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId,
623 : : const char *operatorName, Oid operatorNamespace,
624 : : Oid leftTypeId, Oid rightTypeId)
625 : : {
626 : : Oid other_oid;
627 : : bool otherDefined;
628 : : char *otherName;
629 : : Oid otherNamespace;
630 : : AclResult aclresult;
631 : :
632 : 828 : other_oid = OperatorLookup(otherOp,
633 : : otherLeftTypeId,
634 : : otherRightTypeId,
635 : : &otherDefined);
636 : :
637 [ + + ]: 828 : if (OidIsValid(other_oid))
638 : : {
639 : : /* other op already in catalogs */
640 : 460 : return other_oid;
641 : : }
642 : :
643 : 368 : otherNamespace = QualifiedNameGetCreationNamespace(otherOp,
644 : : &otherName);
645 : :
646 [ + + + - ]: 368 : if (strcmp(otherName, operatorName) == 0 &&
647 [ + + ]: 137 : otherNamespace == operatorNamespace &&
648 [ + - ]: 93 : otherLeftTypeId == leftTypeId &&
649 : : otherRightTypeId == rightTypeId)
650 : : {
651 : : /* self-linkage to new operator; caller must handle this */
652 : 93 : return InvalidOid;
653 : : }
654 : :
655 : : /* not in catalogs, different from operator, so make shell */
656 : :
518 peter@eisentraut.org 657 : 275 : aclresult = object_aclcheck(NamespaceRelationId, otherNamespace, GetUserId(),
658 : : ACL_CREATE);
8023 tgl@sss.pgh.pa.us 659 [ - + ]: 275 : if (aclresult != ACLCHECK_OK)
2325 peter_e@gmx.net 660 :UBC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
7562 tgl@sss.pgh.pa.us 661 : 0 : get_namespace_name(otherNamespace));
662 : :
8034 tgl@sss.pgh.pa.us 663 :CBC 275 : other_oid = OperatorShellMake(otherName,
664 : : otherNamespace,
665 : : otherLeftTypeId,
666 : : otherRightTypeId);
667 : 275 : return other_oid;
668 : : }
669 : :
670 : : /*
671 : : * OperatorUpd
672 : : *
673 : : * For a given operator, look up its negator and commutator operators.
674 : : * When isDelete is false, update their negator and commutator fields to
675 : : * point back to the given operator; when isDelete is true, update those
676 : : * fields to be InvalidOid.
677 : : *
678 : : * The !isDelete case solves a problem for users who need to insert two new
679 : : * operators that are the negator or commutator of each other, while the
680 : : * isDelete case is needed so as not to leave dangling OID links behind
681 : : * after dropping an operator.
682 : : */
683 : : void
2942 684 : 711 : OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
685 : : {
686 : : Relation pg_operator_desc;
687 : : HeapTuple tup;
688 : :
689 : : /*
690 : : * If we're making an operator into its own commutator, then we need a
691 : : * command-counter increment here, since we've just inserted the tuple
692 : : * we're about to update. But when we're dropping an operator, we can
693 : : * skip this because we're at the beginning of the command.
694 : : */
695 [ + + ]: 711 : if (!isDelete)
696 : 546 : CommandCounterIncrement();
697 : :
698 : : /* Open the relation. */
1910 andres@anarazel.de 699 : 711 : pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock);
700 : :
701 : : /* Get a writable copy of the commutator's tuple. */
2942 tgl@sss.pgh.pa.us 702 [ + + ]: 711 : if (OidIsValid(commId))
703 : 667 : tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(commId));
704 : : else
705 : 44 : tup = NULL;
706 : :
707 : : /* Update the commutator's tuple if need be. */
708 [ + + ]: 711 : if (HeapTupleIsValid(tup))
709 : : {
710 : 667 : Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup);
711 : 667 : bool update_commutator = false;
712 : :
713 : : /*
714 : : * We can skip doing anything if the commutator's oprcom field is
715 : : * already what we want. While that's not expected in the isDelete
716 : : * case, it's perfectly possible when filling in a shell operator.
717 : : */
177 tgl@sss.pgh.pa.us 718 [ + + + - ]:GNC 667 : if (isDelete && OidIsValid(t->oprcom))
719 : : {
2942 tgl@sss.pgh.pa.us 720 :CBC 165 : t->oprcom = InvalidOid;
721 : 165 : update_commutator = true;
722 : : }
177 tgl@sss.pgh.pa.us 723 [ + - + + ]:GNC 502 : else if (!isDelete && t->oprcom != baseId)
724 : : {
725 : : /*
726 : : * If commutator's oprcom field is already set to point to some
727 : : * third operator, it's an error. Changing its link would be
728 : : * unsafe, and letting the inconsistency stand would not be good
729 : : * either. This might be indicative of catalog corruption, so
730 : : * don't assume t->oprcom is necessarily a valid operator.
731 : : */
732 [ + + ]: 291 : if (OidIsValid(t->oprcom))
733 : : {
734 : 6 : char *thirdop = get_opname(t->oprcom);
735 : :
736 [ + - ]: 6 : if (thirdop != NULL)
737 [ + - ]: 6 : ereport(ERROR,
738 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
739 : : errmsg("commutator operator %s is already the commutator of operator %s",
740 : : NameStr(t->oprname), thirdop)));
741 : : else
177 tgl@sss.pgh.pa.us 742 [ # # ]:UNC 0 : ereport(ERROR,
743 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
744 : : errmsg("commutator operator %s is already the commutator of operator %u",
745 : : NameStr(t->oprname), t->oprcom)));
746 : : }
747 : :
2942 tgl@sss.pgh.pa.us 748 :CBC 285 : t->oprcom = baseId;
749 : 285 : update_commutator = true;
750 : : }
751 : :
752 : : /* If any columns were found to need modification, update tuple. */
753 [ + + ]: 661 : if (update_commutator)
754 : : {
2630 alvherre@alvh.no-ip. 755 : 450 : CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup);
756 : :
757 : : /*
758 : : * Do CCI to make the updated tuple visible. We must do this in
759 : : * case the commutator is also the negator. (Which would be a
760 : : * logic error on the operator definer's part, but that's not a
761 : : * good reason to fail here.) We would need a CCI anyway in the
762 : : * deletion case for a self-commutator with no negator.
763 : : */
2942 tgl@sss.pgh.pa.us 764 : 450 : CommandCounterIncrement();
765 : : }
766 : : }
767 : :
768 : : /*
769 : : * Similarly find and update the negator, if any.
770 : : */
771 [ + + ]: 705 : if (OidIsValid(negId))
772 : 452 : tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(negId));
773 : : else
774 : 253 : tup = NULL;
775 : :
776 [ + + ]: 705 : if (HeapTupleIsValid(tup))
777 : : {
778 : 452 : Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup);
779 : 452 : bool update_negator = false;
780 : :
781 : : /*
782 : : * We can skip doing anything if the negator's oprnegate field is
783 : : * already what we want. While that's not expected in the isDelete
784 : : * case, it's perfectly possible when filling in a shell operator.
785 : : */
177 tgl@sss.pgh.pa.us 786 [ + + + - ]:GNC 452 : if (isDelete && OidIsValid(t->oprnegate))
787 : : {
2942 tgl@sss.pgh.pa.us 788 :CBC 114 : t->oprnegate = InvalidOid;
789 : 114 : update_negator = true;
790 : : }
177 tgl@sss.pgh.pa.us 791 [ + - + + ]:GNC 338 : else if (!isDelete && t->oprnegate != baseId)
792 : : {
793 : : /*
794 : : * If negator's oprnegate field is already set to point to some
795 : : * third operator, it's an error. Changing its link would be
796 : : * unsafe, and letting the inconsistency stand would not be good
797 : : * either. This might be indicative of catalog corruption, so
798 : : * don't assume t->oprnegate is necessarily a valid operator.
799 : : */
800 [ + + ]: 180 : if (OidIsValid(t->oprnegate))
801 : : {
802 : 6 : char *thirdop = get_opname(t->oprnegate);
803 : :
804 [ + - ]: 6 : if (thirdop != NULL)
805 [ + - ]: 6 : ereport(ERROR,
806 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
807 : : errmsg("negator operator %s is already the negator of operator %s",
808 : : NameStr(t->oprname), thirdop)));
809 : : else
177 tgl@sss.pgh.pa.us 810 [ # # ]:UNC 0 : ereport(ERROR,
811 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
812 : : errmsg("negator operator %s is already the negator of operator %u",
813 : : NameStr(t->oprname), t->oprnegate)));
814 : : }
815 : :
2942 tgl@sss.pgh.pa.us 816 :CBC 174 : t->oprnegate = baseId;
817 : 174 : update_negator = true;
818 : : }
819 : :
820 : : /* If any columns were found to need modification, update tuple. */
821 [ + + ]: 446 : if (update_negator)
822 : : {
2630 alvherre@alvh.no-ip. 823 : 288 : CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup);
824 : :
825 : : /*
826 : : * In the deletion case, do CCI to make the updated tuple visible.
827 : : * We must do this in case the operator is its own negator. (Which
828 : : * would be a logic error on the operator definer's part, but
829 : : * that's not a good reason to fail here.)
830 : : */
2942 tgl@sss.pgh.pa.us 831 [ + + ]: 288 : if (isDelete)
832 : 114 : CommandCounterIncrement();
833 : : }
834 : : }
835 : :
836 : : /* Close relation and release catalog lock. */
1910 andres@anarazel.de 837 : 699 : table_close(pg_operator_desc, RowExclusiveLock);
10141 scrappy@hub.org 838 : 699 : }
839 : :
840 : : /*
841 : : * Create dependencies for an operator (either a freshly inserted
842 : : * complete operator, a new shell operator, a just-updated shell,
843 : : * or an operator that's being modified by ALTER OPERATOR).
844 : : *
845 : : * makeExtensionDep should be true when making a new operator or
846 : : * replacing a shell, false for ALTER OPERATOR. Passing false
847 : : * will prevent any change in the operator's extension membership.
848 : : *
849 : : * NB: the OidIsValid tests in this routine are necessary, in case
850 : : * the given operator is a shell.
851 : : */
852 : : ObjectAddress
971 tgl@sss.pgh.pa.us 853 : 1296 : makeOperatorDependencies(HeapTuple tuple,
854 : : bool makeExtensionDep,
855 : : bool isUpdate)
856 : : {
7893 bruce@momjian.us 857 : 1296 : Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple);
858 : : ObjectAddress myself,
859 : : referenced;
860 : : ObjectAddresses *addrs;
861 : :
1383 michael@paquier.xyz 862 : 1296 : ObjectAddressSet(myself, OperatorRelationId, oper->oid);
863 : :
864 : : /*
865 : : * If we are updating the operator, delete any existing entries, except
866 : : * for extension membership which should remain the same.
867 : : */
3027 tgl@sss.pgh.pa.us 868 [ + + ]: 1296 : if (isUpdate)
869 : : {
870 : 532 : deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
871 : 532 : deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
872 : : }
873 : :
1317 michael@paquier.xyz 874 : 1296 : addrs = new_object_addresses();
875 : :
876 : : /* Dependency on namespace */
7941 tgl@sss.pgh.pa.us 877 [ + - ]: 1296 : if (OidIsValid(oper->oprnamespace))
878 : : {
1383 michael@paquier.xyz 879 : 1296 : ObjectAddressSet(referenced, NamespaceRelationId, oper->oprnamespace);
1317 880 : 1296 : add_exact_object_address(&referenced, addrs);
881 : : }
882 : :
883 : : /* Dependency on left type */
7943 tgl@sss.pgh.pa.us 884 [ + + ]: 1296 : if (OidIsValid(oper->oprleft))
885 : : {
1383 michael@paquier.xyz 886 : 1262 : ObjectAddressSet(referenced, TypeRelationId, oper->oprleft);
1317 887 : 1262 : add_exact_object_address(&referenced, addrs);
888 : : }
889 : :
890 : : /* Dependency on right type */
7943 tgl@sss.pgh.pa.us 891 [ + - ]: 1296 : if (OidIsValid(oper->oprright))
892 : : {
1383 michael@paquier.xyz 893 : 1296 : ObjectAddressSet(referenced, TypeRelationId, oper->oprright);
1317 894 : 1296 : add_exact_object_address(&referenced, addrs);
895 : : }
896 : :
897 : : /* Dependency on result type */
7943 tgl@sss.pgh.pa.us 898 [ + + ]: 1296 : if (OidIsValid(oper->oprresult))
899 : : {
1383 michael@paquier.xyz 900 : 1021 : ObjectAddressSet(referenced, TypeRelationId, oper->oprresult);
1317 901 : 1021 : add_exact_object_address(&referenced, addrs);
902 : : }
903 : :
904 : : /*
905 : : * NOTE: we do not consider the operator to depend on the associated
906 : : * operators oprcom and oprnegate. We do not want to delete this operator
907 : : * if those go away, but only reset the link fields; which is not a
908 : : * function that the dependency logic can handle. (It's taken care of
909 : : * manually within RemoveOperatorById, instead.)
910 : : */
911 : :
912 : : /* Dependency on implementation function */
7943 tgl@sss.pgh.pa.us 913 [ + + ]: 1296 : if (OidIsValid(oper->oprcode))
914 : : {
1383 michael@paquier.xyz 915 : 1021 : ObjectAddressSet(referenced, ProcedureRelationId, oper->oprcode);
1317 916 : 1021 : add_exact_object_address(&referenced, addrs);
917 : : }
918 : :
919 : : /* Dependency on restriction selectivity function */
7943 tgl@sss.pgh.pa.us 920 [ + + ]: 1296 : if (OidIsValid(oper->oprrest))
921 : : {
1383 michael@paquier.xyz 922 : 740 : ObjectAddressSet(referenced, ProcedureRelationId, oper->oprrest);
1317 923 : 740 : add_exact_object_address(&referenced, addrs);
924 : : }
925 : :
926 : : /* Dependency on join selectivity function */
7943 tgl@sss.pgh.pa.us 927 [ + + ]: 1296 : if (OidIsValid(oper->oprjoin))
928 : : {
1383 michael@paquier.xyz 929 : 728 : ObjectAddressSet(referenced, ProcedureRelationId, oper->oprjoin);
1317 930 : 728 : add_exact_object_address(&referenced, addrs);
931 : : }
932 : :
933 : 1296 : record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
934 : 1296 : free_object_addresses(addrs);
935 : :
936 : : /* Dependency on owner */
1972 andres@anarazel.de 937 : 1296 : recordDependencyOnOwner(OperatorRelationId, oper->oid,
938 : : oper->oprowner);
939 : :
940 : : /* Dependency on extension */
971 tgl@sss.pgh.pa.us 941 [ + + ]: 1296 : if (makeExtensionDep)
615 942 : 1022 : recordDependencyOnCurrentExtension(&myself, isUpdate);
943 : :
3330 alvherre@alvh.no-ip. 944 : 1295 : return myself;
945 : : }
|