1 /*-------------------------------------------------------------------------
5 * Routines for operator manipulation commands
7 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/backend/commands/operatorcmds.c
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.
22 * These things must be defined and committed in the following order:
24 * input/output, recv/send procedures
30 * Most of the parse-tree manipulation routines are defined in
33 *-------------------------------------------------------------------------
37 #include "access/heapam.h"
38 #include "catalog/dependency.h"
39 #include "catalog/indexing.h"
40 #include "catalog/pg_operator.h"
41 #include "catalog/pg_type.h"
42 #include "commands/alter.h"
43 #include "commands/defrem.h"
44 #include "miscadmin.h"
45 #include "parser/parse_func.h"
46 #include "parser/parse_oper.h"
47 #include "parser/parse_type.h"
48 #include "utils/builtins.h"
49 #include "utils/lsyscache.h"
50 #include "utils/rel.h"
51 #include "utils/syscache.h"
54 static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId);
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.
62 * 'parameters' is a list of DefElem
65 DefineOperator(List *names, List *parameters)
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;
78 List *commutatorName = NIL; /* optional commutator operator name */
79 List *negatorName = NIL; /* optional negator operator name */
80 List *restrictionName = NIL; /* optional restrict. sel. procedure */
81 List *joinName = NIL; /* optional join sel. procedure */
82 Oid functionOid; /* functions converted to OID */
85 Oid typeId[5]; /* only need up to 5 args here */
89 /* Convert list of names to a name and namespace */
90 oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
93 * The SQL standard committee has decided that => should be used for named
94 * parameters; therefore, a future release of PostgreSQL may disallow it
95 * as the name of a user-defined operator.
97 if (strcmp(oprName, "=>") == 0)
99 (errmsg("=> is deprecated as an operator name"),
100 errdetail("This name may be disallowed altogether in future versions of PostgreSQL.")));
102 /* Check we have creation rights in target namespace */
103 aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
104 if (aclresult != ACLCHECK_OK)
105 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
106 get_namespace_name(oprNamespace));
109 * loop over the definition list and extract the information we need.
111 foreach(pl, parameters)
113 DefElem *defel = (DefElem *) lfirst(pl);
115 if (pg_strcasecmp(defel->defname, "leftarg") == 0)
117 typeName1 = defGetTypeName(defel);
118 if (typeName1->setof)
120 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
121 errmsg("SETOF type not allowed for operator argument")));
123 else if (pg_strcasecmp(defel->defname, "rightarg") == 0)
125 typeName2 = defGetTypeName(defel);
126 if (typeName2->setof)
128 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
129 errmsg("SETOF type not allowed for operator argument")));
131 else if (pg_strcasecmp(defel->defname, "procedure") == 0)
132 functionName = defGetQualifiedName(defel);
133 else if (pg_strcasecmp(defel->defname, "commutator") == 0)
134 commutatorName = defGetQualifiedName(defel);
135 else if (pg_strcasecmp(defel->defname, "negator") == 0)
136 negatorName = defGetQualifiedName(defel);
137 else if (pg_strcasecmp(defel->defname, "restrict") == 0)
138 restrictionName = defGetQualifiedName(defel);
139 else if (pg_strcasecmp(defel->defname, "join") == 0)
140 joinName = defGetQualifiedName(defel);
141 else if (pg_strcasecmp(defel->defname, "hashes") == 0)
142 canHash = defGetBoolean(defel);
143 else if (pg_strcasecmp(defel->defname, "merges") == 0)
144 canMerge = defGetBoolean(defel);
145 /* These obsolete options are taken as meaning canMerge */
146 else if (pg_strcasecmp(defel->defname, "sort1") == 0)
148 else if (pg_strcasecmp(defel->defname, "sort2") == 0)
150 else if (pg_strcasecmp(defel->defname, "ltcmp") == 0)
152 else if (pg_strcasecmp(defel->defname, "gtcmp") == 0)
156 (errcode(ERRCODE_SYNTAX_ERROR),
157 errmsg("operator attribute \"%s\" not recognized",
162 * make sure we have our required definitions
164 if (functionName == NIL)
166 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
167 errmsg("operator procedure must be specified")));
169 /* Transform type names to type OIDs */
171 typeId1 = typenameTypeId(NULL, typeName1);
173 typeId2 = typenameTypeId(NULL, typeName2);
175 if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
177 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
178 errmsg("at least one of leftarg or rightarg must be specified")));
182 aclresult = pg_type_aclcheck(typeId1, GetUserId(), ACL_USAGE);
183 if (aclresult != ACLCHECK_OK)
184 aclcheck_error_type(aclresult, typeId1);
189 aclresult = pg_type_aclcheck(typeId2, GetUserId(), ACL_USAGE);
190 if (aclresult != ACLCHECK_OK)
191 aclcheck_error_type(aclresult, typeId2);
195 * Look up the operator's underlying function.
197 if (!OidIsValid(typeId1))
202 else if (!OidIsValid(typeId2))
213 functionOid = LookupFuncName(functionName, nargs, typeId, false);
216 * We require EXECUTE rights for the function. This isn't strictly
217 * necessary, since EXECUTE will be checked at any attempted use of the
218 * operator, but it seems like a good idea anyway.
220 aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE);
221 if (aclresult != ACLCHECK_OK)
222 aclcheck_error(aclresult, ACL_KIND_PROC,
223 NameListToString(functionName));
225 rettype = get_func_rettype(functionOid);
226 aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE);
227 if (aclresult != ACLCHECK_OK)
228 aclcheck_error_type(aclresult, rettype);
231 * Look up restriction estimator if specified
235 typeId[0] = INTERNALOID; /* PlannerInfo */
236 typeId[1] = OIDOID; /* operator OID */
237 typeId[2] = INTERNALOID; /* args list */
238 typeId[3] = INT4OID; /* varRelid */
240 restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
242 /* estimators must return float8 */
243 if (get_func_rettype(restrictionOid) != FLOAT8OID)
245 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
246 errmsg("restriction estimator function %s must return type \"float8\"",
247 NameListToString(restrictionName))));
249 /* Require EXECUTE rights for the estimator */
250 aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE);
251 if (aclresult != ACLCHECK_OK)
252 aclcheck_error(aclresult, ACL_KIND_PROC,
253 NameListToString(restrictionName));
256 restrictionOid = InvalidOid;
259 * Look up join estimator if specified
263 typeId[0] = INTERNALOID; /* PlannerInfo */
264 typeId[1] = OIDOID; /* operator OID */
265 typeId[2] = INTERNALOID; /* args list */
266 typeId[3] = INT2OID; /* jointype */
267 typeId[4] = INTERNALOID; /* SpecialJoinInfo */
270 * As of Postgres 8.4, the preferred signature for join estimators has
271 * 5 arguments, but we still allow the old 4-argument form. Try the
272 * preferred form first.
274 joinOid = LookupFuncName(joinName, 5, typeId, true);
275 if (!OidIsValid(joinOid))
276 joinOid = LookupFuncName(joinName, 4, typeId, true);
277 /* If not found, reference the 5-argument signature in error msg */
278 if (!OidIsValid(joinOid))
279 joinOid = LookupFuncName(joinName, 5, typeId, false);
281 /* estimators must return float8 */
282 if (get_func_rettype(joinOid) != FLOAT8OID)
284 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
285 errmsg("join estimator function %s must return type \"float8\"",
286 NameListToString(joinName))));
288 /* Require EXECUTE rights for the estimator */
289 aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
290 if (aclresult != ACLCHECK_OK)
291 aclcheck_error(aclresult, ACL_KIND_PROC,
292 NameListToString(joinName));
295 joinOid = InvalidOid;
298 * now have OperatorCreate do all the work..
300 OperatorCreate(oprName, /* operator name */
301 oprNamespace, /* namespace */
302 typeId1, /* left type id */
303 typeId2, /* right type id */
304 functionOid, /* function for operator */
305 commutatorName, /* optional commutator operator name */
306 negatorName, /* optional negator operator name */
307 restrictionOid, /* optional restrict. sel. procedure */
308 joinOid, /* optional join sel. procedure name */
309 canMerge, /* operator merges */
310 canHash); /* operator hashes */
315 * Guts of operator deletion.
318 RemoveOperatorById(Oid operOid)
323 relation = heap_open(OperatorRelationId, RowExclusiveLock);
325 tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
326 if (!HeapTupleIsValid(tup)) /* should not happen */
327 elog(ERROR, "cache lookup failed for operator %u", operOid);
329 simple_heap_delete(relation, &tup->t_self);
331 ReleaseSysCache(tup);
333 heap_close(relation, RowExclusiveLock);
337 AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId)
341 rel = heap_open(OperatorRelationId, RowExclusiveLock);
343 AlterOperatorOwner_internal(rel, operOid, newOwnerId);
345 heap_close(rel, NoLock);
349 * change operator owner
352 AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
358 rel = heap_open(OperatorRelationId, RowExclusiveLock);
360 operOid = LookupOperNameTypeNames(NULL, name,
361 typeName1, typeName2,
364 AlterOperatorOwner_internal(rel, operOid, newOwnerId);
366 heap_close(rel, NoLock);
370 AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
374 Form_pg_operator oprForm;
376 Assert(RelationGetRelid(rel) == OperatorRelationId);
378 tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(operOid));
379 if (!HeapTupleIsValid(tup)) /* should not happen */
380 elog(ERROR, "cache lookup failed for operator %u", operOid);
382 oprForm = (Form_pg_operator) GETSTRUCT(tup);
385 * If the new owner is the same as the existing owner, consider the
386 * command to have succeeded. This is for dump restoration purposes.
388 if (oprForm->oprowner != newOwnerId)
390 /* Superusers can always do it */
393 /* Otherwise, must be owner of the existing object */
394 if (!pg_oper_ownercheck(operOid, GetUserId()))
395 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
396 NameStr(oprForm->oprname));
398 /* Must be able to become new owner */
399 check_is_member_of_role(GetUserId(), newOwnerId);
401 /* New owner must have CREATE privilege on namespace */
402 aclresult = pg_namespace_aclcheck(oprForm->oprnamespace,
405 if (aclresult != ACLCHECK_OK)
406 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
407 get_namespace_name(oprForm->oprnamespace));
411 * Modify the owner --- okay to scribble on tup because it's a copy
413 oprForm->oprowner = newOwnerId;
415 simple_heap_update(rel, &tup->t_self, tup);
417 CatalogUpdateIndexes(rel, tup);
419 /* Update owner dependency reference */
420 changeDependencyOnOwner(OperatorRelationId, operOid, newOwnerId);
427 * Execute ALTER OPERATOR SET SCHEMA
430 AlterOperatorNamespace(List *names, List *argtypes, const char *newschema)
432 List *operatorName = names;
433 TypeName *typeName1 = (TypeName *) linitial(argtypes);
434 TypeName *typeName2 = (TypeName *) lsecond(argtypes);
439 rel = heap_open(OperatorRelationId, RowExclusiveLock);
441 Assert(list_length(argtypes) == 2);
442 operOid = LookupOperNameTypeNames(NULL, operatorName,
443 typeName1, typeName2,
447 nspOid = LookupCreationNamespace(newschema);
449 AlterObjectNamespace(rel, OPEROID, -1,
451 Anum_pg_operator_oprname,
452 Anum_pg_operator_oprnamespace,
453 Anum_pg_operator_oprowner,
456 heap_close(rel, RowExclusiveLock);
460 AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid)
465 rel = heap_open(OperatorRelationId, RowExclusiveLock);
467 oldNspOid = AlterObjectNamespace(rel, OPEROID, -1,
469 Anum_pg_operator_oprname,
470 Anum_pg_operator_oprnamespace,
471 Anum_pg_operator_oprowner,
474 heap_close(rel, RowExclusiveLock);