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
DefineOperator(List * names,List * parameters)68 DefineOperator(List *names, List *parameters)
69 {
70 	char	   *oprName;
71 	Oid			oprNamespace;
72 	AclResult	aclresult;
73 	bool		canMerge = false;	/* operator merges */
74 	bool		canHash = false;	/* operator hashes */
75 	List	   *functionName = NIL; /* function for operator */
76 	TypeName   *typeName1 = NULL;	/* first type name */
77 	TypeName   *typeName2 = NULL;	/* second type name */
78 	Oid			typeId1 = InvalidOid;	/* types converted to OID */
79 	Oid			typeId2 = InvalidOid;
80 	Oid			rettype;
81 	List	   *commutatorName = NIL;	/* optional commutator operator name */
82 	List	   *negatorName = NIL;	/* optional negator operator name */
83 	List	   *restrictionName = NIL;	/* optional restrict. sel. procedure */
84 	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 	oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
94 
95 	/* Check we have creation rights in target namespace */
96 	aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
97 	if (aclresult != ACLCHECK_OK)
98 		aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
99 					   get_namespace_name(oprNamespace));
100 
101 	/*
102 	 * loop over the definition list and extract the information we need.
103 	 */
104 	foreach(pl, parameters)
105 	{
106 		DefElem    *defel = (DefElem *) lfirst(pl);
107 
108 		if (pg_strcasecmp(defel->defname, "leftarg") == 0)
109 		{
110 			typeName1 = defGetTypeName(defel);
111 			if (typeName1->setof)
112 				ereport(ERROR,
113 						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
114 						 errmsg("SETOF type not allowed for operator argument")));
115 		}
116 		else if (pg_strcasecmp(defel->defname, "rightarg") == 0)
117 		{
118 			typeName2 = defGetTypeName(defel);
119 			if (typeName2->setof)
120 				ereport(ERROR,
121 						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
122 						 errmsg("SETOF type not allowed for operator argument")));
123 		}
124 		else if (pg_strcasecmp(defel->defname, "procedure") == 0)
125 			functionName = defGetQualifiedName(defel);
126 		else if (pg_strcasecmp(defel->defname, "commutator") == 0)
127 			commutatorName = defGetQualifiedName(defel);
128 		else if (pg_strcasecmp(defel->defname, "negator") == 0)
129 			negatorName = defGetQualifiedName(defel);
130 		else if (pg_strcasecmp(defel->defname, "restrict") == 0)
131 			restrictionName = defGetQualifiedName(defel);
132 		else if (pg_strcasecmp(defel->defname, "join") == 0)
133 			joinName = defGetQualifiedName(defel);
134 		else if (pg_strcasecmp(defel->defname, "hashes") == 0)
135 			canHash = defGetBoolean(defel);
136 		else if (pg_strcasecmp(defel->defname, "merges") == 0)
137 			canMerge = defGetBoolean(defel);
138 		/* These obsolete options are taken as meaning canMerge */
139 		else if (pg_strcasecmp(defel->defname, "sort1") == 0)
140 			canMerge = true;
141 		else if (pg_strcasecmp(defel->defname, "sort2") == 0)
142 			canMerge = true;
143 		else if (pg_strcasecmp(defel->defname, "ltcmp") == 0)
144 			canMerge = true;
145 		else if (pg_strcasecmp(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 procedure 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, ACL_KIND_PROC,
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. 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
ValidateRestrictionEstimator(List * restrictionName)261 ValidateRestrictionEstimator(List *restrictionName)
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, ACL_KIND_PROC,
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
ValidateJoinEstimator(List * joinName)296 ValidateJoinEstimator(List *joinName)
297 {
298 	Oid			typeId[5];
299 	Oid			joinOid;
300 	Oid			joinOid2;
301 	AclResult	aclresult;
302 
303 	typeId[0] = INTERNALOID;	/* PlannerInfo */
304 	typeId[1] = OIDOID;			/* operator OID */
305 	typeId[2] = INTERNALOID;	/* args list */
306 	typeId[3] = INT2OID;		/* jointype */
307 	typeId[4] = INTERNALOID;	/* SpecialJoinInfo */
308 
309 	/*
310 	 * As of Postgres 8.4, the preferred signature for join estimators has 5
311 	 * arguments, but we still allow the old 4-argument form.  Whine about
312 	 * ambiguity if both forms exist.
313 	 */
314 	joinOid = LookupFuncName(joinName, 5, typeId, true);
315 	joinOid2 = LookupFuncName(joinName, 4, typeId, true);
316 	if (OidIsValid(joinOid))
317 	{
318 		if (OidIsValid(joinOid2))
319 			ereport(ERROR,
320 					(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
321 					 errmsg("join estimator function %s has multiple matches",
322 							NameListToString(joinName))));
323 	}
324 	else
325 	{
326 		joinOid = joinOid2;
327 		/* If not found, reference the 5-argument signature in error msg */
328 		if (!OidIsValid(joinOid))
329 			joinOid = LookupFuncName(joinName, 5, typeId, false);
330 	}
331 
332 	/* estimators must return float8 */
333 	if (get_func_rettype(joinOid) != FLOAT8OID)
334 		ereport(ERROR,
335 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
336 				 errmsg("join estimator function %s must return type %s",
337 						NameListToString(joinName), "float8")));
338 
339 	/* Require EXECUTE rights for the estimator */
340 	aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
341 	if (aclresult != ACLCHECK_OK)
342 		aclcheck_error(aclresult, ACL_KIND_PROC,
343 					   NameListToString(joinName));
344 
345 	return joinOid;
346 }
347 
348 /*
349  * Guts of operator deletion.
350  */
351 void
RemoveOperatorById(Oid operOid)352 RemoveOperatorById(Oid operOid)
353 {
354 	Relation	relation;
355 	HeapTuple	tup;
356 	Form_pg_operator op;
357 
358 	relation = heap_open(OperatorRelationId, RowExclusiveLock);
359 
360 	tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
361 	if (!HeapTupleIsValid(tup)) /* should not happen */
362 		elog(ERROR, "cache lookup failed for operator %u", operOid);
363 	op = (Form_pg_operator) GETSTRUCT(tup);
364 
365 	/*
366 	 * Reset links from commutator and negator, if any.  In case of a
367 	 * self-commutator or self-negator, this means we have to re-fetch the
368 	 * updated tuple.  (We could optimize away updates on the tuple we're
369 	 * about to drop, but it doesn't seem worth convoluting the logic for.)
370 	 */
371 	if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
372 	{
373 		OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
374 		if (operOid == op->oprcom || operOid == op->oprnegate)
375 		{
376 			ReleaseSysCache(tup);
377 			tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
378 			if (!HeapTupleIsValid(tup)) /* should not happen */
379 				elog(ERROR, "cache lookup failed for operator %u", operOid);
380 		}
381 	}
382 
383 	CatalogTupleDelete(relation, &tup->t_self);
384 
385 	ReleaseSysCache(tup);
386 
387 	heap_close(relation, RowExclusiveLock);
388 }
389 
390 /*
391  * AlterOperator
392  *		routine implementing ALTER OPERATOR <operator> SET (option = ...).
393  *
394  * Currently, only RESTRICT and JOIN estimator functions can be changed.
395  */
396 ObjectAddress
AlterOperator(AlterOperatorStmt * stmt)397 AlterOperator(AlterOperatorStmt *stmt)
398 {
399 	ObjectAddress address;
400 	Oid			oprId;
401 	Relation	catalog;
402 	HeapTuple	tup;
403 	Form_pg_operator oprForm;
404 	int			i;
405 	ListCell   *pl;
406 	Datum		values[Natts_pg_operator];
407 	bool		nulls[Natts_pg_operator];
408 	bool		replaces[Natts_pg_operator];
409 	List	   *restrictionName = NIL;	/* optional restrict. sel. procedure */
410 	bool		updateRestriction = false;
411 	Oid			restrictionOid;
412 	List	   *joinName = NIL; /* optional join sel. procedure */
413 	bool		updateJoin = false;
414 	Oid			joinOid;
415 
416 	/* Look up the operator */
417 	oprId = LookupOperWithArgs(stmt->opername, false);
418 	catalog = heap_open(OperatorRelationId, RowExclusiveLock);
419 	tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
420 	if (tup == NULL)
421 		elog(ERROR, "cache lookup failed for operator %u", oprId);
422 	oprForm = (Form_pg_operator) GETSTRUCT(tup);
423 
424 	/* Process options */
425 	foreach(pl, stmt->options)
426 	{
427 		DefElem    *defel = (DefElem *) lfirst(pl);
428 		List	   *param;
429 
430 		if (defel->arg == NULL)
431 			param = NIL;		/* NONE, removes the function */
432 		else
433 			param = defGetQualifiedName(defel);
434 
435 		if (pg_strcasecmp(defel->defname, "restrict") == 0)
436 		{
437 			restrictionName = param;
438 			updateRestriction = true;
439 		}
440 		else if (pg_strcasecmp(defel->defname, "join") == 0)
441 		{
442 			joinName = param;
443 			updateJoin = true;
444 		}
445 
446 		/*
447 		 * The rest of the options that CREATE accepts cannot be changed.
448 		 * Check for them so that we can give a meaningful error message.
449 		 */
450 		else if (pg_strcasecmp(defel->defname, "leftarg") == 0 ||
451 				 pg_strcasecmp(defel->defname, "rightarg") == 0 ||
452 				 pg_strcasecmp(defel->defname, "procedure") == 0 ||
453 				 pg_strcasecmp(defel->defname, "commutator") == 0 ||
454 				 pg_strcasecmp(defel->defname, "negator") == 0 ||
455 				 pg_strcasecmp(defel->defname, "hashes") == 0 ||
456 				 pg_strcasecmp(defel->defname, "merges") == 0)
457 		{
458 			ereport(ERROR,
459 					(errcode(ERRCODE_SYNTAX_ERROR),
460 					 errmsg("operator attribute \"%s\" cannot be changed",
461 							defel->defname)));
462 		}
463 		else
464 			ereport(ERROR,
465 					(errcode(ERRCODE_SYNTAX_ERROR),
466 					 errmsg("operator attribute \"%s\" not recognized",
467 							defel->defname)));
468 	}
469 
470 	/* Check permissions. Must be owner. */
471 	if (!pg_oper_ownercheck(oprId, GetUserId()))
472 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
473 					   NameStr(oprForm->oprname));
474 
475 	/*
476 	 * Look up restriction and join estimators if specified
477 	 */
478 	if (restrictionName)
479 		restrictionOid = ValidateRestrictionEstimator(restrictionName);
480 	else
481 		restrictionOid = InvalidOid;
482 	if (joinName)
483 		joinOid = ValidateJoinEstimator(joinName);
484 	else
485 		joinOid = InvalidOid;
486 
487 	/* Perform additional checks, like OperatorCreate does */
488 	if (!(OidIsValid(oprForm->oprleft) && OidIsValid(oprForm->oprright)))
489 	{
490 		/* If it's not a binary op, these things mustn't be set: */
491 		if (OidIsValid(joinOid))
492 			ereport(ERROR,
493 					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
494 					 errmsg("only binary operators can have join selectivity")));
495 	}
496 
497 	if (oprForm->oprresult != BOOLOID)
498 	{
499 		if (OidIsValid(restrictionOid))
500 			ereport(ERROR,
501 					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
502 					 errmsg("only boolean operators can have restriction selectivity")));
503 		if (OidIsValid(joinOid))
504 			ereport(ERROR,
505 					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
506 					 errmsg("only boolean operators can have join selectivity")));
507 	}
508 
509 	/* Update the tuple */
510 	for (i = 0; i < Natts_pg_operator; ++i)
511 	{
512 		values[i] = (Datum) 0;
513 		replaces[i] = false;
514 		nulls[i] = false;
515 	}
516 	if (updateRestriction)
517 	{
518 		replaces[Anum_pg_operator_oprrest - 1] = true;
519 		values[Anum_pg_operator_oprrest - 1] = restrictionOid;
520 	}
521 	if (updateJoin)
522 	{
523 		replaces[Anum_pg_operator_oprjoin - 1] = true;
524 		values[Anum_pg_operator_oprjoin - 1] = joinOid;
525 	}
526 
527 	tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
528 							values, nulls, replaces);
529 
530 	CatalogTupleUpdate(catalog, &tup->t_self, tup);
531 
532 	address = makeOperatorDependencies(tup, true);
533 
534 	InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
535 
536 	heap_close(catalog, NoLock);
537 
538 	return address;
539 }
540