1 /*-------------------------------------------------------------------------
5 * Routines for operator manipulation commands
7 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/backend/commands/operatorcmds.c
15 * The "DefineFoo" routines take the parse tree and pick out the
16 * appropriate arguments/flags, passing the results to the
17 * corresponding "FooCreate" routines (in src/backend/catalog) that do
18 * the actual catalog-munging. These routines also verify permission
19 * of the user to execute the command.
22 * These things must be defined and committed in the following order:
24 * input/output, recv/send functions
30 *-------------------------------------------------------------------------
34 #include "access/htup_details.h"
35 #include "access/table.h"
36 #include "catalog/indexing.h"
37 #include "catalog/objectaccess.h"
38 #include "catalog/pg_namespace.h"
39 #include "catalog/pg_operator.h"
40 #include "catalog/pg_proc.h"
41 #include "catalog/pg_type.h"
42 #include "commands/defrem.h"
43 #include "miscadmin.h"
44 #include "parser/parse_func.h"
45 #include "parser/parse_oper.h"
46 #include "parser/parse_type.h"
47 #include "utils/acl.h"
48 #include "utils/lsyscache.h"
49 #include "utils/rel.h"
50 #include "utils/syscache.h"
52 static Oid
ValidateRestrictionEstimator(List
*restrictionName
);
53 static Oid
ValidateJoinEstimator(List
*joinName
);
54 static Oid
ValidateOperatorReference(List
*name
,
60 * this function extracts all the information from the
61 * parameter list generated by the parser and then has
62 * OperatorCreate() do all the actual work.
64 * 'parameters' is a list of DefElem
67 DefineOperator(List
*names
, List
*parameters
)
72 bool canMerge
= false; /* operator merges */
73 bool canHash
= false; /* operator hashes */
74 List
*functionName
= NIL
; /* function for operator */
75 TypeName
*typeName1
= NULL
; /* first type name */
76 TypeName
*typeName2
= NULL
; /* second type name */
77 Oid typeId1
= InvalidOid
; /* types converted to OID */
78 Oid typeId2
= InvalidOid
;
80 List
*commutatorName
= NIL
; /* optional commutator operator name */
81 List
*negatorName
= NIL
; /* optional negator operator name */
82 List
*restrictionName
= NIL
; /* optional restrict. sel. function */
83 List
*joinName
= NIL
; /* optional join sel. function */
84 Oid functionOid
; /* functions converted to OID */
87 Oid typeId
[2]; /* to hold left and right arg */
91 /* Convert list of names to a name and namespace */
92 oprNamespace
= QualifiedNameGetCreationNamespace(names
, &oprName
);
94 /* Check we have creation rights in target namespace */
95 aclresult
= object_aclcheck(NamespaceRelationId
, oprNamespace
, GetUserId(), ACL_CREATE
);
96 if (aclresult
!= ACLCHECK_OK
)
97 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
98 get_namespace_name(oprNamespace
));
101 * loop over the definition list and extract the information we need.
103 foreach(pl
, parameters
)
105 DefElem
*defel
= (DefElem
*) lfirst(pl
);
107 if (strcmp(defel
->defname
, "leftarg") == 0)
109 typeName1
= defGetTypeName(defel
);
110 if (typeName1
->setof
)
112 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
113 errmsg("SETOF type not allowed for operator argument")));
115 else if (strcmp(defel
->defname
, "rightarg") == 0)
117 typeName2
= defGetTypeName(defel
);
118 if (typeName2
->setof
)
120 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
121 errmsg("SETOF type not allowed for operator argument")));
123 /* "function" and "procedure" are equivalent here */
124 else if (strcmp(defel
->defname
, "function") == 0)
125 functionName
= defGetQualifiedName(defel
);
126 else if (strcmp(defel
->defname
, "procedure") == 0)
127 functionName
= defGetQualifiedName(defel
);
128 else if (strcmp(defel
->defname
, "commutator") == 0)
129 commutatorName
= defGetQualifiedName(defel
);
130 else if (strcmp(defel
->defname
, "negator") == 0)
131 negatorName
= defGetQualifiedName(defel
);
132 else if (strcmp(defel
->defname
, "restrict") == 0)
133 restrictionName
= defGetQualifiedName(defel
);
134 else if (strcmp(defel
->defname
, "join") == 0)
135 joinName
= defGetQualifiedName(defel
);
136 else if (strcmp(defel
->defname
, "hashes") == 0)
137 canHash
= defGetBoolean(defel
);
138 else if (strcmp(defel
->defname
, "merges") == 0)
139 canMerge
= defGetBoolean(defel
);
140 /* These obsolete options are taken as meaning canMerge */
141 else if (strcmp(defel
->defname
, "sort1") == 0)
143 else if (strcmp(defel
->defname
, "sort2") == 0)
145 else if (strcmp(defel
->defname
, "ltcmp") == 0)
147 else if (strcmp(defel
->defname
, "gtcmp") == 0)
151 /* WARNING, not ERROR, for historical backwards-compatibility */
153 (errcode(ERRCODE_SYNTAX_ERROR
),
154 errmsg("operator attribute \"%s\" not recognized",
160 * make sure we have our required definitions
162 if (functionName
== NIL
)
164 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
165 errmsg("operator function must be specified")));
167 /* Transform type names to type OIDs */
169 typeId1
= typenameTypeId(NULL
, typeName1
);
171 typeId2
= typenameTypeId(NULL
, typeName2
);
174 * If only the right argument is missing, the user is likely trying to
175 * create a postfix operator, so give them a hint about why that does not
176 * work. But if both arguments are missing, do not mention postfix
177 * operators, as the user most likely simply neglected to mention the
180 if (!OidIsValid(typeId1
) && !OidIsValid(typeId2
))
182 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
183 errmsg("operator argument types must be specified")));
184 if (!OidIsValid(typeId2
))
186 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
187 errmsg("operator right argument type must be specified"),
188 errdetail("Postfix operators are not supported.")));
192 aclresult
= object_aclcheck(TypeRelationId
, typeId1
, GetUserId(), ACL_USAGE
);
193 if (aclresult
!= ACLCHECK_OK
)
194 aclcheck_error_type(aclresult
, typeId1
);
199 aclresult
= object_aclcheck(TypeRelationId
, typeId2
, GetUserId(), ACL_USAGE
);
200 if (aclresult
!= ACLCHECK_OK
)
201 aclcheck_error_type(aclresult
, typeId2
);
205 * Look up the operator's underlying function.
207 if (!OidIsValid(typeId1
))
212 else if (!OidIsValid(typeId2
))
223 functionOid
= LookupFuncName(functionName
, nargs
, typeId
, false);
226 * We require EXECUTE rights for the function. This isn't strictly
227 * necessary, since EXECUTE will be checked at any attempted use of the
228 * operator, but it seems like a good idea anyway.
230 aclresult
= object_aclcheck(ProcedureRelationId
, functionOid
, GetUserId(), ACL_EXECUTE
);
231 if (aclresult
!= ACLCHECK_OK
)
232 aclcheck_error(aclresult
, OBJECT_FUNCTION
,
233 NameListToString(functionName
));
235 rettype
= get_func_rettype(functionOid
);
236 aclresult
= object_aclcheck(TypeRelationId
, rettype
, GetUserId(), ACL_USAGE
);
237 if (aclresult
!= ACLCHECK_OK
)
238 aclcheck_error_type(aclresult
, rettype
);
241 * Look up restriction and join estimators if specified
244 restrictionOid
= ValidateRestrictionEstimator(restrictionName
);
246 restrictionOid
= InvalidOid
;
248 joinOid
= ValidateJoinEstimator(joinName
);
250 joinOid
= InvalidOid
;
253 * now have OperatorCreate do all the work..
256 OperatorCreate(oprName
, /* operator name */
257 oprNamespace
, /* namespace */
258 typeId1
, /* left type id */
259 typeId2
, /* right type id */
260 functionOid
, /* function for operator */
261 commutatorName
, /* optional commutator operator name */
262 negatorName
, /* optional negator operator name */
263 restrictionOid
, /* optional restrict. sel. function */
264 joinOid
, /* optional join sel. function name */
265 canMerge
, /* operator merges */
266 canHash
); /* operator hashes */
270 * Look up a restriction estimator function by name, and verify that it has
271 * the correct signature and we have the permissions to attach it to an
275 ValidateRestrictionEstimator(List
*restrictionName
)
281 typeId
[0] = INTERNALOID
; /* PlannerInfo */
282 typeId
[1] = OIDOID
; /* operator OID */
283 typeId
[2] = INTERNALOID
; /* args list */
284 typeId
[3] = INT4OID
; /* varRelid */
286 restrictionOid
= LookupFuncName(restrictionName
, 4, typeId
, false);
288 /* estimators must return float8 */
289 if (get_func_rettype(restrictionOid
) != FLOAT8OID
)
291 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
292 errmsg("restriction estimator function %s must return type %s",
293 NameListToString(restrictionName
), "float8")));
295 /* Require EXECUTE rights for the estimator */
296 aclresult
= object_aclcheck(ProcedureRelationId
, restrictionOid
, GetUserId(), ACL_EXECUTE
);
297 if (aclresult
!= ACLCHECK_OK
)
298 aclcheck_error(aclresult
, OBJECT_FUNCTION
,
299 NameListToString(restrictionName
));
301 return restrictionOid
;
305 * Look up a join estimator function by name, and verify that it has the
306 * correct signature and we have the permissions to attach it to an
310 ValidateJoinEstimator(List
*joinName
)
317 typeId
[0] = INTERNALOID
; /* PlannerInfo */
318 typeId
[1] = OIDOID
; /* operator OID */
319 typeId
[2] = INTERNALOID
; /* args list */
320 typeId
[3] = INT2OID
; /* jointype */
321 typeId
[4] = INTERNALOID
; /* SpecialJoinInfo */
324 * As of Postgres 8.4, the preferred signature for join estimators has 5
325 * arguments, but we still allow the old 4-argument form. Whine about
326 * ambiguity if both forms exist.
328 joinOid
= LookupFuncName(joinName
, 5, typeId
, true);
329 joinOid2
= LookupFuncName(joinName
, 4, typeId
, true);
330 if (OidIsValid(joinOid
))
332 if (OidIsValid(joinOid2
))
334 (errcode(ERRCODE_AMBIGUOUS_FUNCTION
),
335 errmsg("join estimator function %s has multiple matches",
336 NameListToString(joinName
))));
341 /* If not found, reference the 5-argument signature in error msg */
342 if (!OidIsValid(joinOid
))
343 joinOid
= LookupFuncName(joinName
, 5, typeId
, false);
346 /* estimators must return float8 */
347 if (get_func_rettype(joinOid
) != FLOAT8OID
)
349 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
350 errmsg("join estimator function %s must return type %s",
351 NameListToString(joinName
), "float8")));
353 /* Require EXECUTE rights for the estimator */
354 aclresult
= object_aclcheck(ProcedureRelationId
, joinOid
, GetUserId(), ACL_EXECUTE
);
355 if (aclresult
!= ACLCHECK_OK
)
356 aclcheck_error(aclresult
, OBJECT_FUNCTION
,
357 NameListToString(joinName
));
363 * Look up and return the OID of an operator,
364 * given a possibly-qualified name and left and right type IDs.
366 * Verifies that the operator is defined (not a shell) and owned by
367 * the current user, so that we have permission to associate it with
368 * the operator being altered. Rejecting shell operators is a policy
369 * choice to help catch mistakes, rather than something essential.
372 ValidateOperatorReference(List
*name
,
379 oid
= OperatorLookup(name
,
384 /* These message strings are chosen to match parse_oper.c */
385 if (!OidIsValid(oid
))
387 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
388 errmsg("operator does not exist: %s",
389 op_signature_string(name
,
395 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
396 errmsg("operator is only a shell: %s",
397 op_signature_string(name
,
401 if (!object_ownercheck(OperatorRelationId
, oid
, GetUserId()))
402 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_OPERATOR
,
403 NameListToString(name
));
410 * Guts of operator deletion.
413 RemoveOperatorById(Oid operOid
)
419 relation
= table_open(OperatorRelationId
, RowExclusiveLock
);
421 tup
= SearchSysCache1(OPEROID
, ObjectIdGetDatum(operOid
));
422 if (!HeapTupleIsValid(tup
)) /* should not happen */
423 elog(ERROR
, "cache lookup failed for operator %u", operOid
);
424 op
= (Form_pg_operator
) GETSTRUCT(tup
);
427 * Reset links from commutator and negator, if any. In case of a
428 * self-commutator or self-negator, this means we have to re-fetch the
429 * updated tuple. (We could optimize away updates on the tuple we're
430 * about to drop, but it doesn't seem worth convoluting the logic for.)
432 if (OidIsValid(op
->oprcom
) || OidIsValid(op
->oprnegate
))
434 OperatorUpd(operOid
, op
->oprcom
, op
->oprnegate
, true);
435 if (operOid
== op
->oprcom
|| operOid
== op
->oprnegate
)
437 ReleaseSysCache(tup
);
438 tup
= SearchSysCache1(OPEROID
, ObjectIdGetDatum(operOid
));
439 if (!HeapTupleIsValid(tup
)) /* should not happen */
440 elog(ERROR
, "cache lookup failed for operator %u", operOid
);
444 CatalogTupleDelete(relation
, &tup
->t_self
);
446 ReleaseSysCache(tup
);
448 table_close(relation
, RowExclusiveLock
);
453 * routine implementing ALTER OPERATOR <operator> SET (option = ...).
455 * Currently, only RESTRICT and JOIN estimator functions can be changed.
456 * COMMUTATOR, NEGATOR, MERGES, and HASHES attributes can be set if they
457 * have not been set previously. (Changing or removing one of these
458 * attributes could invalidate existing plans, which seems more trouble
462 AlterOperator(AlterOperatorStmt
*stmt
)
464 ObjectAddress address
;
468 Form_pg_operator oprForm
;
471 Datum values
[Natts_pg_operator
];
472 bool nulls
[Natts_pg_operator
];
473 bool replaces
[Natts_pg_operator
];
474 List
*restrictionName
= NIL
; /* optional restrict. sel. function */
475 bool updateRestriction
= false;
477 List
*joinName
= NIL
; /* optional join sel. function */
478 bool updateJoin
= false;
480 List
*commutatorName
= NIL
; /* optional commutator operator name */
482 List
*negatorName
= NIL
; /* optional negator operator name */
484 bool canMerge
= false;
485 bool updateMerges
= false;
486 bool canHash
= false;
487 bool updateHashes
= false;
489 /* Look up the operator */
490 oprId
= LookupOperWithArgs(stmt
->opername
, false);
491 catalog
= table_open(OperatorRelationId
, RowExclusiveLock
);
492 tup
= SearchSysCacheCopy1(OPEROID
, ObjectIdGetDatum(oprId
));
493 if (!HeapTupleIsValid(tup
))
494 elog(ERROR
, "cache lookup failed for operator %u", oprId
);
495 oprForm
= (Form_pg_operator
) GETSTRUCT(tup
);
497 /* Process options */
498 foreach(pl
, stmt
->options
)
500 DefElem
*defel
= (DefElem
*) lfirst(pl
);
503 if (defel
->arg
== NULL
)
504 param
= NIL
; /* NONE, removes the function */
506 param
= defGetQualifiedName(defel
);
508 if (strcmp(defel
->defname
, "restrict") == 0)
510 restrictionName
= param
;
511 updateRestriction
= true;
513 else if (strcmp(defel
->defname
, "join") == 0)
518 else if (strcmp(defel
->defname
, "commutator") == 0)
520 commutatorName
= defGetQualifiedName(defel
);
522 else if (strcmp(defel
->defname
, "negator") == 0)
524 negatorName
= defGetQualifiedName(defel
);
526 else if (strcmp(defel
->defname
, "merges") == 0)
528 canMerge
= defGetBoolean(defel
);
531 else if (strcmp(defel
->defname
, "hashes") == 0)
533 canHash
= defGetBoolean(defel
);
538 * The rest of the options that CREATE accepts cannot be changed.
539 * Check for them so that we can give a meaningful error message.
541 else if (strcmp(defel
->defname
, "leftarg") == 0 ||
542 strcmp(defel
->defname
, "rightarg") == 0 ||
543 strcmp(defel
->defname
, "function") == 0 ||
544 strcmp(defel
->defname
, "procedure") == 0)
547 (errcode(ERRCODE_SYNTAX_ERROR
),
548 errmsg("operator attribute \"%s\" cannot be changed",
553 (errcode(ERRCODE_SYNTAX_ERROR
),
554 errmsg("operator attribute \"%s\" not recognized",
558 /* Check permissions. Must be owner. */
559 if (!object_ownercheck(OperatorRelationId
, oprId
, GetUserId()))
560 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_OPERATOR
,
561 NameStr(oprForm
->oprname
));
564 * Look up OIDs for any parameters specified
567 restrictionOid
= ValidateRestrictionEstimator(restrictionName
);
569 restrictionOid
= InvalidOid
;
571 joinOid
= ValidateJoinEstimator(joinName
);
573 joinOid
= InvalidOid
;
577 /* commutator has reversed arg types */
578 commutatorOid
= ValidateOperatorReference(commutatorName
,
583 * We don't need to do anything extra for a self commutator as in
584 * OperatorCreate, since the operator surely exists already.
588 commutatorOid
= InvalidOid
;
592 negatorOid
= ValidateOperatorReference(negatorName
,
596 /* Must reject self-negation */
597 if (negatorOid
== oprForm
->oid
)
599 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
600 errmsg("operator cannot be its own negator")));
604 negatorOid
= InvalidOid
;
608 * Check that we're not changing any attributes that might be depended on
609 * by plans, while allowing no-op updates.
611 if (OidIsValid(commutatorOid
) && OidIsValid(oprForm
->oprcom
) &&
612 commutatorOid
!= oprForm
->oprcom
)
614 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
615 errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
618 if (OidIsValid(negatorOid
) && OidIsValid(oprForm
->oprnegate
) &&
619 negatorOid
!= oprForm
->oprnegate
)
621 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
622 errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
625 if (updateMerges
&& oprForm
->oprcanmerge
&& !canMerge
)
627 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
628 errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
631 if (updateHashes
&& oprForm
->oprcanhash
&& !canHash
)
633 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
634 errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
637 /* Perform additional checks, like OperatorCreate does */
638 OperatorValidateParams(oprForm
->oprleft
,
641 OidIsValid(commutatorOid
),
642 OidIsValid(negatorOid
),
643 OidIsValid(restrictionOid
),
648 /* Update the tuple */
649 for (i
= 0; i
< Natts_pg_operator
; ++i
)
651 values
[i
] = (Datum
) 0;
655 if (updateRestriction
)
657 replaces
[Anum_pg_operator_oprrest
- 1] = true;
658 values
[Anum_pg_operator_oprrest
- 1] = ObjectIdGetDatum(restrictionOid
);
662 replaces
[Anum_pg_operator_oprjoin
- 1] = true;
663 values
[Anum_pg_operator_oprjoin
- 1] = ObjectIdGetDatum(joinOid
);
665 if (OidIsValid(commutatorOid
))
667 replaces
[Anum_pg_operator_oprcom
- 1] = true;
668 values
[Anum_pg_operator_oprcom
- 1] = ObjectIdGetDatum(commutatorOid
);
670 if (OidIsValid(negatorOid
))
672 replaces
[Anum_pg_operator_oprnegate
- 1] = true;
673 values
[Anum_pg_operator_oprnegate
- 1] = ObjectIdGetDatum(negatorOid
);
677 replaces
[Anum_pg_operator_oprcanmerge
- 1] = true;
678 values
[Anum_pg_operator_oprcanmerge
- 1] = BoolGetDatum(canMerge
);
682 replaces
[Anum_pg_operator_oprcanhash
- 1] = true;
683 values
[Anum_pg_operator_oprcanhash
- 1] = BoolGetDatum(canHash
);
686 tup
= heap_modify_tuple(tup
, RelationGetDescr(catalog
),
687 values
, nulls
, replaces
);
689 CatalogTupleUpdate(catalog
, &tup
->t_self
, tup
);
691 address
= makeOperatorDependencies(tup
, false, true);
693 if (OidIsValid(commutatorOid
) || OidIsValid(negatorOid
))
694 OperatorUpd(oprId
, commutatorOid
, negatorOid
, false);
696 InvokeObjectPostAlterHook(OperatorRelationId
, oprId
, 0);
698 table_close(catalog
, NoLock
);