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
68 2056 : validOperatorName(const char *name)
69 : {
70 2056 : size_t len = strlen(name);
71 :
72 : /* Can't be empty or too long */
73 2056 : if (len == 0 || len >= NAMEDATALEN)
74 0 : return false;
75 :
76 : /* Can't contain any invalid characters */
77 : /* Test string here should match op_chars in scan.l */
78 2056 : if (strspn(name, "~!@#^&|`?+-*/%<>=") != len)
79 0 : return false;
80 :
81 : /* Can't contain slash-star or dash-dash (comment starts) */
82 2056 : if (strstr(name, "/*") || strstr(name, "--"))
83 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 : */
92 2056 : if (len > 1 &&
93 1394 : (name[len - 1] == '+' ||
94 1394 : name[len - 1] == '-'))
95 : {
96 : int ic;
97 :
98 16 : for (ic = len - 2; ic >= 0; ic--)
99 : {
100 16 : if (strchr("~!@#^&|`?%", name[ic]))
101 8 : break;
102 : }
103 8 : if (ic < 0)
104 0 : return false; /* nope, not valid */
105 : }
106 :
107 : /* != isn't valid either, because parser will convert it to <> */
108 2056 : if (strcmp(name, "!=") == 0)
109 0 : return false;
110 :
111 2056 : 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
124 1506 : OperatorGet(const char *operatorName,
125 : Oid operatorNamespace,
126 : Oid leftObjectId,
127 : Oid rightObjectId,
128 : bool *defined)
129 : {
130 : HeapTuple tup;
131 : Oid operatorObjectId;
132 :
133 1506 : tup = SearchSysCache4(OPERNAMENSP,
134 : PointerGetDatum(operatorName),
135 : ObjectIdGetDatum(leftObjectId),
136 : ObjectIdGetDatum(rightObjectId),
137 : ObjectIdGetDatum(operatorNamespace));
138 1506 : if (HeapTupleIsValid(tup))
139 : {
140 522 : Form_pg_operator oprform = (Form_pg_operator) GETSTRUCT(tup);
141 :
142 522 : operatorObjectId = oprform->oid;
143 522 : *defined = RegProcedureIsValid(oprform->oprcode);
144 522 : ReleaseSysCache(tup);
145 : }
146 : else
147 : {
148 984 : operatorObjectId = InvalidOid;
149 984 : *defined = false;
150 : }
151 :
152 1506 : 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 1710 : OperatorLookup(List *operatorName,
165 : Oid leftObjectId,
166 : Oid rightObjectId,
167 : bool *defined)
168 : {
169 : Oid operatorObjectId;
170 : RegProcedure oprcode;
171 :
172 1710 : operatorObjectId = LookupOperName(NULL, operatorName,
173 : leftObjectId, rightObjectId,
174 : true, -1);
175 1710 : if (!OidIsValid(operatorObjectId))
176 : {
177 736 : *defined = false;
178 736 : return InvalidOid;
179 : }
180 :
181 974 : oprcode = get_opcode(operatorObjectId);
182 974 : *defined = RegProcedureIsValid(oprcode);
183 :
184 974 : return operatorObjectId;
185 : }
186 :
187 :
188 : /*
189 : * OperatorShellMake
190 : * Make a "shell" entry for a not-yet-existing operator.
191 : */
192 : static Oid
193 550 : 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 : */
210 550 : if (!validOperatorName(operatorName))
211 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 : */
219 550 : pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock);
220 550 : tupDesc = pg_operator_desc->rd_att;
221 :
222 : /*
223 : * initialize our *nulls and *values arrays
224 : */
225 8800 : for (i = 0; i < Natts_pg_operator; ++i)
226 : {
227 8250 : nulls[i] = false;
228 8250 : 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 : */
235 550 : operatorObjectId = GetNewOidWithIndex(pg_operator_desc, OperatorOidIndexId,
236 : Anum_pg_operator_oid);
237 550 : values[Anum_pg_operator_oid - 1] = ObjectIdGetDatum(operatorObjectId);
238 550 : namestrcpy(&oname, operatorName);
239 550 : values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname);
240 550 : values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace);
241 550 : values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId());
242 550 : values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? 'b' : 'l');
243 550 : values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(false);
244 550 : values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(false);
245 550 : values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId);
246 550 : values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId);
247 550 : values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(InvalidOid);
248 550 : values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(InvalidOid);
249 550 : values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(InvalidOid);
250 550 : values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(InvalidOid);
251 550 : values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
252 550 : values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid);
253 :
254 : /*
255 : * create a new operator tuple
256 : */
257 550 : tup = heap_form_tuple(tupDesc, values, nulls);
258 :
259 : /*
260 : * insert our "shell" operator tuple
261 : */
262 550 : CatalogTupleInsert(pg_operator_desc, tup);
263 :
264 : /* Add dependencies for the entry */
265 550 : makeOperatorDependencies(tup, true, false);
266 :
267 550 : heap_freetuple(tup);
268 :
269 : /* Post creation hook for new shell operator */
270 550 : InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0);
271 :
272 : /*
273 : * Make sure the tuple is visible for subsequent lookups/updates.
274 : */
275 550 : CommandCounterIncrement();
276 :
277 : /*
278 : * close the operator relation and return the oid.
279 : */
280 550 : table_close(pg_operator_desc, RowExclusiveLock);
281 :
282 550 : 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
321 1506 : 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;
344 1506 : bool selfCommutator = false;
345 : NameData oname;
346 : int i;
347 : ObjectAddress address;
348 :
349 : /*
350 : * Sanity checks
351 : */
352 1506 : if (!validOperatorName(operatorName))
353 0 : ereport(ERROR,
354 : (errcode(ERRCODE_INVALID_NAME),
355 : errmsg("\"%s\" is not a valid operator name",
356 : operatorName)));
357 :
358 1506 : operResultType = get_func_rettype(procedureId);
359 :
360 1506 : OperatorValidateParams(leftTypeId,
361 : rightTypeId,
362 : operResultType,
363 : commutatorName != NIL,
364 : negatorName != NIL,
365 : OidIsValid(restrictionId),
366 : OidIsValid(joinId),
367 : canMerge,
368 : canHash);
369 :
370 1506 : operatorObjectId = OperatorGet(operatorName,
371 : operatorNamespace,
372 : leftTypeId,
373 : rightTypeId,
374 : &operatorAlreadyDefined);
375 :
376 1506 : if (operatorAlreadyDefined)
377 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 : */
387 1506 : if (OidIsValid(operatorObjectId) &&
388 522 : !object_ownercheck(OperatorRelationId, operatorObjectId, GetUserId()))
389 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 :
397 1506 : if (commutatorName)
398 : {
399 : /* commutator has reversed arg types */
400 986 : commutatorId = get_other_operator(commutatorName,
401 : rightTypeId, leftTypeId,
402 : operatorName, operatorNamespace,
403 : leftTypeId, rightTypeId);
404 :
405 : /* Permission check: must own other operator */
406 986 : if (OidIsValid(commutatorId) &&
407 806 : !object_ownercheck(OperatorRelationId, commutatorId, GetUserId()))
408 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
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 : */
416 986 : if (!OidIsValid(commutatorId))
417 180 : selfCommutator = true;
418 : }
419 : else
420 520 : commutatorId = InvalidOid;
421 :
422 1506 : if (negatorName)
423 : {
424 : /* negator has same arg types */
425 670 : negatorId = get_other_operator(negatorName,
426 : leftTypeId, rightTypeId,
427 : operatorName, operatorNamespace,
428 : leftTypeId, rightTypeId);
429 :
430 : /* Permission check: must own other operator */
431 670 : if (OidIsValid(negatorId) &&
432 664 : !object_ownercheck(OperatorRelationId, negatorId, GetUserId()))
433 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
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 : */
442 670 : if (!OidIsValid(negatorId) || negatorId == operatorObjectId)
443 12 : ereport(ERROR,
444 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
445 : errmsg("operator cannot be its own negator")));
446 : }
447 : else
448 836 : negatorId = InvalidOid;
449 :
450 : /*
451 : * set up values in the operator tuple
452 : */
453 :
454 23904 : for (i = 0; i < Natts_pg_operator; ++i)
455 : {
456 22410 : values[i] = (Datum) NULL;
457 22410 : replaces[i] = true;
458 22410 : nulls[i] = false;
459 : }
460 :
461 1494 : namestrcpy(&oname, operatorName);
462 1494 : values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname);
463 1494 : values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace);
464 1494 : values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId());
465 1494 : values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? 'b' : 'l');
466 1494 : values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge);
467 1494 : values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash);
468 1494 : values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId);
469 1494 : values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId);
470 1494 : values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(operResultType);
471 1494 : values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorId);
472 1494 : values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorId);
473 1494 : values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(procedureId);
474 1494 : values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionId);
475 1494 : values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinId);
476 :
477 1494 : pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock);
478 :
479 : /*
480 : * If we are replacing an operator shell, update; else insert
481 : */
482 1494 : if (operatorObjectId)
483 : {
484 516 : isUpdate = true;
485 :
486 516 : tup = SearchSysCacheCopy1(OPEROID,
487 : ObjectIdGetDatum(operatorObjectId));
488 516 : if (!HeapTupleIsValid(tup))
489 0 : elog(ERROR, "cache lookup failed for operator %u",
490 : operatorObjectId);
491 :
492 516 : replaces[Anum_pg_operator_oid - 1] = false;
493 516 : tup = heap_modify_tuple(tup,
494 : RelationGetDescr(pg_operator_desc),
495 : values,
496 : nulls,
497 : replaces);
498 :
499 516 : CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup);
500 : }
501 : else
502 : {
503 978 : isUpdate = false;
504 :
505 978 : operatorObjectId = GetNewOidWithIndex(pg_operator_desc,
506 : OperatorOidIndexId,
507 : Anum_pg_operator_oid);
508 978 : values[Anum_pg_operator_oid - 1] = ObjectIdGetDatum(operatorObjectId);
509 :
510 978 : tup = heap_form_tuple(RelationGetDescr(pg_operator_desc),
511 : values, nulls);
512 :
513 978 : CatalogTupleInsert(pg_operator_desc, tup);
514 : }
515 :
516 : /* Add dependencies for the entry */
517 1494 : 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 : */
530 1492 : if (selfCommutator)
531 180 : commutatorId = operatorObjectId;
532 :
533 1492 : if (OidIsValid(commutatorId) || OidIsValid(negatorId))
534 1056 : OperatorUpd(operatorObjectId, commutatorId, negatorId, false);
535 :
536 : /* Post creation hook for new operator */
537 1480 : InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0);
538 :
539 1480 : table_close(pg_operator_desc, RowExclusiveLock);
540 :
541 1480 : 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
556 2060 : 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 2060 : if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId)))
567 : {
568 : /* If it's not a binary op, these things mustn't be set: */
569 68 : if (hasCommutator)
570 0 : ereport(ERROR,
571 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
572 : errmsg("only binary operators can have commutators")));
573 68 : if (hasJoinSelectivity)
574 0 : ereport(ERROR,
575 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
576 : errmsg("only binary operators can have join selectivity")));
577 68 : if (canMerge)
578 0 : ereport(ERROR,
579 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
580 : errmsg("only binary operators can merge join")));
581 68 : if (canHash)
582 0 : ereport(ERROR,
583 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
584 : errmsg("only binary operators can hash")));
585 : }
586 :
587 2060 : if (operResultType != BOOLOID)
588 : {
589 : /* If it's not a boolean op, these things mustn't be set: */
590 306 : if (hasNegator)
591 0 : ereport(ERROR,
592 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
593 : errmsg("only boolean operators can have negators")));
594 306 : if (hasRestrictionSelectivity)
595 0 : ereport(ERROR,
596 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
597 : errmsg("only boolean operators can have restriction selectivity")));
598 306 : if (hasJoinSelectivity)
599 0 : ereport(ERROR,
600 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
601 : errmsg("only boolean operators can have join selectivity")));
602 306 : if (canMerge)
603 0 : ereport(ERROR,
604 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
605 : errmsg("only boolean operators can merge join")));
606 306 : if (canHash)
607 0 : ereport(ERROR,
608 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
609 : errmsg("only boolean operators can hash")));
610 : }
611 2060 : }
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
622 1656 : 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 1656 : other_oid = OperatorLookup(otherOp,
633 : otherLeftTypeId,
634 : otherRightTypeId,
635 : &otherDefined);
636 :
637 1656 : if (OidIsValid(other_oid))
638 : {
639 : /* other op already in catalogs */
640 920 : return other_oid;
641 : }
642 :
643 736 : otherNamespace = QualifiedNameGetCreationNamespace(otherOp,
644 : &otherName);
645 :
646 736 : if (strcmp(otherName, operatorName) == 0 &&
647 274 : otherNamespace == operatorNamespace &&
648 186 : otherLeftTypeId == leftTypeId &&
649 : otherRightTypeId == rightTypeId)
650 : {
651 : /* self-linkage to new operator; caller must handle this */
652 186 : return InvalidOid;
653 : }
654 :
655 : /* not in catalogs, different from operator, so make shell */
656 :
657 550 : aclresult = object_aclcheck(NamespaceRelationId, otherNamespace, GetUserId(),
658 : ACL_CREATE);
659 550 : if (aclresult != ACLCHECK_OK)
660 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
661 0 : get_namespace_name(otherNamespace));
662 :
663 550 : other_oid = OperatorShellMake(otherName,
664 : otherNamespace,
665 : otherLeftTypeId,
666 : otherRightTypeId);
667 550 : 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
684 1422 : 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 1422 : if (!isDelete)
696 1092 : CommandCounterIncrement();
697 :
698 : /* Open the relation. */
699 1422 : pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock);
700 :
701 : /* Get a writable copy of the commutator's tuple. */
702 1422 : if (OidIsValid(commId))
703 1334 : tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(commId));
704 : else
705 88 : tup = NULL;
706 :
707 : /* Update the commutator's tuple if need be. */
708 1422 : if (HeapTupleIsValid(tup))
709 : {
710 1334 : Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup);
711 1334 : 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 : */
718 1334 : if (isDelete && OidIsValid(t->oprcom))
719 : {
720 330 : t->oprcom = InvalidOid;
721 330 : update_commutator = true;
722 : }
723 1004 : 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 582 : if (OidIsValid(t->oprcom))
733 : {
734 12 : char *thirdop = get_opname(t->oprcom);
735 :
736 12 : if (thirdop != NULL)
737 12 : 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
742 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 :
748 570 : t->oprcom = baseId;
749 570 : update_commutator = true;
750 : }
751 :
752 : /* If any columns were found to need modification, update tuple. */
753 1322 : if (update_commutator)
754 : {
755 900 : 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 : */
764 900 : CommandCounterIncrement();
765 : }
766 : }
767 :
768 : /*
769 : * Similarly find and update the negator, if any.
770 : */
771 1410 : if (OidIsValid(negId))
772 904 : tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(negId));
773 : else
774 506 : tup = NULL;
775 :
776 1410 : if (HeapTupleIsValid(tup))
777 : {
778 904 : Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup);
779 904 : 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 : */
786 904 : if (isDelete && OidIsValid(t->oprnegate))
787 : {
788 228 : t->oprnegate = InvalidOid;
789 228 : update_negator = true;
790 : }
791 676 : 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 360 : if (OidIsValid(t->oprnegate))
801 : {
802 12 : char *thirdop = get_opname(t->oprnegate);
803 :
804 12 : if (thirdop != NULL)
805 12 : 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
810 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 :
816 348 : t->oprnegate = baseId;
817 348 : update_negator = true;
818 : }
819 :
820 : /* If any columns were found to need modification, update tuple. */
821 892 : if (update_negator)
822 : {
823 576 : 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 : */
831 576 : if (isDelete)
832 228 : CommandCounterIncrement();
833 : }
834 : }
835 :
836 : /* Close relation and release catalog lock. */
837 1398 : table_close(pg_operator_desc, RowExclusiveLock);
838 1398 : }
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
853 2598 : makeOperatorDependencies(HeapTuple tuple,
854 : bool makeExtensionDep,
855 : bool isUpdate)
856 : {
857 2598 : Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple);
858 : ObjectAddress myself,
859 : referenced;
860 : ObjectAddresses *addrs;
861 :
862 2598 : 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 : */
868 2598 : if (isUpdate)
869 : {
870 1070 : deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
871 1070 : deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
872 : }
873 :
874 2598 : addrs = new_object_addresses();
875 :
876 : /* Dependency on namespace */
877 2598 : if (OidIsValid(oper->oprnamespace))
878 : {
879 2598 : ObjectAddressSet(referenced, NamespaceRelationId, oper->oprnamespace);
880 2598 : add_exact_object_address(&referenced, addrs);
881 : }
882 :
883 : /* Dependency on left type */
884 2598 : if (OidIsValid(oper->oprleft))
885 : {
886 2530 : ObjectAddressSet(referenced, TypeRelationId, oper->oprleft);
887 2530 : add_exact_object_address(&referenced, addrs);
888 : }
889 :
890 : /* Dependency on right type */
891 2598 : if (OidIsValid(oper->oprright))
892 : {
893 2598 : ObjectAddressSet(referenced, TypeRelationId, oper->oprright);
894 2598 : add_exact_object_address(&referenced, addrs);
895 : }
896 :
897 : /* Dependency on result type */
898 2598 : if (OidIsValid(oper->oprresult))
899 : {
900 2048 : ObjectAddressSet(referenced, TypeRelationId, oper->oprresult);
901 2048 : 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 */
913 2598 : if (OidIsValid(oper->oprcode))
914 : {
915 2048 : ObjectAddressSet(referenced, ProcedureRelationId, oper->oprcode);
916 2048 : add_exact_object_address(&referenced, addrs);
917 : }
918 :
919 : /* Dependency on restriction selectivity function */
920 2598 : if (OidIsValid(oper->oprrest))
921 : {
922 1486 : ObjectAddressSet(referenced, ProcedureRelationId, oper->oprrest);
923 1486 : add_exact_object_address(&referenced, addrs);
924 : }
925 :
926 : /* Dependency on join selectivity function */
927 2598 : if (OidIsValid(oper->oprjoin))
928 : {
929 1462 : ObjectAddressSet(referenced, ProcedureRelationId, oper->oprjoin);
930 1462 : add_exact_object_address(&referenced, addrs);
931 : }
932 :
933 2598 : record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
934 2598 : free_object_addresses(addrs);
935 :
936 : /* Dependency on owner */
937 2598 : recordDependencyOnOwner(OperatorRelationId, oper->oid,
938 : oper->oprowner);
939 :
940 : /* Dependency on extension */
941 2598 : if (makeExtensionDep)
942 2044 : recordDependencyOnCurrentExtension(&myself, isUpdate);
943 :
944 2596 : return myself;
945 : }
|