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