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-2024, 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 "FooDefine" routines (in src/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
8035 tgl@sss.pgh.pa.us 67 :CBC 789 : DefineOperator(List *names, List *parameters)
68 : : {
69 : : char *oprName;
70 : : Oid oprNamespace;
71 : : AclResult aclresult;
2489 72 : 789 : bool canMerge = false; /* operator merges */
5995 bruce@momjian.us 73 : 789 : bool canHash = false; /* operator hashes */
2489 tgl@sss.pgh.pa.us 74 : 789 : List *functionName = NIL; /* function for operator */
75 : 789 : TypeName *typeName1 = NULL; /* first type name */
76 : 789 : TypeName *typeName2 = NULL; /* second type name */
8035 77 : 789 : Oid typeId1 = InvalidOid; /* types converted to OID */
78 : 789 : Oid typeId2 = InvalidOid;
79 : : Oid rettype;
6756 bruce@momjian.us 80 : 789 : List *commutatorName = NIL; /* optional commutator operator name */
2489 tgl@sss.pgh.pa.us 81 : 789 : List *negatorName = NIL; /* optional negator operator name */
2069 peter_e@gmx.net 82 : 789 : List *restrictionName = NIL; /* optional restrict. sel. function */
83 : 789 : 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 */
8035 tgl@sss.pgh.pa.us 92 : 789 : oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
93 : :
94 : : /* Check we have creation rights in target namespace */
518 peter@eisentraut.org 95 : 789 : aclresult = object_aclcheck(NamespaceRelationId, oprNamespace, GetUserId(), ACL_CREATE);
8023 tgl@sss.pgh.pa.us 96 [ + + ]: 789 : if (aclresult != ACLCHECK_OK)
2325 peter_e@gmx.net 97 : 3 : aclcheck_error(aclresult, OBJECT_SCHEMA,
7562 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 : : */
8035 103 [ + - + + : 5069 : foreach(pl, parameters)
+ + ]
104 : : {
105 : 4289 : DefElem *defel = (DefElem *) lfirst(pl);
106 : :
2270 107 [ + + ]: 4289 : if (strcmp(defel->defname, "leftarg") == 0)
108 : : {
8035 109 : 740 : typeName1 = defGetTypeName(defel);
110 [ + + ]: 740 : if (typeName1->setof)
7574 111 [ + - ]: 3 : ereport(ERROR,
112 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
113 : : errmsg("SETOF type not allowed for operator argument")));
114 : : }
2270 115 [ + + ]: 3549 : else if (strcmp(defel->defname, "rightarg") == 0)
116 : : {
8035 117 : 774 : typeName2 = defGetTypeName(defel);
118 [ + + ]: 774 : if (typeName2->setof)
7574 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 */
2069 peter_e@gmx.net 124 [ + + ]: 2775 : else if (strcmp(defel->defname, "function") == 0)
125 : 14 : functionName = defGetQualifiedName(defel);
2270 tgl@sss.pgh.pa.us 126 [ + + ]: 2761 : else if (strcmp(defel->defname, "procedure") == 0)
8034 127 : 760 : functionName = defGetQualifiedName(defel);
2270 128 [ + + ]: 2001 : else if (strcmp(defel->defname, "commutator") == 0)
8034 129 : 493 : commutatorName = defGetQualifiedName(defel);
2270 130 [ + + ]: 1508 : else if (strcmp(defel->defname, "negator") == 0)
8034 131 : 335 : negatorName = defGetQualifiedName(defel);
2270 132 [ + + ]: 1173 : else if (strcmp(defel->defname, "restrict") == 0)
8034 133 : 511 : restrictionName = defGetQualifiedName(defel);
2270 134 [ + + ]: 662 : else if (strcmp(defel->defname, "join") == 0)
8034 135 : 499 : joinName = defGetQualifiedName(defel);
2270 136 [ + + ]: 163 : else if (strcmp(defel->defname, "hashes") == 0)
7275 137 : 46 : canHash = defGetBoolean(defel);
2270 138 [ + + ]: 117 : else if (strcmp(defel->defname, "merges") == 0)
7275 139 : 71 : canMerge = defGetBoolean(defel);
140 : : /* These obsolete options are taken as meaning canMerge */
2270 141 [ + + ]: 46 : else if (strcmp(defel->defname, "sort1") == 0)
6322 142 : 5 : canMerge = true;
2270 143 [ + + ]: 41 : else if (strcmp(defel->defname, "sort2") == 0)
6322 144 : 5 : canMerge = true;
2270 145 [ + + ]: 36 : else if (strcmp(defel->defname, "ltcmp") == 0)
6322 146 : 3 : canMerge = true;
2270 147 [ + + ]: 33 : else if (strcmp(defel->defname, "gtcmp") == 0)
6322 148 : 3 : canMerge = true;
149 : : else
150 : : {
151 : : /* WARNING, not ERROR, for historical backwards-compatibility */
7574 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 : : */
8034 162 [ + + ]: 780 : if (functionName == NIL)
7574 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 */
8035 168 [ + + ]: 774 : if (typeName1)
4920 peter_e@gmx.net 169 : 737 : typeId1 = typenameTypeId(NULL, typeName1);
8035 tgl@sss.pgh.pa.us 170 [ + + ]: 774 : if (typeName2)
4920 peter_e@gmx.net 171 : 768 : 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 : : */
5720 tgl@sss.pgh.pa.us 180 [ + + + + ]: 774 : if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
181 [ + - ]: 3 : ereport(ERROR,
182 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
183 : : errmsg("operator argument types must be specified")));
1305 184 [ + + ]: 771 : 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 : :
4499 peter_e@gmx.net 190 [ + + ]: 768 : if (typeName1)
191 : : {
518 peter@eisentraut.org 192 : 734 : aclresult = object_aclcheck(TypeRelationId, typeId1, GetUserId(), ACL_USAGE);
4499 peter_e@gmx.net 193 [ + + ]: 734 : if (aclresult != ACLCHECK_OK)
4321 194 : 6 : aclcheck_error_type(aclresult, typeId1);
195 : : }
196 : :
4499 197 [ + - ]: 762 : if (typeName2)
198 : : {
518 peter@eisentraut.org 199 : 762 : aclresult = object_aclcheck(TypeRelationId, typeId2, GetUserId(), ACL_USAGE);
4499 peter_e@gmx.net 200 [ + + ]: 762 : if (aclresult != ACLCHECK_OK)
4321 201 : 3 : aclcheck_error_type(aclresult, typeId2);
202 : : }
203 : :
204 : : /*
205 : : * Look up the operator's underlying function.
206 : : */
5720 tgl@sss.pgh.pa.us 207 [ + + ]: 759 : if (!OidIsValid(typeId1))
208 : : {
209 : 34 : typeId[0] = typeId2;
210 : 34 : nargs = 1;
211 : : }
212 [ - + ]: 725 : else if (!OidIsValid(typeId2))
213 : : {
5720 tgl@sss.pgh.pa.us 214 :UBC 0 : typeId[0] = typeId1;
215 : 0 : nargs = 1;
216 : : }
217 : : else
218 : : {
5720 tgl@sss.pgh.pa.us 219 :CBC 725 : typeId[0] = typeId1;
220 : 725 : typeId[1] = typeId2;
221 : 725 : nargs = 2;
222 : : }
223 : 759 : 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 : : */
518 peter@eisentraut.org 230 : 759 : aclresult = object_aclcheck(ProcedureRelationId, functionOid, GetUserId(), ACL_EXECUTE);
5720 tgl@sss.pgh.pa.us 231 [ + + ]: 759 : if (aclresult != ACLCHECK_OK)
2325 peter_e@gmx.net 232 : 3 : aclcheck_error(aclresult, OBJECT_FUNCTION,
5720 tgl@sss.pgh.pa.us 233 : 3 : NameListToString(functionName));
234 : :
4499 peter_e@gmx.net 235 : 756 : rettype = get_func_rettype(functionOid);
518 peter@eisentraut.org 236 : 756 : aclresult = object_aclcheck(TypeRelationId, rettype, GetUserId(), ACL_USAGE);
4499 peter_e@gmx.net 237 [ + + ]: 756 : if (aclresult != ACLCHECK_OK)
4321 238 : 3 : aclcheck_error_type(aclresult, rettype);
239 : :
240 : : /*
241 : : * Look up restriction and join estimators if specified
242 : : */
5720 tgl@sss.pgh.pa.us 243 [ + + ]: 753 : if (restrictionName)
3197 heikki.linnakangas@i 244 : 511 : restrictionOid = ValidateRestrictionEstimator(restrictionName);
245 : : else
5720 tgl@sss.pgh.pa.us 246 : 242 : restrictionOid = InvalidOid;
247 [ + + ]: 753 : if (joinName)
3197 heikki.linnakangas@i 248 : 499 : joinOid = ValidateJoinEstimator(joinName);
249 : : else
5720 tgl@sss.pgh.pa.us 250 : 254 : joinOid = InvalidOid;
251 : :
252 : : /*
253 : : * now have OperatorCreate do all the work..
254 : : */
255 : : return
3973 bruce@momjian.us 256 : 753 : 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
3197 heikki.linnakangas@i 275 : 738 : ValidateRestrictionEstimator(List *restrictionName)
276 : : {
277 : : Oid typeId[4];
278 : : Oid restrictionOid;
279 : : AclResult aclresult;
280 : :
281 : 738 : typeId[0] = INTERNALOID; /* PlannerInfo */
282 : 738 : typeId[1] = OIDOID; /* operator OID */
283 : 738 : typeId[2] = INTERNALOID; /* args list */
284 : 738 : typeId[3] = INT4OID; /* varRelid */
285 : :
286 : 738 : restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
287 : :
288 : : /* estimators must return float8 */
289 [ - + ]: 735 : if (get_func_rettype(restrictionOid) != FLOAT8OID)
3197 heikki.linnakangas@i 290 [ # # ]:UBC 0 : ereport(ERROR,
291 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
292 : : errmsg("restriction estimator function %s must return type %s",
293 : : NameListToString(restrictionName), "float8")));
294 : :
295 : : /* Require EXECUTE rights for the estimator */
518 peter@eisentraut.org 296 :CBC 735 : aclresult = object_aclcheck(ProcedureRelationId, restrictionOid, GetUserId(), ACL_EXECUTE);
3197 heikki.linnakangas@i 297 [ - + ]: 735 : if (aclresult != ACLCHECK_OK)
2325 peter_e@gmx.net 298 :UBC 0 : aclcheck_error(aclresult, OBJECT_FUNCTION,
3197 heikki.linnakangas@i 299 : 0 : NameListToString(restrictionName));
300 : :
3197 heikki.linnakangas@i 301 :CBC 735 : return restrictionOid;
302 : : }
303 : :
304 : : /*
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
307 : : * operator.
308 : : */
309 : : static Oid
310 : 726 : ValidateJoinEstimator(List *joinName)
311 : : {
312 : : Oid typeId[5];
313 : : Oid joinOid;
314 : : Oid joinOid2;
315 : : AclResult aclresult;
316 : :
317 : 726 : typeId[0] = INTERNALOID; /* PlannerInfo */
318 : 726 : typeId[1] = OIDOID; /* operator OID */
319 : 726 : typeId[2] = INTERNALOID; /* args list */
320 : 726 : typeId[3] = INT2OID; /* jointype */
321 : 726 : typeId[4] = INTERNALOID; /* SpecialJoinInfo */
322 : :
323 : : /*
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.
327 : : */
328 : 726 : joinOid = LookupFuncName(joinName, 5, typeId, true);
1343 tgl@sss.pgh.pa.us 329 : 726 : joinOid2 = LookupFuncName(joinName, 4, typeId, true);
330 [ + + ]: 726 : if (OidIsValid(joinOid))
331 : : {
332 [ - + ]: 723 : if (OidIsValid(joinOid2))
1343 tgl@sss.pgh.pa.us 333 [ # # ]:UBC 0 : ereport(ERROR,
334 : : (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
335 : : errmsg("join estimator function %s has multiple matches",
336 : : NameListToString(joinName))));
337 : : }
338 : : else
339 : : {
1343 tgl@sss.pgh.pa.us 340 :CBC 3 : joinOid = joinOid2;
341 : : /* If not found, reference the 5-argument signature in error msg */
342 [ + - ]: 3 : if (!OidIsValid(joinOid))
343 : 3 : joinOid = LookupFuncName(joinName, 5, typeId, false);
344 : : }
345 : :
346 : : /* estimators must return float8 */
3197 heikki.linnakangas@i 347 [ - + ]: 723 : if (get_func_rettype(joinOid) != FLOAT8OID)
3197 heikki.linnakangas@i 348 [ # # ]:UBC 0 : ereport(ERROR,
349 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
350 : : errmsg("join estimator function %s must return type %s",
351 : : NameListToString(joinName), "float8")));
352 : :
353 : : /* Require EXECUTE rights for the estimator */
518 peter@eisentraut.org 354 :CBC 723 : aclresult = object_aclcheck(ProcedureRelationId, joinOid, GetUserId(), ACL_EXECUTE);
3197 heikki.linnakangas@i 355 [ - + ]: 723 : if (aclresult != ACLCHECK_OK)
2325 peter_e@gmx.net 356 :UBC 0 : aclcheck_error(aclresult, OBJECT_FUNCTION,
3197 heikki.linnakangas@i 357 : 0 : NameListToString(joinName));
358 : :
3197 heikki.linnakangas@i 359 :CBC 723 : return joinOid;
360 : : }
361 : :
362 : : /*
363 : : * Look up and return the OID of an operator,
364 : : * given a possibly-qualified name and left and right type IDs.
365 : : *
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.
370 : : */
371 : : static Oid
177 tgl@sss.pgh.pa.us 372 :GNC 27 : ValidateOperatorReference(List *name,
373 : : Oid leftTypeId,
374 : : Oid rightTypeId)
375 : : {
376 : : Oid oid;
377 : : bool defined;
378 : :
379 : 27 : oid = OperatorLookup(name,
380 : : leftTypeId,
381 : : rightTypeId,
382 : : &defined);
383 : :
384 : : /* These message strings are chosen to match parse_oper.c */
385 [ - + ]: 27 : if (!OidIsValid(oid))
177 tgl@sss.pgh.pa.us 386 [ # # ]:UNC 0 : ereport(ERROR,
387 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
388 : : errmsg("operator does not exist: %s",
389 : : op_signature_string(name,
390 : : leftTypeId,
391 : : rightTypeId))));
392 : :
177 tgl@sss.pgh.pa.us 393 [ - + ]:GNC 27 : if (!defined)
177 tgl@sss.pgh.pa.us 394 [ # # ]:UNC 0 : ereport(ERROR,
395 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
396 : : errmsg("operator is only a shell: %s",
397 : : op_signature_string(name,
398 : : leftTypeId,
399 : : rightTypeId))));
400 : :
177 tgl@sss.pgh.pa.us 401 [ - + ]:GNC 27 : if (!object_ownercheck(OperatorRelationId, oid, GetUserId()))
177 tgl@sss.pgh.pa.us 402 :UNC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
403 : 0 : NameListToString(name));
404 : :
177 tgl@sss.pgh.pa.us 405 :GNC 27 : return oid;
406 : : }
407 : :
408 : :
409 : : /*
410 : : * Guts of operator deletion.
411 : : */
412 : : void
7947 tgl@sss.pgh.pa.us 413 :CBC 367 : RemoveOperatorById(Oid operOid)
414 : : {
415 : : Relation relation;
416 : : HeapTuple tup;
417 : : Form_pg_operator op;
418 : :
1910 andres@anarazel.de 419 : 367 : relation = table_open(OperatorRelationId, RowExclusiveLock);
420 : :
5173 rhaas@postgresql.org 421 : 367 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
7893 bruce@momjian.us 422 [ - + ]: 367 : if (!HeapTupleIsValid(tup)) /* should not happen */
7574 tgl@sss.pgh.pa.us 423 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator %u", operOid);
2942 tgl@sss.pgh.pa.us 424 :CBC 367 : op = (Form_pg_operator) GETSTRUCT(tup);
425 : :
426 : : /*
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.)
431 : : */
432 [ + + - + ]: 367 : if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
433 : : {
434 : 165 : OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
435 [ + + - + ]: 165 : if (operOid == op->oprcom || operOid == op->oprnegate)
436 : : {
437 : 50 : ReleaseSysCache(tup);
438 : 50 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
439 [ - + ]: 50 : if (!HeapTupleIsValid(tup)) /* should not happen */
2942 tgl@sss.pgh.pa.us 440 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator %u", operOid);
441 : : }
442 : : }
443 : :
2629 tgl@sss.pgh.pa.us 444 :CBC 367 : CatalogTupleDelete(relation, &tup->t_self);
445 : :
7947 446 : 367 : ReleaseSysCache(tup);
447 : :
1910 andres@anarazel.de 448 : 367 : table_close(relation, RowExclusiveLock);
8035 tgl@sss.pgh.pa.us 449 : 367 : }
450 : :
451 : : /*
452 : : * AlterOperator
453 : : * routine implementing ALTER OPERATOR <operator> SET (option = ...).
454 : : *
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
459 : : * than it's worth.)
460 : : */
461 : : ObjectAddress
3197 heikki.linnakangas@i 462 : 301 : AlterOperator(AlterOperatorStmt *stmt)
463 : : {
464 : : ObjectAddress address;
465 : : Oid oprId;
466 : : Relation catalog;
467 : : HeapTuple tup;
468 : : Form_pg_operator oprForm;
469 : : int i;
470 : : ListCell *pl;
471 : : Datum values[Natts_pg_operator];
472 : : bool nulls[Natts_pg_operator];
473 : : bool replaces[Natts_pg_operator];
2069 peter_e@gmx.net 474 : 301 : List *restrictionName = NIL; /* optional restrict. sel. function */
3197 heikki.linnakangas@i 475 : 301 : bool updateRestriction = false;
476 : : Oid restrictionOid;
2069 peter_e@gmx.net 477 : 301 : List *joinName = NIL; /* optional join sel. function */
3197 heikki.linnakangas@i 478 : 301 : bool updateJoin = false;
479 : : Oid joinOid;
177 tgl@sss.pgh.pa.us 480 :GNC 301 : List *commutatorName = NIL; /* optional commutator operator name */
481 : : Oid commutatorOid;
482 : 301 : List *negatorName = NIL; /* optional negator operator name */
483 : : Oid negatorOid;
484 : 301 : bool canMerge = false;
485 : 301 : bool updateMerges = false;
486 : 301 : bool canHash = false;
487 : 301 : bool updateHashes = false;
488 : :
489 : : /* Look up the operator */
2664 peter_e@gmx.net 490 :CBC 301 : oprId = LookupOperWithArgs(stmt->opername, false);
1910 andres@anarazel.de 491 : 301 : catalog = table_open(OperatorRelationId, RowExclusiveLock);
3197 heikki.linnakangas@i 492 : 301 : tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
1806 tgl@sss.pgh.pa.us 493 [ - + ]: 301 : if (!HeapTupleIsValid(tup))
3197 heikki.linnakangas@i 494 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator %u", oprId);
3197 heikki.linnakangas@i 495 :CBC 301 : oprForm = (Form_pg_operator) GETSTRUCT(tup);
496 : :
497 : : /* Process options */
498 [ + - + + : 823 : foreach(pl, stmt->options)
+ + ]
499 : : {
500 : 525 : DefElem *defel = (DefElem *) lfirst(pl);
501 : : List *param;
502 : :
503 [ + + ]: 525 : if (defel->arg == NULL)
504 : 29 : param = NIL; /* NONE, removes the function */
505 : : else
506 : 496 : param = defGetQualifiedName(defel);
507 : :
2270 tgl@sss.pgh.pa.us 508 [ + + ]: 525 : if (strcmp(defel->defname, "restrict") == 0)
509 : : {
3197 heikki.linnakangas@i 510 : 236 : restrictionName = param;
511 : 236 : updateRestriction = true;
512 : : }
2270 tgl@sss.pgh.pa.us 513 [ + + ]: 289 : else if (strcmp(defel->defname, "join") == 0)
514 : : {
3197 heikki.linnakangas@i 515 : 233 : joinName = param;
516 : 233 : updateJoin = true;
517 : : }
177 tgl@sss.pgh.pa.us 518 [ + + ]:GNC 56 : else if (strcmp(defel->defname, "commutator") == 0)
519 : : {
520 : 12 : commutatorName = defGetQualifiedName(defel);
521 : : }
522 [ + + ]: 44 : else if (strcmp(defel->defname, "negator") == 0)
523 : : {
524 : 15 : negatorName = defGetQualifiedName(defel);
525 : : }
526 [ + + ]: 29 : else if (strcmp(defel->defname, "merges") == 0)
527 : : {
528 : 12 : canMerge = defGetBoolean(defel);
529 : 12 : updateMerges = true;
530 : : }
531 [ + + ]: 17 : else if (strcmp(defel->defname, "hashes") == 0)
532 : : {
533 : 14 : canHash = defGetBoolean(defel);
534 : 14 : updateHashes = true;
535 : : }
536 : :
537 : : /*
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.
540 : : */
2270 tgl@sss.pgh.pa.us 541 [ + - ]:CBC 3 : else if (strcmp(defel->defname, "leftarg") == 0 ||
542 [ + - ]: 3 : strcmp(defel->defname, "rightarg") == 0 ||
2069 peter_e@gmx.net 543 [ + - ]: 3 : strcmp(defel->defname, "function") == 0 ||
177 tgl@sss.pgh.pa.us 544 [ - + ]:GNC 3 : strcmp(defel->defname, "procedure") == 0)
545 : : {
3197 heikki.linnakangas@i 546 [ # # ]:LBC (12) : ereport(ERROR,
547 : : (errcode(ERRCODE_SYNTAX_ERROR),
548 : : errmsg("operator attribute \"%s\" cannot be changed",
549 : : defel->defname)));
550 : : }
551 : : else
3197 heikki.linnakangas@i 552 [ + - ]:CBC 3 : ereport(ERROR,
553 : : (errcode(ERRCODE_SYNTAX_ERROR),
554 : : errmsg("operator attribute \"%s\" not recognized",
555 : : defel->defname)));
556 : : }
557 : :
558 : : /* Check permissions. Must be owner. */
518 peter@eisentraut.org 559 [ + + ]: 298 : if (!object_ownercheck(OperatorRelationId, oprId, GetUserId()))
2325 peter_e@gmx.net 560 : 3 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
3197 heikki.linnakangas@i 561 : 3 : NameStr(oprForm->oprname));
562 : :
563 : : /*
564 : : * Look up OIDs for any parameters specified
565 : : */
566 [ + + ]: 295 : if (restrictionName)
567 : 227 : restrictionOid = ValidateRestrictionEstimator(restrictionName);
568 : : else
569 : 68 : restrictionOid = InvalidOid;
570 [ + + ]: 292 : if (joinName)
571 : 227 : joinOid = ValidateJoinEstimator(joinName);
572 : : else
573 : 65 : joinOid = InvalidOid;
574 : :
177 tgl@sss.pgh.pa.us 575 [ + + ]:GNC 289 : if (commutatorName)
576 : : {
577 : : /* commutator has reversed arg types */
578 : 12 : commutatorOid = ValidateOperatorReference(commutatorName,
579 : : oprForm->oprright,
580 : : oprForm->oprleft);
581 : :
582 : : /*
583 : : * We don't need to do anything extra for a self commutator as in
584 : : * OperatorCreate, since the operator surely exists already.
585 : : */
586 : : }
587 : : else
588 : 277 : commutatorOid = InvalidOid;
589 : :
590 [ + + ]: 289 : if (negatorName)
591 : : {
592 : 15 : negatorOid = ValidateOperatorReference(negatorName,
593 : : oprForm->oprleft,
594 : : oprForm->oprright);
595 : :
596 : : /* Must reject self-negation */
597 [ + + ]: 15 : if (negatorOid == oprForm->oid)
3197 heikki.linnakangas@i 598 [ + - ]:GBC 3 : ereport(ERROR,
599 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
600 : : errmsg("operator cannot be its own negator")));
601 : : }
602 : : else
603 : : {
177 tgl@sss.pgh.pa.us 604 :GNC 274 : negatorOid = InvalidOid;
605 : : }
606 : :
607 : : /*
608 : : * Check that we're not changing any attributes that might be depended on
609 : : * by plans, while allowing no-op updates.
610 : : */
611 [ + + + + ]: 286 : if (OidIsValid(commutatorOid) && OidIsValid(oprForm->oprcom) &&
612 [ + + ]: 6 : commutatorOid != oprForm->oprcom)
613 [ + - ]: 3 : ereport(ERROR,
614 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
615 : : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
616 : : "commutator")));
617 : :
618 [ + + + + ]: 283 : if (OidIsValid(negatorOid) && OidIsValid(oprForm->oprnegate) &&
619 [ + + ]: 6 : negatorOid != oprForm->oprnegate)
620 [ + - ]: 3 : ereport(ERROR,
621 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
622 : : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
623 : : "negator")));
624 : :
625 [ + + + + : 280 : if (updateMerges && oprForm->oprcanmerge && !canMerge)
+ + ]
626 [ + - ]: 3 : ereport(ERROR,
627 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
628 : : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
629 : : "merges")));
630 : :
631 [ + + + + : 277 : if (updateHashes && oprForm->oprcanhash && !canHash)
+ + ]
632 [ + - ]: 3 : ereport(ERROR,
633 : : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
634 : : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
635 : : "hashes")));
636 : :
637 : : /* Perform additional checks, like OperatorCreate does */
638 : 274 : OperatorValidateParams(oprForm->oprleft,
639 : : oprForm->oprright,
640 : : oprForm->oprresult,
641 : : OidIsValid(commutatorOid),
642 : : OidIsValid(negatorOid),
643 : : OidIsValid(restrictionOid),
644 : : OidIsValid(joinOid),
645 : : canMerge,
646 : : canHash);
647 : :
648 : : /* Update the tuple */
3197 heikki.linnakangas@i 649 [ + + ]:CBC 4384 : for (i = 0; i < Natts_pg_operator; ++i)
650 : : {
651 : 4110 : values[i] = (Datum) 0;
652 : 4110 : replaces[i] = false;
653 : 4110 : nulls[i] = false;
654 : : }
655 [ + + ]: 274 : if (updateRestriction)
656 : : {
657 : 230 : replaces[Anum_pg_operator_oprrest - 1] = true;
177 tgl@sss.pgh.pa.us 658 :GNC 230 : values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionOid);
659 : : }
3197 heikki.linnakangas@i 660 [ + + ]:CBC 274 : if (updateJoin)
661 : : {
662 : 230 : replaces[Anum_pg_operator_oprjoin - 1] = true;
177 tgl@sss.pgh.pa.us 663 :GNC 230 : values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid);
664 : : }
665 [ + + ]: 274 : if (OidIsValid(commutatorOid))
666 : : {
667 : 9 : replaces[Anum_pg_operator_oprcom - 1] = true;
668 : 9 : values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorOid);
669 : : }
670 [ + + ]: 274 : if (OidIsValid(negatorOid))
671 : : {
672 : 9 : replaces[Anum_pg_operator_oprnegate - 1] = true;
673 : 9 : values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorOid);
674 : : }
675 [ + + ]: 274 : if (updateMerges)
676 : : {
677 : 9 : replaces[Anum_pg_operator_oprcanmerge - 1] = true;
678 : 9 : values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge);
679 : : }
680 [ + + ]: 274 : if (updateHashes)
681 : : {
682 : 11 : replaces[Anum_pg_operator_oprcanhash - 1] = true;
683 : 11 : values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash);
684 : : }
685 : :
3197 heikki.linnakangas@i 686 :CBC 274 : tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
687 : : values, nulls, replaces);
688 : :
2630 alvherre@alvh.no-ip. 689 : 274 : CatalogTupleUpdate(catalog, &tup->t_self, tup);
690 : :
971 tgl@sss.pgh.pa.us 691 : 274 : address = makeOperatorDependencies(tup, false, true);
692 : :
177 tgl@sss.pgh.pa.us 693 [ + + + + ]:GNC 274 : if (OidIsValid(commutatorOid) || OidIsValid(negatorOid))
694 : 18 : OperatorUpd(oprId, commutatorOid, negatorOid, false);
695 : :
3027 tgl@sss.pgh.pa.us 696 [ - + ]:CBC 268 : InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
697 : :
1910 andres@anarazel.de 698 : 268 : table_close(catalog, NoLock);
699 : :
3197 heikki.linnakangas@i 700 : 268 : return address;
701 : : }
|