Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * operatorcmds.c
4 : : *
5 : : * Routines for operator manipulation commands
6 : : *
7 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : *
11 : : * IDENTIFICATION
12 : : * src/backend/commands/operatorcmds.c
13 : : *
14 : : * DESCRIPTION
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.
20 : : *
21 : : * NOTES
22 : : * These things must be defined and committed in the following order:
23 : : * "create function":
24 : : * input/output, recv/send functions
25 : : * "create type":
26 : : * type
27 : : * "create operator":
28 : : * operators
29 : : *
30 : : *-------------------------------------------------------------------------
31 : : */
32 : : #include "postgres.h"
33 : :
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"
51 : :
52 : : static Oid ValidateRestrictionEstimator(List *restrictionName);
53 : : static Oid ValidateJoinEstimator(List *joinName);
54 : : static Oid ValidateOperatorReference(List *name,
55 : : Oid leftTypeId,
56 : : Oid rightTypeId);
57 : :
58 : : /*
59 : : * DefineOperator
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.
63 : : *
64 : : * 'parameters' is a list of DefElem
65 : : */
66 : : ObjectAddress
8735 tgl@sss.pgh.pa.us 67 :CBC 826 : DefineOperator(List *names, List *parameters)
68 : : {
69 : : char *oprName;
70 : : Oid oprNamespace;
71 : : AclResult aclresult;
3189 72 : 826 : bool canMerge = false; /* operator merges */
6695 bruce@momjian.us 73 : 826 : bool canHash = false; /* operator hashes */
3189 tgl@sss.pgh.pa.us 74 : 826 : List *functionName = NIL; /* function for operator */
75 : 826 : TypeName *typeName1 = NULL; /* first type name */
76 : 826 : TypeName *typeName2 = NULL; /* second type name */
8735 77 : 826 : Oid typeId1 = InvalidOid; /* types converted to OID */
78 : 826 : Oid typeId2 = InvalidOid;
79 : : Oid rettype;
7456 bruce@momjian.us 80 : 826 : List *commutatorName = NIL; /* optional commutator operator name */
3189 tgl@sss.pgh.pa.us 81 : 826 : List *negatorName = NIL; /* optional negator operator name */
2769 peter_e@gmx.net 82 : 826 : List *restrictionName = NIL; /* optional restrict. sel. function */
83 : 826 : List *joinName = NIL; /* optional join sel. function */
84 : : Oid functionOid; /* functions converted to OID */
85 : : Oid restrictionOid;
86 : : Oid joinOid;
87 : : Oid typeId[2]; /* to hold left and right arg */
88 : : int nargs;
89 : : ListCell *pl;
90 : :
91 : : /* Convert list of names to a name and namespace */
8735 tgl@sss.pgh.pa.us 92 : 826 : oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
93 : :
94 : : /* Check we have creation rights in target namespace */
1218 peter@eisentraut.org 95 : 826 : aclresult = object_aclcheck(NamespaceRelationId, oprNamespace, GetUserId(), ACL_CREATE);
8723 tgl@sss.pgh.pa.us 96 [ + + ]: 826 : if (aclresult != ACLCHECK_OK)
3025 peter_e@gmx.net 97 : 3 : aclcheck_error(aclresult, OBJECT_SCHEMA,
8262 tgl@sss.pgh.pa.us 98 : 3 : get_namespace_name(oprNamespace));
99 : :
100 : : /*
101 : : * loop over the definition list and extract the information we need.
102 : : */
8735 103 [ + - + + : 5271 : foreach(pl, parameters)
+ + ]
104 : : {
105 : 4454 : DefElem *defel = (DefElem *) lfirst(pl);
106 : :
2970 107 [ + + ]: 4454 : if (strcmp(defel->defname, "leftarg") == 0)
108 : : {
8735 109 : 777 : typeName1 = defGetTypeName(defel);
110 [ + + ]: 777 : if (typeName1->setof)
8274 111 [ + - ]: 3 : ereport(ERROR,
112 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
113 : : errmsg("SETOF type not allowed for operator argument")));
114 : : }
2970 115 [ + + ]: 3677 : else if (strcmp(defel->defname, "rightarg") == 0)
116 : : {
8735 117 : 811 : typeName2 = defGetTypeName(defel);
118 [ + + ]: 811 : if (typeName2->setof)
8274 119 [ + - ]: 3 : ereport(ERROR,
120 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
121 : : errmsg("SETOF type not allowed for operator argument")));
122 : : }
123 : : /* "function" and "procedure" are equivalent here */
2769 peter_e@gmx.net 124 [ + + ]: 2866 : else if (strcmp(defel->defname, "function") == 0)
125 : 20 : functionName = defGetQualifiedName(defel);
2970 tgl@sss.pgh.pa.us 126 [ + + ]: 2846 : else if (strcmp(defel->defname, "procedure") == 0)
8734 127 : 791 : functionName = defGetQualifiedName(defel);
2970 128 [ + + ]: 2055 : else if (strcmp(defel->defname, "commutator") == 0)
8734 129 : 524 : commutatorName = defGetQualifiedName(defel);
2970 130 [ + + ]: 1531 : else if (strcmp(defel->defname, "negator") == 0)
8734 131 : 341 : negatorName = defGetQualifiedName(defel);
2970 132 [ + + ]: 1190 : else if (strcmp(defel->defname, "restrict") == 0)
8734 133 : 520 : restrictionName = defGetQualifiedName(defel);
2970 134 [ + + ]: 670 : else if (strcmp(defel->defname, "join") == 0)
8734 135 : 505 : joinName = defGetQualifiedName(defel);
2970 136 [ + + ]: 165 : else if (strcmp(defel->defname, "hashes") == 0)
7975 137 : 47 : canHash = defGetBoolean(defel);
2970 138 [ + + ]: 118 : else if (strcmp(defel->defname, "merges") == 0)
7975 139 : 72 : canMerge = defGetBoolean(defel);
140 : : /* These obsolete options are taken as meaning canMerge */
2970 141 [ + + ]: 46 : else if (strcmp(defel->defname, "sort1") == 0)
7022 142 : 5 : canMerge = true;
2970 143 [ + + ]: 41 : else if (strcmp(defel->defname, "sort2") == 0)
7022 144 : 5 : canMerge = true;
2970 145 [ + + ]: 36 : else if (strcmp(defel->defname, "ltcmp") == 0)
7022 146 : 3 : canMerge = true;
2970 147 [ + + ]: 33 : else if (strcmp(defel->defname, "gtcmp") == 0)
7022 148 : 3 : canMerge = true;
149 : : else
150 : : {
151 : : /* WARNING, not ERROR, for historical backwards-compatibility */
8274 152 [ + - ]: 30 : ereport(WARNING,
153 : : (errcode(ERRCODE_SYNTAX_ERROR),
154 : : errmsg("operator attribute \"%s\" not recognized",
155 : : defel->defname)));
156 : : }
157 : : }
158 : :
159 : : /*
160 : : * make sure we have our required definitions
161 : : */
8734 162 [ + + ]: 817 : if (functionName == NIL)
8274 163 [ + - ]: 6 : ereport(ERROR,
164 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
165 : : errmsg("operator function must be specified")));
166 : :
167 : : /* Transform type names to type OIDs */
8735 168 [ + + ]: 811 : if (typeName1)
5620 peter_e@gmx.net 169 : 774 : typeId1 = typenameTypeId(NULL, typeName1);
8735 tgl@sss.pgh.pa.us 170 [ + + ]: 811 : if (typeName2)
5620 peter_e@gmx.net 171 : 805 : typeId2 = typenameTypeId(NULL, typeName2);
172 : :
173 : : /*
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
178 : : * arguments.
179 : : */
6420 tgl@sss.pgh.pa.us 180 [ + + + + ]: 811 : if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
181 [ + - ]: 3 : ereport(ERROR,
182 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
183 : : errmsg("operator argument types must be specified")));
2005 184 [ + + ]: 808 : if (!OidIsValid(typeId2))
185 [ + - ]: 3 : ereport(ERROR,
186 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
187 : : errmsg("operator right argument type must be specified"),
188 : : errdetail("Postfix operators are not supported.")));
189 : :
5199 peter_e@gmx.net 190 [ + + ]: 805 : if (typeName1)
191 : : {
1218 peter@eisentraut.org 192 : 771 : aclresult = object_aclcheck(TypeRelationId, typeId1, GetUserId(), ACL_USAGE);
5199 peter_e@gmx.net 193 [ + + ]: 771 : if (aclresult != ACLCHECK_OK)
5021 194 : 6 : aclcheck_error_type(aclresult, typeId1);
195 : : }
196 : :
5199 197 [ + - ]: 799 : if (typeName2)
198 : : {
1218 peter@eisentraut.org 199 : 799 : aclresult = object_aclcheck(TypeRelationId, typeId2, GetUserId(), ACL_USAGE);
5199 peter_e@gmx.net 200 [ + + ]: 799 : if (aclresult != ACLCHECK_OK)
5021 201 : 3 : aclcheck_error_type(aclresult, typeId2);
202 : : }
203 : :
204 : : /*
205 : : * Look up the operator's underlying function.
206 : : */
6420 tgl@sss.pgh.pa.us 207 [ + + ]: 796 : if (!OidIsValid(typeId1))
208 : : {
209 : 34 : typeId[0] = typeId2;
210 : 34 : nargs = 1;
211 : : }
212 [ - + ]: 762 : else if (!OidIsValid(typeId2))
213 : : {
6420 tgl@sss.pgh.pa.us 214 :UBC 0 : typeId[0] = typeId1;
215 : 0 : nargs = 1;
216 : : }
217 : : else
218 : : {
6420 tgl@sss.pgh.pa.us 219 :CBC 762 : typeId[0] = typeId1;
220 : 762 : typeId[1] = typeId2;
221 : 762 : nargs = 2;
222 : : }
223 : 796 : functionOid = LookupFuncName(functionName, nargs, typeId, false);
224 : :
225 : : /*
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.
229 : : */
1218 peter@eisentraut.org 230 : 796 : aclresult = object_aclcheck(ProcedureRelationId, functionOid, GetUserId(), ACL_EXECUTE);
6420 tgl@sss.pgh.pa.us 231 [ + + ]: 796 : if (aclresult != ACLCHECK_OK)
3025 peter_e@gmx.net 232 : 3 : aclcheck_error(aclresult, OBJECT_FUNCTION,
6420 tgl@sss.pgh.pa.us 233 : 3 : NameListToString(functionName));
234 : :
5199 peter_e@gmx.net 235 : 793 : rettype = get_func_rettype(functionOid);
1218 peter@eisentraut.org 236 : 793 : aclresult = object_aclcheck(TypeRelationId, rettype, GetUserId(), ACL_USAGE);
5199 peter_e@gmx.net 237 [ + + ]: 793 : if (aclresult != ACLCHECK_OK)
5021 238 : 3 : aclcheck_error_type(aclresult, rettype);
239 : :
240 : : /*
241 : : * Look up restriction and join estimators if specified
242 : : */
6420 tgl@sss.pgh.pa.us 243 [ + + ]: 790 : if (restrictionName)
3897 heikki.linnakangas@i 244 : 520 : restrictionOid = ValidateRestrictionEstimator(restrictionName);
245 : : else
6420 tgl@sss.pgh.pa.us 246 : 270 : restrictionOid = InvalidOid;
247 [ + + ]: 790 : if (joinName)
3897 heikki.linnakangas@i 248 : 505 : joinOid = ValidateJoinEstimator(joinName);
249 : : else
6420 tgl@sss.pgh.pa.us 250 : 285 : joinOid = InvalidOid;
251 : :
252 : : /*
253 : : * now have OperatorCreate do all the work..
254 : : */
255 : : return
4673 bruce@momjian.us 256 : 790 : 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 */
267 : : }
268 : :
269 : : /*
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
272 : : * operator.
273 : : */
274 : : static Oid
3897 heikki.linnakangas@i 275 : 747 : ValidateRestrictionEstimator(List *restrictionName)
276 : : {
277 : : Oid typeId[4];
278 : : Oid restrictionOid;
279 : :
280 : 747 : typeId[0] = INTERNALOID; /* PlannerInfo */
281 : 747 : typeId[1] = OIDOID; /* operator OID */
282 : 747 : typeId[2] = INTERNALOID; /* args list */
283 : 747 : typeId[3] = INT4OID; /* varRelid */
284 : :
285 : 747 : restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
286 : :
287 : : /* estimators must return float8 */
288 [ - + ]: 744 : if (get_func_rettype(restrictionOid) != FLOAT8OID)
3897 heikki.linnakangas@i 289 [ # # ]:UBC 0 : ereport(ERROR,
290 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
291 : : errmsg("restriction estimator function %s must return type %s",
292 : : NameListToString(restrictionName), "float8")));
293 : :
294 : : /*
295 : : * If the estimator is not a built-in function, require superuser
296 : : * privilege to install it. This protects against using something that is
297 : : * not a restriction estimator or has hard-wired assumptions about what
298 : : * data types it is working with. (Built-in estimators are required to
299 : : * defend themselves adequately against unexpected data type choices, but
300 : : * it seems impractical to expect that of extensions' estimators.)
301 : : *
302 : : * If it is built-in, only require EXECUTE rights.
303 : : */
34 tgl@sss.pgh.pa.us 304 [ + + ]:CBC 744 : if (restrictionOid >= FirstGenbkiObjectId)
305 : : {
306 [ - + ]: 17 : if (!superuser())
34 tgl@sss.pgh.pa.us 307 [ # # ]:UBC 0 : ereport(ERROR,
308 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
309 : : errmsg("must be superuser to specify a non-built-in restriction estimator function")));
310 : : }
311 : : else
312 : : {
313 : : AclResult aclresult;
314 : :
34 tgl@sss.pgh.pa.us 315 :CBC 727 : aclresult = object_aclcheck(ProcedureRelationId, restrictionOid,
316 : : GetUserId(), ACL_EXECUTE);
317 [ - + ]: 727 : if (aclresult != ACLCHECK_OK)
34 tgl@sss.pgh.pa.us 318 :UBC 0 : aclcheck_error(aclresult, OBJECT_FUNCTION,
319 : 0 : NameListToString(restrictionName));
320 : : }
321 : :
3897 heikki.linnakangas@i 322 :CBC 744 : return restrictionOid;
323 : : }
324 : :
325 : : /*
326 : : * Look up a join estimator function by name, and verify that it has the
327 : : * correct signature and we have the permissions to attach it to an
328 : : * operator.
329 : : */
330 : : static Oid
331 : 732 : ValidateJoinEstimator(List *joinName)
332 : : {
333 : : Oid typeId[5];
334 : : Oid joinOid;
335 : : Oid joinOid2;
336 : :
337 : 732 : typeId[0] = INTERNALOID; /* PlannerInfo */
338 : 732 : typeId[1] = OIDOID; /* operator OID */
339 : 732 : typeId[2] = INTERNALOID; /* args list */
340 : 732 : typeId[3] = INT2OID; /* jointype */
341 : 732 : typeId[4] = INTERNALOID; /* SpecialJoinInfo */
342 : :
343 : : /*
344 : : * As of Postgres 8.4, the preferred signature for join estimators has 5
345 : : * arguments, but we still allow the old 4-argument form. Whine about
346 : : * ambiguity if both forms exist.
347 : : */
348 : 732 : joinOid = LookupFuncName(joinName, 5, typeId, true);
2043 tgl@sss.pgh.pa.us 349 : 732 : joinOid2 = LookupFuncName(joinName, 4, typeId, true);
350 [ + + ]: 732 : if (OidIsValid(joinOid))
351 : : {
352 [ - + ]: 729 : if (OidIsValid(joinOid2))
2043 tgl@sss.pgh.pa.us 353 [ # # ]:UBC 0 : ereport(ERROR,
354 : : (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
355 : : errmsg("join estimator function %s has multiple matches",
356 : : NameListToString(joinName))));
357 : : }
358 : : else
359 : : {
2043 tgl@sss.pgh.pa.us 360 :CBC 3 : joinOid = joinOid2;
361 : : /* If not found, reference the 5-argument signature in error msg */
362 [ + - ]: 3 : if (!OidIsValid(joinOid))
363 : 3 : joinOid = LookupFuncName(joinName, 5, typeId, false);
364 : : }
365 : :
366 : : /* estimators must return float8 */
3897 heikki.linnakangas@i 367 [ - + ]: 729 : if (get_func_rettype(joinOid) != FLOAT8OID)
3897 heikki.linnakangas@i 368 [ # # ]:UBC 0 : ereport(ERROR,
369 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
370 : : errmsg("join estimator function %s must return type %s",
371 : : NameListToString(joinName), "float8")));
372 : :
373 : : /* privilege checks are the same as in ValidateRestrictionEstimator */
34 tgl@sss.pgh.pa.us 374 [ + + ]:CBC 729 : if (joinOid >= FirstGenbkiObjectId)
375 : : {
376 [ - + ]: 5 : if (!superuser())
34 tgl@sss.pgh.pa.us 377 [ # # ]:UBC 0 : ereport(ERROR,
378 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
379 : : errmsg("must be superuser to specify a non-built-in join estimator function")));
380 : : }
381 : : else
382 : : {
383 : : AclResult aclresult;
384 : :
34 tgl@sss.pgh.pa.us 385 :CBC 724 : aclresult = object_aclcheck(ProcedureRelationId, joinOid,
386 : : GetUserId(), ACL_EXECUTE);
387 [ - + ]: 724 : if (aclresult != ACLCHECK_OK)
34 tgl@sss.pgh.pa.us 388 :UBC 0 : aclcheck_error(aclresult, OBJECT_FUNCTION,
389 : 0 : NameListToString(joinName));
390 : : }
391 : :
3897 heikki.linnakangas@i 392 :CBC 729 : return joinOid;
393 : : }
394 : :
395 : : /*
396 : : * Look up and return the OID of an operator,
397 : : * given a possibly-qualified name and left and right type IDs.
398 : : *
399 : : * Verifies that the operator is defined (not a shell) and owned by
400 : : * the current user, so that we have permission to associate it with
401 : : * the operator being altered. Rejecting shell operators is a policy
402 : : * choice to help catch mistakes, rather than something essential.
403 : : */
404 : : static Oid
877 tgl@sss.pgh.pa.us 405 : 27 : ValidateOperatorReference(List *name,
406 : : Oid leftTypeId,
407 : : Oid rightTypeId)
408 : : {
409 : : Oid oid;
410 : : bool defined;
411 : :
412 : 27 : oid = OperatorLookup(name,
413 : : leftTypeId,
414 : : rightTypeId,
415 : : &defined);
416 : :
417 : : /* These message strings are chosen to match parse_oper.c */
418 [ - + ]: 27 : if (!OidIsValid(oid))
877 tgl@sss.pgh.pa.us 419 [ # # ]:UBC 0 : ereport(ERROR,
420 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
421 : : errmsg("operator does not exist: %s",
422 : : op_signature_string(name,
423 : : leftTypeId,
424 : : rightTypeId))));
425 : :
877 tgl@sss.pgh.pa.us 426 [ - + ]:CBC 27 : if (!defined)
877 tgl@sss.pgh.pa.us 427 [ # # ]:UBC 0 : ereport(ERROR,
428 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
429 : : errmsg("operator is only a shell: %s",
430 : : op_signature_string(name,
431 : : leftTypeId,
432 : : rightTypeId))));
433 : :
877 tgl@sss.pgh.pa.us 434 [ - + ]:CBC 27 : if (!object_ownercheck(OperatorRelationId, oid, GetUserId()))
877 tgl@sss.pgh.pa.us 435 :UBC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
436 : 0 : NameListToString(name));
437 : :
877 tgl@sss.pgh.pa.us 438 :CBC 27 : return oid;
439 : : }
440 : :
441 : :
442 : : /*
443 : : * Guts of operator deletion.
444 : : */
445 : : void
8647 446 : 400 : RemoveOperatorById(Oid operOid)
447 : : {
448 : : Relation relation;
449 : : HeapTuple tup;
450 : : Form_pg_operator op;
451 : :
2610 andres@anarazel.de 452 : 400 : relation = table_open(OperatorRelationId, RowExclusiveLock);
453 : :
5873 rhaas@postgresql.org 454 : 400 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
8593 bruce@momjian.us 455 [ - + ]: 400 : if (!HeapTupleIsValid(tup)) /* should not happen */
8274 tgl@sss.pgh.pa.us 456 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator %u", operOid);
3642 tgl@sss.pgh.pa.us 457 :CBC 400 : op = (Form_pg_operator) GETSTRUCT(tup);
458 : :
459 : : /*
460 : : * Reset links from commutator and negator, if any. In case of a
461 : : * self-commutator or self-negator, this means we have to re-fetch the
462 : : * updated tuple. (We could optimize away updates on the tuple we're
463 : : * about to drop, but it doesn't seem worth convoluting the logic for.)
464 : : */
465 [ + + - + ]: 400 : if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
466 : : {
467 : 193 : OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
468 [ + + - + ]: 193 : if (operOid == op->oprcom || operOid == op->oprnegate)
469 : : {
470 : 76 : ReleaseSysCache(tup);
471 : 76 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
472 [ - + ]: 76 : if (!HeapTupleIsValid(tup)) /* should not happen */
3642 tgl@sss.pgh.pa.us 473 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator %u", operOid);
474 : : }
475 : : }
476 : :
3329 tgl@sss.pgh.pa.us 477 :CBC 400 : CatalogTupleDelete(relation, &tup->t_self);
478 : :
8647 479 : 400 : ReleaseSysCache(tup);
480 : :
2610 andres@anarazel.de 481 : 400 : table_close(relation, RowExclusiveLock);
8735 tgl@sss.pgh.pa.us 482 : 400 : }
483 : :
484 : : /*
485 : : * AlterOperator
486 : : * routine implementing ALTER OPERATOR <operator> SET (option = ...).
487 : : *
488 : : * Currently, only RESTRICT and JOIN estimator functions can be changed.
489 : : * COMMUTATOR, NEGATOR, MERGES, and HASHES attributes can be set if they
490 : : * have not been set previously. (Changing or removing one of these
491 : : * attributes could invalidate existing plans, which seems more trouble
492 : : * than it's worth.)
493 : : */
494 : : ObjectAddress
3897 heikki.linnakangas@i 495 : 304 : AlterOperator(AlterOperatorStmt *stmt)
496 : : {
497 : : ObjectAddress address;
498 : : Oid oprId;
499 : : Relation catalog;
500 : : HeapTuple tup;
501 : : Form_pg_operator oprForm;
502 : : int i;
503 : : ListCell *pl;
504 : : Datum values[Natts_pg_operator];
505 : : bool nulls[Natts_pg_operator];
506 : : bool replaces[Natts_pg_operator];
2769 peter_e@gmx.net 507 : 304 : List *restrictionName = NIL; /* optional restrict. sel. function */
3897 heikki.linnakangas@i 508 : 304 : bool updateRestriction = false;
509 : : Oid restrictionOid;
2769 peter_e@gmx.net 510 : 304 : List *joinName = NIL; /* optional join sel. function */
3897 heikki.linnakangas@i 511 : 304 : bool updateJoin = false;
512 : : Oid joinOid;
877 tgl@sss.pgh.pa.us 513 : 304 : List *commutatorName = NIL; /* optional commutator operator name */
514 : : Oid commutatorOid;
515 : 304 : List *negatorName = NIL; /* optional negator operator name */
516 : : Oid negatorOid;
517 : 304 : bool canMerge = false;
518 : 304 : bool updateMerges = false;
519 : 304 : bool canHash = false;
520 : 304 : bool updateHashes = false;
521 : :
522 : : /* Look up the operator */
3364 peter_e@gmx.net 523 : 304 : oprId = LookupOperWithArgs(stmt->opername, false);
2610 andres@anarazel.de 524 : 304 : catalog = table_open(OperatorRelationId, RowExclusiveLock);
3897 heikki.linnakangas@i 525 : 304 : tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
2506 tgl@sss.pgh.pa.us 526 [ - + ]: 304 : if (!HeapTupleIsValid(tup))
3897 heikki.linnakangas@i 527 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator %u", oprId);
3897 heikki.linnakangas@i 528 :CBC 304 : oprForm = (Form_pg_operator) GETSTRUCT(tup);
529 : :
530 : : /* Process options */
531 [ + - + + : 829 : foreach(pl, stmt->options)
+ + ]
532 : : {
533 : 528 : DefElem *defel = (DefElem *) lfirst(pl);
534 : : List *param;
535 : :
536 [ + + ]: 528 : if (defel->arg == NULL)
537 : 32 : param = NIL; /* NONE, removes the function */
538 : : else
539 : 496 : param = defGetQualifiedName(defel);
540 : :
2970 tgl@sss.pgh.pa.us 541 [ + + ]: 528 : if (strcmp(defel->defname, "restrict") == 0)
542 : : {
3897 heikki.linnakangas@i 543 : 236 : restrictionName = param;
544 : 236 : updateRestriction = true;
545 : : }
2970 tgl@sss.pgh.pa.us 546 [ + + ]: 292 : else if (strcmp(defel->defname, "join") == 0)
547 : : {
3897 heikki.linnakangas@i 548 : 233 : joinName = param;
549 : 233 : updateJoin = true;
550 : : }
877 tgl@sss.pgh.pa.us 551 [ + + ]: 59 : else if (strcmp(defel->defname, "commutator") == 0)
552 : : {
553 : 12 : commutatorName = defGetQualifiedName(defel);
554 : : }
555 [ + + ]: 47 : else if (strcmp(defel->defname, "negator") == 0)
556 : : {
557 : 15 : negatorName = defGetQualifiedName(defel);
558 : : }
559 [ + + ]: 32 : else if (strcmp(defel->defname, "merges") == 0)
560 : : {
561 : 12 : canMerge = defGetBoolean(defel);
562 : 12 : updateMerges = true;
563 : : }
564 [ + + ]: 20 : else if (strcmp(defel->defname, "hashes") == 0)
565 : : {
566 : 17 : canHash = defGetBoolean(defel);
567 : 17 : updateHashes = true;
568 : : }
569 : :
570 : : /*
571 : : * The rest of the options that CREATE accepts cannot be changed.
572 : : * Check for them so that we can give a meaningful error message.
573 : : */
2970 574 [ + - ]: 3 : else if (strcmp(defel->defname, "leftarg") == 0 ||
575 [ + - ]: 3 : strcmp(defel->defname, "rightarg") == 0 ||
2769 peter_e@gmx.net 576 [ + - ]: 3 : strcmp(defel->defname, "function") == 0 ||
877 tgl@sss.pgh.pa.us 577 [ - + ]: 3 : strcmp(defel->defname, "procedure") == 0)
578 : : {
3897 heikki.linnakangas@i 579 [ # # ]:UBC 0 : ereport(ERROR,
580 : : (errcode(ERRCODE_SYNTAX_ERROR),
581 : : errmsg("operator attribute \"%s\" cannot be changed",
582 : : defel->defname)));
583 : : }
584 : : else
3897 heikki.linnakangas@i 585 [ + - ]:CBC 3 : ereport(ERROR,
586 : : (errcode(ERRCODE_SYNTAX_ERROR),
587 : : errmsg("operator attribute \"%s\" not recognized",
588 : : defel->defname)));
589 : : }
590 : :
591 : : /* Check permissions. Must be owner. */
1218 peter@eisentraut.org 592 [ + + ]: 301 : if (!object_ownercheck(OperatorRelationId, oprId, GetUserId()))
3025 peter_e@gmx.net 593 : 3 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
3897 heikki.linnakangas@i 594 : 3 : NameStr(oprForm->oprname));
595 : :
596 : : /*
597 : : * Look up OIDs for any parameters specified
598 : : */
599 [ + + ]: 298 : if (restrictionName)
600 : 227 : restrictionOid = ValidateRestrictionEstimator(restrictionName);
601 : : else
602 : 71 : restrictionOid = InvalidOid;
603 [ + + ]: 295 : if (joinName)
604 : 227 : joinOid = ValidateJoinEstimator(joinName);
605 : : else
606 : 68 : joinOid = InvalidOid;
607 : :
877 tgl@sss.pgh.pa.us 608 [ + + ]: 292 : if (commutatorName)
609 : : {
610 : : /* commutator has reversed arg types */
611 : 12 : commutatorOid = ValidateOperatorReference(commutatorName,
612 : : oprForm->oprright,
613 : : oprForm->oprleft);
614 : :
615 : : /*
616 : : * We don't need to do anything extra for a self commutator as in
617 : : * OperatorCreate, since the operator surely exists already.
618 : : */
619 : : }
620 : : else
621 : 280 : commutatorOid = InvalidOid;
622 : :
623 [ + + ]: 292 : if (negatorName)
624 : : {
625 : 15 : negatorOid = ValidateOperatorReference(negatorName,
626 : : oprForm->oprleft,
627 : : oprForm->oprright);
628 : :
629 : : /* Must reject self-negation */
630 [ + + ]: 15 : if (negatorOid == oprForm->oid)
3897 heikki.linnakangas@i 631 [ + - ]: 3 : ereport(ERROR,
632 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
633 : : errmsg("operator cannot be its own negator")));
634 : : }
635 : : else
636 : : {
877 tgl@sss.pgh.pa.us 637 : 277 : negatorOid = InvalidOid;
638 : : }
639 : :
640 : : /*
641 : : * Check that we're not changing any attributes that might be depended on
642 : : * by plans, while allowing no-op updates.
643 : : */
644 [ + + + + ]: 289 : if (OidIsValid(commutatorOid) && OidIsValid(oprForm->oprcom) &&
645 [ + + ]: 6 : commutatorOid != oprForm->oprcom)
646 [ + - ]: 3 : ereport(ERROR,
647 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
648 : : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
649 : : "commutator")));
650 : :
651 [ + + + + ]: 286 : if (OidIsValid(negatorOid) && OidIsValid(oprForm->oprnegate) &&
652 [ + + ]: 6 : negatorOid != oprForm->oprnegate)
653 [ + - ]: 3 : ereport(ERROR,
654 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
655 : : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
656 : : "negator")));
657 : :
658 [ + + + + : 283 : if (updateMerges && oprForm->oprcanmerge && !canMerge)
+ + ]
659 [ + - ]: 3 : ereport(ERROR,
660 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
661 : : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
662 : : "merges")));
663 : :
664 [ + + + + : 280 : if (updateHashes && oprForm->oprcanhash && !canHash)
+ + ]
665 [ + - ]: 3 : ereport(ERROR,
666 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
667 : : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
668 : : "hashes")));
669 : :
670 : : /* Perform additional checks, like OperatorCreate does */
671 : 277 : OperatorValidateParams(oprForm->oprleft,
672 : : oprForm->oprright,
673 : : oprForm->oprresult,
674 : : OidIsValid(commutatorOid),
675 : : OidIsValid(negatorOid),
676 : : OidIsValid(restrictionOid),
677 : : OidIsValid(joinOid),
678 : : canMerge,
679 : : canHash);
680 : :
681 : : /* Update the tuple */
3897 heikki.linnakangas@i 682 [ + + ]: 4432 : for (i = 0; i < Natts_pg_operator; ++i)
683 : : {
684 : 4155 : values[i] = (Datum) 0;
685 : 4155 : replaces[i] = false;
686 : 4155 : nulls[i] = false;
687 : : }
688 [ + + ]: 277 : if (updateRestriction)
689 : : {
690 : 230 : replaces[Anum_pg_operator_oprrest - 1] = true;
877 tgl@sss.pgh.pa.us 691 : 230 : values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionOid);
692 : : }
3897 heikki.linnakangas@i 693 [ + + ]: 277 : if (updateJoin)
694 : : {
695 : 230 : replaces[Anum_pg_operator_oprjoin - 1] = true;
877 tgl@sss.pgh.pa.us 696 : 230 : values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid);
697 : : }
698 [ + + ]: 277 : if (OidIsValid(commutatorOid))
699 : : {
700 : 9 : replaces[Anum_pg_operator_oprcom - 1] = true;
701 : 9 : values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorOid);
702 : : }
703 [ + + ]: 277 : if (OidIsValid(negatorOid))
704 : : {
705 : 9 : replaces[Anum_pg_operator_oprnegate - 1] = true;
706 : 9 : values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorOid);
707 : : }
708 [ + + ]: 277 : if (updateMerges)
709 : : {
710 : 9 : replaces[Anum_pg_operator_oprcanmerge - 1] = true;
711 : 9 : values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge);
712 : : }
713 [ + + ]: 277 : if (updateHashes)
714 : : {
715 : 14 : replaces[Anum_pg_operator_oprcanhash - 1] = true;
716 : 14 : values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash);
717 : : }
718 : :
3897 heikki.linnakangas@i 719 : 277 : tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
720 : : values, nulls, replaces);
721 : :
3330 alvherre@alvh.no-ip. 722 : 277 : CatalogTupleUpdate(catalog, &tup->t_self, tup);
723 : :
1671 tgl@sss.pgh.pa.us 724 : 277 : address = makeOperatorDependencies(tup, false, true);
725 : :
877 726 [ + + + + ]: 277 : if (OidIsValid(commutatorOid) || OidIsValid(negatorOid))
727 : 18 : OperatorUpd(oprId, commutatorOid, negatorOid, false);
728 : :
3727 729 [ - + ]: 271 : InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
730 : :
2610 andres@anarazel.de 731 : 271 : table_close(catalog, NoLock);
732 : :
3897 heikki.linnakangas@i 733 : 271 : return address;
734 : : }
|