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