Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * operatorcmds.c
4 : *
5 : * Routines for operator manipulation commands
6 : *
7 : * Portions Copyright (c) 1996-2017, 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 procedures
25 : * "create type":
26 : * type
27 : * "create operator":
28 : * operators
29 : *
30 : * Most of the parse-tree manipulation routines are defined in
31 : * commands/manip.c.
32 : *
33 : *-------------------------------------------------------------------------
34 : */
35 : #include "postgres.h"
36 :
37 : #include "access/heapam.h"
38 : #include "access/htup_details.h"
39 : #include "catalog/dependency.h"
40 : #include "catalog/indexing.h"
41 : #include "catalog/objectaccess.h"
42 : #include "catalog/pg_operator.h"
43 : #include "catalog/pg_operator_fn.h"
44 : #include "catalog/pg_type.h"
45 : #include "commands/alter.h"
46 : #include "commands/defrem.h"
47 : #include "miscadmin.h"
48 : #include "parser/parse_func.h"
49 : #include "parser/parse_oper.h"
50 : #include "parser/parse_type.h"
51 : #include "utils/builtins.h"
52 : #include "utils/lsyscache.h"
53 : #include "utils/rel.h"
54 : #include "utils/syscache.h"
55 :
56 : static Oid ValidateRestrictionEstimator(List *restrictionName);
57 : static Oid ValidateJoinEstimator(List *joinName);
58 :
59 : /*
60 : * DefineOperator
61 : * this function extracts all the information from the
62 : * parameter list generated by the parser and then has
63 : * OperatorCreate() do all the actual work.
64 : *
65 : * 'parameters' is a list of DefElem
66 : */
67 : ObjectAddress
68 38 : DefineOperator(List *names, List *parameters)
69 : {
70 : char *oprName;
71 : Oid oprNamespace;
72 : AclResult aclresult;
73 38 : bool canMerge = false; /* operator merges */
74 38 : bool canHash = false; /* operator hashes */
75 38 : List *functionName = NIL; /* function for operator */
76 38 : TypeName *typeName1 = NULL; /* first type name */
77 38 : TypeName *typeName2 = NULL; /* second type name */
78 38 : Oid typeId1 = InvalidOid; /* types converted to OID */
79 38 : Oid typeId2 = InvalidOid;
80 : Oid rettype;
81 38 : List *commutatorName = NIL; /* optional commutator operator name */
82 38 : List *negatorName = NIL; /* optional negator operator name */
83 38 : List *restrictionName = NIL; /* optional restrict. sel. procedure */
84 38 : List *joinName = NIL; /* optional join sel. procedure */
85 : Oid functionOid; /* functions converted to OID */
86 : Oid restrictionOid;
87 : Oid joinOid;
88 : Oid typeId[2]; /* to hold left and right arg */
89 : int nargs;
90 : ListCell *pl;
91 :
92 : /* Convert list of names to a name and namespace */
93 38 : oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
94 :
95 : /* Check we have creation rights in target namespace */
96 38 : aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
97 38 : if (aclresult != ACLCHECK_OK)
98 1 : aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
99 1 : get_namespace_name(oprNamespace));
100 :
101 : /*
102 : * loop over the definition list and extract the information we need.
103 : */
104 173 : foreach(pl, parameters)
105 : {
106 138 : DefElem *defel = (DefElem *) lfirst(pl);
107 :
108 138 : if (pg_strcasecmp(defel->defname, "leftarg") == 0)
109 : {
110 32 : typeName1 = defGetTypeName(defel);
111 32 : if (typeName1->setof)
112 1 : ereport(ERROR,
113 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
114 : errmsg("SETOF type not allowed for operator argument")));
115 : }
116 106 : else if (pg_strcasecmp(defel->defname, "rightarg") == 0)
117 : {
118 31 : typeName2 = defGetTypeName(defel);
119 31 : if (typeName2->setof)
120 1 : ereport(ERROR,
121 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
122 : errmsg("SETOF type not allowed for operator argument")));
123 : }
124 75 : else if (pg_strcasecmp(defel->defname, "procedure") == 0)
125 34 : functionName = defGetQualifiedName(defel);
126 41 : else if (pg_strcasecmp(defel->defname, "commutator") == 0)
127 9 : commutatorName = defGetQualifiedName(defel);
128 32 : else if (pg_strcasecmp(defel->defname, "negator") == 0)
129 5 : negatorName = defGetQualifiedName(defel);
130 27 : else if (pg_strcasecmp(defel->defname, "restrict") == 0)
131 8 : restrictionName = defGetQualifiedName(defel);
132 19 : else if (pg_strcasecmp(defel->defname, "join") == 0)
133 6 : joinName = defGetQualifiedName(defel);
134 13 : else if (pg_strcasecmp(defel->defname, "hashes") == 0)
135 2 : canHash = defGetBoolean(defel);
136 11 : else if (pg_strcasecmp(defel->defname, "merges") == 0)
137 6 : canMerge = defGetBoolean(defel);
138 : /* These obsolete options are taken as meaning canMerge */
139 5 : else if (pg_strcasecmp(defel->defname, "sort1") == 0)
140 1 : canMerge = true;
141 4 : else if (pg_strcasecmp(defel->defname, "sort2") == 0)
142 1 : canMerge = true;
143 3 : else if (pg_strcasecmp(defel->defname, "ltcmp") == 0)
144 1 : canMerge = true;
145 2 : else if (pg_strcasecmp(defel->defname, "gtcmp") == 0)
146 1 : canMerge = true;
147 : else
148 : {
149 : /* WARNING, not ERROR, for historical backwards-compatibility */
150 1 : ereport(WARNING,
151 : (errcode(ERRCODE_SYNTAX_ERROR),
152 : errmsg("operator attribute \"%s\" not recognized",
153 : defel->defname)));
154 : }
155 : }
156 :
157 : /*
158 : * make sure we have our required definitions
159 : */
160 35 : if (functionName == NIL)
161 1 : ereport(ERROR,
162 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
163 : errmsg("operator procedure must be specified")));
164 :
165 : /* Transform type names to type OIDs */
166 34 : if (typeName1)
167 30 : typeId1 = typenameTypeId(NULL, typeName1);
168 34 : if (typeName2)
169 30 : typeId2 = typenameTypeId(NULL, typeName2);
170 :
171 34 : if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
172 1 : ereport(ERROR,
173 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
174 : errmsg("at least one of leftarg or rightarg must be specified")));
175 :
176 33 : if (typeName1)
177 : {
178 30 : aclresult = pg_type_aclcheck(typeId1, GetUserId(), ACL_USAGE);
179 30 : if (aclresult != ACLCHECK_OK)
180 2 : aclcheck_error_type(aclresult, typeId1);
181 : }
182 :
183 31 : if (typeName2)
184 : {
185 28 : aclresult = pg_type_aclcheck(typeId2, GetUserId(), ACL_USAGE);
186 28 : if (aclresult != ACLCHECK_OK)
187 1 : aclcheck_error_type(aclresult, typeId2);
188 : }
189 :
190 : /*
191 : * Look up the operator's underlying function.
192 : */
193 30 : if (!OidIsValid(typeId1))
194 : {
195 3 : typeId[0] = typeId2;
196 3 : nargs = 1;
197 : }
198 27 : else if (!OidIsValid(typeId2))
199 : {
200 3 : typeId[0] = typeId1;
201 3 : nargs = 1;
202 : }
203 : else
204 : {
205 24 : typeId[0] = typeId1;
206 24 : typeId[1] = typeId2;
207 24 : nargs = 2;
208 : }
209 30 : functionOid = LookupFuncName(functionName, nargs, typeId, false);
210 :
211 : /*
212 : * We require EXECUTE rights for the function. This isn't strictly
213 : * necessary, since EXECUTE will be checked at any attempted use of the
214 : * operator, but it seems like a good idea anyway.
215 : */
216 30 : aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE);
217 30 : if (aclresult != ACLCHECK_OK)
218 1 : aclcheck_error(aclresult, ACL_KIND_PROC,
219 1 : NameListToString(functionName));
220 :
221 29 : rettype = get_func_rettype(functionOid);
222 29 : aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE);
223 29 : if (aclresult != ACLCHECK_OK)
224 1 : aclcheck_error_type(aclresult, rettype);
225 :
226 : /*
227 : * Look up restriction and join estimators if specified
228 : */
229 28 : if (restrictionName)
230 8 : restrictionOid = ValidateRestrictionEstimator(restrictionName);
231 : else
232 20 : restrictionOid = InvalidOid;
233 28 : if (joinName)
234 6 : joinOid = ValidateJoinEstimator(joinName);
235 : else
236 22 : joinOid = InvalidOid;
237 :
238 : /*
239 : * now have OperatorCreate do all the work..
240 : */
241 28 : return
242 28 : OperatorCreate(oprName, /* operator name */
243 : oprNamespace, /* namespace */
244 : typeId1, /* left type id */
245 : typeId2, /* right type id */
246 : functionOid, /* function for operator */
247 : commutatorName, /* optional commutator operator name */
248 : negatorName, /* optional negator operator name */
249 : restrictionOid, /* optional restrict. sel. procedure */
250 : joinOid, /* optional join sel. procedure name */
251 : canMerge, /* operator merges */
252 : canHash); /* operator hashes */
253 : }
254 :
255 : /*
256 : * Look up a restriction estimator function ny name, and verify that it has
257 : * the correct signature and we have the permissions to attach it to an
258 : * operator.
259 : */
260 : static Oid
261 11 : ValidateRestrictionEstimator(List *restrictionName)
262 : {
263 : Oid typeId[4];
264 : Oid restrictionOid;
265 : AclResult aclresult;
266 :
267 11 : typeId[0] = INTERNALOID; /* PlannerInfo */
268 11 : typeId[1] = OIDOID; /* operator OID */
269 11 : typeId[2] = INTERNALOID; /* args list */
270 11 : typeId[3] = INT4OID; /* varRelid */
271 :
272 11 : restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
273 :
274 : /* estimators must return float8 */
275 10 : if (get_func_rettype(restrictionOid) != FLOAT8OID)
276 0 : ereport(ERROR,
277 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
278 : errmsg("restriction estimator function %s must return type %s",
279 : NameListToString(restrictionName), "float8")));
280 :
281 : /* Require EXECUTE rights for the estimator */
282 10 : aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE);
283 10 : if (aclresult != ACLCHECK_OK)
284 0 : aclcheck_error(aclresult, ACL_KIND_PROC,
285 0 : NameListToString(restrictionName));
286 :
287 10 : return restrictionOid;
288 : }
289 :
290 : /*
291 : * Look up a join estimator function ny name, and verify that it has the
292 : * correct signature and we have the permissions to attach it to an
293 : * operator.
294 : */
295 : static Oid
296 9 : ValidateJoinEstimator(List *joinName)
297 : {
298 : Oid typeId[5];
299 : Oid joinOid;
300 : AclResult aclresult;
301 :
302 9 : typeId[0] = INTERNALOID; /* PlannerInfo */
303 9 : typeId[1] = OIDOID; /* operator OID */
304 9 : typeId[2] = INTERNALOID; /* args list */
305 9 : typeId[3] = INT2OID; /* jointype */
306 9 : typeId[4] = INTERNALOID; /* SpecialJoinInfo */
307 :
308 : /*
309 : * As of Postgres 8.4, the preferred signature for join estimators has 5
310 : * arguments, but we still allow the old 4-argument form. Try the
311 : * preferred form first.
312 : */
313 9 : joinOid = LookupFuncName(joinName, 5, typeId, true);
314 9 : if (!OidIsValid(joinOid))
315 1 : joinOid = LookupFuncName(joinName, 4, typeId, true);
316 : /* If not found, reference the 5-argument signature in error msg */
317 9 : if (!OidIsValid(joinOid))
318 1 : joinOid = LookupFuncName(joinName, 5, typeId, false);
319 :
320 : /* estimators must return float8 */
321 8 : if (get_func_rettype(joinOid) != FLOAT8OID)
322 0 : ereport(ERROR,
323 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
324 : errmsg("join estimator function %s must return type %s",
325 : NameListToString(joinName), "float8")));
326 :
327 : /* Require EXECUTE rights for the estimator */
328 8 : aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
329 8 : if (aclresult != ACLCHECK_OK)
330 0 : aclcheck_error(aclresult, ACL_KIND_PROC,
331 0 : NameListToString(joinName));
332 :
333 8 : return joinOid;
334 : }
335 :
336 : /*
337 : * Guts of operator deletion.
338 : */
339 : void
340 13 : RemoveOperatorById(Oid operOid)
341 : {
342 : Relation relation;
343 : HeapTuple tup;
344 : Form_pg_operator op;
345 :
346 13 : relation = heap_open(OperatorRelationId, RowExclusiveLock);
347 :
348 13 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
349 13 : if (!HeapTupleIsValid(tup)) /* should not happen */
350 0 : elog(ERROR, "cache lookup failed for operator %u", operOid);
351 13 : op = (Form_pg_operator) GETSTRUCT(tup);
352 :
353 : /*
354 : * Reset links from commutator and negator, if any. In case of a
355 : * self-commutator or self-negator, this means we have to re-fetch the
356 : * updated tuple. (We could optimize away updates on the tuple we're
357 : * about to drop, but it doesn't seem worth convoluting the logic for.)
358 : */
359 13 : if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
360 : {
361 4 : OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
362 4 : if (operOid == op->oprcom || operOid == op->oprnegate)
363 : {
364 3 : ReleaseSysCache(tup);
365 3 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
366 3 : if (!HeapTupleIsValid(tup)) /* should not happen */
367 0 : elog(ERROR, "cache lookup failed for operator %u", operOid);
368 : }
369 : }
370 :
371 13 : CatalogTupleDelete(relation, &tup->t_self);
372 :
373 13 : ReleaseSysCache(tup);
374 :
375 13 : heap_close(relation, RowExclusiveLock);
376 13 : }
377 :
378 : /*
379 : * AlterOperator
380 : * routine implementing ALTER OPERATOR <operator> SET (option = ...).
381 : *
382 : * Currently, only RESTRICT and JOIN estimator functions can be changed.
383 : */
384 : ObjectAddress
385 13 : AlterOperator(AlterOperatorStmt *stmt)
386 : {
387 : ObjectAddress address;
388 : Oid oprId;
389 : Relation catalog;
390 : HeapTuple tup;
391 : Form_pg_operator oprForm;
392 : int i;
393 : ListCell *pl;
394 : Datum values[Natts_pg_operator];
395 : bool nulls[Natts_pg_operator];
396 : bool replaces[Natts_pg_operator];
397 13 : List *restrictionName = NIL; /* optional restrict. sel. procedure */
398 13 : bool updateRestriction = false;
399 : Oid restrictionOid;
400 13 : List *joinName = NIL; /* optional join sel. procedure */
401 13 : bool updateJoin = false;
402 : Oid joinOid;
403 :
404 : /* Look up the operator */
405 13 : oprId = LookupOperWithArgs(stmt->opername, false);
406 13 : catalog = heap_open(OperatorRelationId, RowExclusiveLock);
407 13 : tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
408 13 : if (tup == NULL)
409 0 : elog(ERROR, "cache lookup failed for operator %u", oprId);
410 13 : oprForm = (Form_pg_operator) GETSTRUCT(tup);
411 :
412 : /* Process options */
413 24 : foreach(pl, stmt->options)
414 : {
415 15 : DefElem *defel = (DefElem *) lfirst(pl);
416 : List *param;
417 :
418 15 : if (defel->arg == NULL)
419 5 : param = NIL; /* NONE, removes the function */
420 : else
421 10 : param = defGetQualifiedName(defel);
422 :
423 15 : if (pg_strcasecmp(defel->defname, "restrict") == 0)
424 : {
425 6 : restrictionName = param;
426 6 : updateRestriction = true;
427 : }
428 9 : else if (pg_strcasecmp(defel->defname, "join") == 0)
429 : {
430 5 : joinName = param;
431 5 : updateJoin = true;
432 : }
433 :
434 : /*
435 : * The rest of the options that CREATE accepts cannot be changed.
436 : * Check for them so that we can give a meaningful error message.
437 : */
438 8 : else if (pg_strcasecmp(defel->defname, "leftarg") == 0 ||
439 8 : pg_strcasecmp(defel->defname, "rightarg") == 0 ||
440 8 : pg_strcasecmp(defel->defname, "procedure") == 0 ||
441 6 : pg_strcasecmp(defel->defname, "commutator") == 0 ||
442 2 : pg_strcasecmp(defel->defname, "negator") == 0 ||
443 0 : pg_strcasecmp(defel->defname, "hashes") == 0 ||
444 0 : pg_strcasecmp(defel->defname, "merges") == 0)
445 : {
446 4 : ereport(ERROR,
447 : (errcode(ERRCODE_SYNTAX_ERROR),
448 : errmsg("operator attribute \"%s\" cannot be changed",
449 : defel->defname)));
450 : }
451 : else
452 0 : ereport(ERROR,
453 : (errcode(ERRCODE_SYNTAX_ERROR),
454 : errmsg("operator attribute \"%s\" not recognized",
455 : defel->defname)));
456 : }
457 :
458 : /* Check permissions. Must be owner. */
459 9 : if (!pg_oper_ownercheck(oprId, GetUserId()))
460 1 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
461 1 : NameStr(oprForm->oprname));
462 :
463 : /*
464 : * Look up restriction and join estimators if specified
465 : */
466 8 : if (restrictionName)
467 3 : restrictionOid = ValidateRestrictionEstimator(restrictionName);
468 : else
469 5 : restrictionOid = InvalidOid;
470 7 : if (joinName)
471 3 : joinOid = ValidateJoinEstimator(joinName);
472 : else
473 4 : joinOid = InvalidOid;
474 :
475 : /* Perform additional checks, like OperatorCreate does */
476 6 : if (!(OidIsValid(oprForm->oprleft) && OidIsValid(oprForm->oprright)))
477 : {
478 : /* If it's not a binary op, these things mustn't be set: */
479 0 : if (OidIsValid(joinOid))
480 0 : ereport(ERROR,
481 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
482 : errmsg("only binary operators can have join selectivity")));
483 : }
484 :
485 6 : if (oprForm->oprresult != BOOLOID)
486 : {
487 0 : if (OidIsValid(restrictionOid))
488 0 : ereport(ERROR,
489 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
490 : errmsg("only boolean operators can have restriction selectivity")));
491 0 : if (OidIsValid(joinOid))
492 0 : ereport(ERROR,
493 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
494 : errmsg("only boolean operators can have join selectivity")));
495 : }
496 :
497 : /* Update the tuple */
498 90 : for (i = 0; i < Natts_pg_operator; ++i)
499 : {
500 84 : values[i] = (Datum) 0;
501 84 : replaces[i] = false;
502 84 : nulls[i] = false;
503 : }
504 6 : if (updateRestriction)
505 : {
506 4 : replaces[Anum_pg_operator_oprrest - 1] = true;
507 4 : values[Anum_pg_operator_oprrest - 1] = restrictionOid;
508 : }
509 6 : if (updateJoin)
510 : {
511 4 : replaces[Anum_pg_operator_oprjoin - 1] = true;
512 4 : values[Anum_pg_operator_oprjoin - 1] = joinOid;
513 : }
514 :
515 6 : tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
516 : values, nulls, replaces);
517 :
518 6 : CatalogTupleUpdate(catalog, &tup->t_self, tup);
519 :
520 6 : address = makeOperatorDependencies(tup, true);
521 :
522 6 : InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
523 :
524 6 : heap_close(catalog, NoLock);
525 :
526 6 : return address;
527 : }
|