PostgreSQL Source Code  git master
operatorcmds.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * operatorcmds.c
4  *
5  * Routines for operator manipulation commands
6  *
7  * Portions Copyright (c) 1996-2020, 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/dependency.h"
37 #include "catalog/indexing.h"
38 #include "catalog/objectaccess.h"
39 #include "catalog/pg_operator.h"
40 #include "catalog/pg_type.h"
41 #include "commands/alter.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/builtins.h"
49 #include "utils/lsyscache.h"
50 #include "utils/rel.h"
51 #include "utils/syscache.h"
52 
53 static Oid ValidateRestrictionEstimator(List *restrictionName);
54 static Oid ValidateJoinEstimator(List *joinName);
55 
56 /*
57  * DefineOperator
58  * this function extracts all the information from the
59  * parameter list generated by the parser and then has
60  * OperatorCreate() do all the actual work.
61  *
62  * 'parameters' is a list of DefElem
63  */
65 DefineOperator(List *names, List *parameters)
66 {
67  char *oprName;
68  Oid oprNamespace;
69  AclResult aclresult;
70  bool canMerge = false; /* operator merges */
71  bool canHash = false; /* operator hashes */
72  List *functionName = NIL; /* function for operator */
73  TypeName *typeName1 = NULL; /* first type name */
74  TypeName *typeName2 = NULL; /* second type name */
75  Oid typeId1 = InvalidOid; /* types converted to OID */
76  Oid typeId2 = InvalidOid;
77  Oid rettype;
78  List *commutatorName = NIL; /* optional commutator operator name */
79  List *negatorName = NIL; /* optional negator operator name */
80  List *restrictionName = NIL; /* optional restrict. sel. function */
81  List *joinName = NIL; /* optional join sel. function */
82  Oid functionOid; /* functions converted to OID */
83  Oid restrictionOid;
84  Oid joinOid;
85  Oid typeId[2]; /* to hold left and right arg */
86  int nargs;
87  ListCell *pl;
88 
89  /* Convert list of names to a name and namespace */
90  oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
91 
92  /* Check we have creation rights in target namespace */
93  aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
94  if (aclresult != ACLCHECK_OK)
95  aclcheck_error(aclresult, OBJECT_SCHEMA,
96  get_namespace_name(oprNamespace));
97 
98  /*
99  * loop over the definition list and extract the information we need.
100  */
101  foreach(pl, parameters)
102  {
103  DefElem *defel = (DefElem *) lfirst(pl);
104 
105  if (strcmp(defel->defname, "leftarg") == 0)
106  {
107  typeName1 = defGetTypeName(defel);
108  if (typeName1->setof)
109  ereport(ERROR,
110  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
111  errmsg("SETOF type not allowed for operator argument")));
112  }
113  else if (strcmp(defel->defname, "rightarg") == 0)
114  {
115  typeName2 = defGetTypeName(defel);
116  if (typeName2->setof)
117  ereport(ERROR,
118  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
119  errmsg("SETOF type not allowed for operator argument")));
120  }
121  /* "function" and "procedure" are equivalent here */
122  else if (strcmp(defel->defname, "function") == 0)
123  functionName = defGetQualifiedName(defel);
124  else if (strcmp(defel->defname, "procedure") == 0)
125  functionName = defGetQualifiedName(defel);
126  else if (strcmp(defel->defname, "commutator") == 0)
127  commutatorName = defGetQualifiedName(defel);
128  else if (strcmp(defel->defname, "negator") == 0)
129  negatorName = defGetQualifiedName(defel);
130  else if (strcmp(defel->defname, "restrict") == 0)
131  restrictionName = defGetQualifiedName(defel);
132  else if (strcmp(defel->defname, "join") == 0)
133  joinName = defGetQualifiedName(defel);
134  else if (strcmp(defel->defname, "hashes") == 0)
135  canHash = defGetBoolean(defel);
136  else if (strcmp(defel->defname, "merges") == 0)
137  canMerge = defGetBoolean(defel);
138  /* These obsolete options are taken as meaning canMerge */
139  else if (strcmp(defel->defname, "sort1") == 0)
140  canMerge = true;
141  else if (strcmp(defel->defname, "sort2") == 0)
142  canMerge = true;
143  else if (strcmp(defel->defname, "ltcmp") == 0)
144  canMerge = true;
145  else if (strcmp(defel->defname, "gtcmp") == 0)
146  canMerge = true;
147  else
148  {
149  /* WARNING, not ERROR, for historical backwards-compatibility */
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  if (functionName == NIL)
161  ereport(ERROR,
162  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
163  errmsg("operator function must be specified")));
164 
165  /* Transform type names to type OIDs */
166  if (typeName1)
167  typeId1 = typenameTypeId(NULL, typeName1);
168  if (typeName2)
169  typeId2 = typenameTypeId(NULL, typeName2);
170 
171  if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
172  ereport(ERROR,
173  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
174  errmsg("at least one of leftarg or rightarg must be specified")));
175 
176  if (typeName1)
177  {
178  aclresult = pg_type_aclcheck(typeId1, GetUserId(), ACL_USAGE);
179  if (aclresult != ACLCHECK_OK)
180  aclcheck_error_type(aclresult, typeId1);
181  }
182 
183  if (typeName2)
184  {
185  aclresult = pg_type_aclcheck(typeId2, GetUserId(), ACL_USAGE);
186  if (aclresult != ACLCHECK_OK)
187  aclcheck_error_type(aclresult, typeId2);
188  }
189 
190  /*
191  * Look up the operator's underlying function.
192  */
193  if (!OidIsValid(typeId1))
194  {
195  typeId[0] = typeId2;
196  nargs = 1;
197  }
198  else if (!OidIsValid(typeId2))
199  {
200  typeId[0] = typeId1;
201  nargs = 1;
202  }
203  else
204  {
205  typeId[0] = typeId1;
206  typeId[1] = typeId2;
207  nargs = 2;
208  }
209  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  aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE);
217  if (aclresult != ACLCHECK_OK)
218  aclcheck_error(aclresult, OBJECT_FUNCTION,
219  NameListToString(functionName));
220 
221  rettype = get_func_rettype(functionOid);
222  aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE);
223  if (aclresult != ACLCHECK_OK)
224  aclcheck_error_type(aclresult, rettype);
225 
226  /*
227  * Look up restriction and join estimators if specified
228  */
229  if (restrictionName)
230  restrictionOid = ValidateRestrictionEstimator(restrictionName);
231  else
232  restrictionOid = InvalidOid;
233  if (joinName)
234  joinOid = ValidateJoinEstimator(joinName);
235  else
236  joinOid = InvalidOid;
237 
238  /*
239  * now have OperatorCreate do all the work..
240  */
241  return
242  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. function */
250  joinOid, /* optional join sel. function 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
262 {
263  Oid typeId[4];
264  Oid restrictionOid;
265  AclResult aclresult;
266 
267  typeId[0] = INTERNALOID; /* PlannerInfo */
268  typeId[1] = OIDOID; /* operator OID */
269  typeId[2] = INTERNALOID; /* args list */
270  typeId[3] = INT4OID; /* varRelid */
271 
272  restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
273 
274  /* estimators must return float8 */
275  if (get_func_rettype(restrictionOid) != FLOAT8OID)
276  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  aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE);
283  if (aclresult != ACLCHECK_OK)
284  aclcheck_error(aclresult, OBJECT_FUNCTION,
285  NameListToString(restrictionName));
286 
287  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
297 {
298  Oid typeId[5];
299  Oid joinOid;
300  AclResult aclresult;
301 
302  typeId[0] = INTERNALOID; /* PlannerInfo */
303  typeId[1] = OIDOID; /* operator OID */
304  typeId[2] = INTERNALOID; /* args list */
305  typeId[3] = INT2OID; /* jointype */
306  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  joinOid = LookupFuncName(joinName, 5, typeId, true);
314  if (!OidIsValid(joinOid))
315  joinOid = LookupFuncName(joinName, 4, typeId, true);
316  /* If not found, reference the 5-argument signature in error msg */
317  if (!OidIsValid(joinOid))
318  joinOid = LookupFuncName(joinName, 5, typeId, false);
319 
320  /* estimators must return float8 */
321  if (get_func_rettype(joinOid) != FLOAT8OID)
322  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  aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
329  if (aclresult != ACLCHECK_OK)
330  aclcheck_error(aclresult, OBJECT_FUNCTION,
331  NameListToString(joinName));
332 
333  return joinOid;
334 }
335 
336 /*
337  * Guts of operator deletion.
338  */
339 void
341 {
342  Relation relation;
343  HeapTuple tup;
344  Form_pg_operator op;
345 
346  relation = table_open(OperatorRelationId, RowExclusiveLock);
347 
348  tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
349  if (!HeapTupleIsValid(tup)) /* should not happen */
350  elog(ERROR, "cache lookup failed for operator %u", operOid);
351  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  if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
360  {
361  OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
362  if (operOid == op->oprcom || operOid == op->oprnegate)
363  {
364  ReleaseSysCache(tup);
365  tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
366  if (!HeapTupleIsValid(tup)) /* should not happen */
367  elog(ERROR, "cache lookup failed for operator %u", operOid);
368  }
369  }
370 
371  CatalogTupleDelete(relation, &tup->t_self);
372 
373  ReleaseSysCache(tup);
374 
375  table_close(relation, RowExclusiveLock);
376 }
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  */
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  List *restrictionName = NIL; /* optional restrict. sel. function */
398  bool updateRestriction = false;
399  Oid restrictionOid;
400  List *joinName = NIL; /* optional join sel. function */
401  bool updateJoin = false;
402  Oid joinOid;
403 
404  /* Look up the operator */
405  oprId = LookupOperWithArgs(stmt->opername, false);
406  catalog = table_open(OperatorRelationId, RowExclusiveLock);
408  if (!HeapTupleIsValid(tup))
409  elog(ERROR, "cache lookup failed for operator %u", oprId);
410  oprForm = (Form_pg_operator) GETSTRUCT(tup);
411 
412  /* Process options */
413  foreach(pl, stmt->options)
414  {
415  DefElem *defel = (DefElem *) lfirst(pl);
416  List *param;
417 
418  if (defel->arg == NULL)
419  param = NIL; /* NONE, removes the function */
420  else
421  param = defGetQualifiedName(defel);
422 
423  if (strcmp(defel->defname, "restrict") == 0)
424  {
425  restrictionName = param;
426  updateRestriction = true;
427  }
428  else if (strcmp(defel->defname, "join") == 0)
429  {
430  joinName = param;
431  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  else if (strcmp(defel->defname, "leftarg") == 0 ||
439  strcmp(defel->defname, "rightarg") == 0 ||
440  strcmp(defel->defname, "function") == 0 ||
441  strcmp(defel->defname, "procedure") == 0 ||
442  strcmp(defel->defname, "commutator") == 0 ||
443  strcmp(defel->defname, "negator") == 0 ||
444  strcmp(defel->defname, "hashes") == 0 ||
445  strcmp(defel->defname, "merges") == 0)
446  {
447  ereport(ERROR,
448  (errcode(ERRCODE_SYNTAX_ERROR),
449  errmsg("operator attribute \"%s\" cannot be changed",
450  defel->defname)));
451  }
452  else
453  ereport(ERROR,
454  (errcode(ERRCODE_SYNTAX_ERROR),
455  errmsg("operator attribute \"%s\" not recognized",
456  defel->defname)));
457  }
458 
459  /* Check permissions. Must be owner. */
460  if (!pg_oper_ownercheck(oprId, GetUserId()))
462  NameStr(oprForm->oprname));
463 
464  /*
465  * Look up restriction and join estimators if specified
466  */
467  if (restrictionName)
468  restrictionOid = ValidateRestrictionEstimator(restrictionName);
469  else
470  restrictionOid = InvalidOid;
471  if (joinName)
472  joinOid = ValidateJoinEstimator(joinName);
473  else
474  joinOid = InvalidOid;
475 
476  /* Perform additional checks, like OperatorCreate does */
477  if (!(OidIsValid(oprForm->oprleft) && OidIsValid(oprForm->oprright)))
478  {
479  /* If it's not a binary op, these things mustn't be set: */
480  if (OidIsValid(joinOid))
481  ereport(ERROR,
482  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
483  errmsg("only binary operators can have join selectivity")));
484  }
485 
486  if (oprForm->oprresult != BOOLOID)
487  {
488  if (OidIsValid(restrictionOid))
489  ereport(ERROR,
490  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
491  errmsg("only boolean operators can have restriction selectivity")));
492  if (OidIsValid(joinOid))
493  ereport(ERROR,
494  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
495  errmsg("only boolean operators can have join selectivity")));
496  }
497 
498  /* Update the tuple */
499  for (i = 0; i < Natts_pg_operator; ++i)
500  {
501  values[i] = (Datum) 0;
502  replaces[i] = false;
503  nulls[i] = false;
504  }
505  if (updateRestriction)
506  {
507  replaces[Anum_pg_operator_oprrest - 1] = true;
508  values[Anum_pg_operator_oprrest - 1] = restrictionOid;
509  }
510  if (updateJoin)
511  {
512  replaces[Anum_pg_operator_oprjoin - 1] = true;
513  values[Anum_pg_operator_oprjoin - 1] = joinOid;
514  }
515 
516  tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
517  values, nulls, replaces);
518 
519  CatalogTupleUpdate(catalog, &tup->t_self, tup);
520 
521  address = makeOperatorDependencies(tup, true);
522 
523  InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
524 
525  table_close(catalog, NoLock);
526 
527  return address;
528 }
Oid LookupOperWithArgs(ObjectWithArgs *oper, bool noError)
Definition: parse_oper.c:140
#define NIL
Definition: pg_list.h:65
ObjectAddress DefineOperator(List *names, List *parameters)
Definition: operatorcmds.c:65
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
ObjectAddress OperatorCreate(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId, Oid procedureId, List *commutatorName, List *negatorName, Oid restrictionId, Oid joinId, bool canMerge, bool canHash)
Definition: pg_operator.c:327
#define RelationGetDescr(relation)
Definition: rel.h:461
Oid GetUserId(void)
Definition: miscinit.c:439
ObjectAddress makeOperatorDependencies(HeapTuple tuple, bool isUpdate)
Definition: pg_operator.c:773
static Oid ValidateJoinEstimator(List *joinName)
Definition: operatorcmds.c:296
Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p)
Definition: namespace.c:2995
bool pg_oper_ownercheck(Oid oper_oid, Oid roleid)
Definition: aclchk.c:4805
int errcode(int sqlerrcode)
Definition: elog.c:610
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:269
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:644
AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4691
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition: aclchk.c:3665
Oid get_func_rettype(Oid funcid)
Definition: lsyscache.c:1483
void RemoveOperatorById(Oid operOid)
Definition: operatorcmds.c:340
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3352
bool defGetBoolean(DefElem *def)
Definition: define.c:111
TypeName * defGetTypeName(DefElem *def)
Definition: define.c:255
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
#define ACL_CREATE
Definition: parsenodes.h:84
ObjectAddress AlterOperator(AlterOperatorStmt *stmt)
Definition: operatorcmds.c:385
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
Definition: parse_func.c:2103
bool setof
Definition: parsenodes.h:211
ItemPointerData t_self
Definition: htup.h:65
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3120
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
#define ACL_USAGE
Definition: parsenodes.h:82
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:175
static Oid ValidateRestrictionEstimator(List *restrictionName)
Definition: operatorcmds.c:261
Node * arg
Definition: parsenodes.h:731
#define WARNING
Definition: elog.h:40
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
char * NameListToString(List *names)
Definition: namespace.c:3102
ObjectWithArgs * opername
Definition: parsenodes.h:2969
AclResult
Definition: acl.h:177
uintptr_t Datum
Definition: postgres.h:367
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
#define InvalidOid
Definition: postgres_ext.h:36
void OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
Definition: pg_operator.c:654
#define ereport(elevel,...)
Definition: elog.h:144
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:190
List * defGetQualifiedName(DefElem *def)
Definition: define.c:223
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:224
FormData_pg_operator * Form_pg_operator
Definition: pg_operator.h:83
static Datum values[MAXATTR]
Definition: bootstrap.c:167
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:174
int errmsg(const char *fmt,...)
Definition: elog.c:824
#define ACL_EXECUTE
Definition: parsenodes.h:81
#define elog(elevel,...)
Definition: elog.h:214
AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4653
int i
#define NameStr(name)
Definition: c.h:615
char * defname
Definition: parsenodes.h:730
AclResult pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4741
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, bool *replIsnull, bool *doReplace)
Definition: heaptuple.c:1113
Definition: pg_list.h:50
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
Definition: parse_type.c:291