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
55static Oid ValidateRestrictionEstimator(List *restrictionName);
56static 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 */
66ObjectAddress
67DefineOperator(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 */
262static Oid
263ValidateRestrictionEstimator(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 */
297static Oid
298ValidateJoinEstimator(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 */
341void
342RemoveOperatorById(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 */
386ObjectAddress
387AlterOperator(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