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