Disallow empty passwords in LDAP authentication, the same way
[PostgreSQL.git] / src / backend / commands / operatorcmds.c
blob6c056115374f4a26bd58f0f2e117cb9767043201
1 /*-------------------------------------------------------------------------
3 * operatorcmds.c
5 * Routines for operator manipulation commands
7 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * IDENTIFICATION
12 * $PostgreSQL$
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.
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
30 * Most of the parse-tree manipulation routines are defined in
31 * commands/manip.c.
33 *-------------------------------------------------------------------------
35 #include "postgres.h"
37 #include "access/heapam.h"
38 #include "catalog/dependency.h"
39 #include "catalog/indexing.h"
40 #include "catalog/namespace.h"
41 #include "catalog/pg_operator.h"
42 #include "catalog/pg_type.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/acl.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);
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.
62 * 'parameters' is a list of DefElem
64 void
65 DefineOperator(List *names, List *parameters)
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 List *commutatorName = NIL; /* optional commutator operator name */
78 List *negatorName = NIL; /* optional negator operator name */
79 List *restrictionName = NIL; /* optional restrict. sel. procedure */
80 List *joinName = NIL; /* optional join sel. procedure */
81 Oid functionOid; /* functions converted to OID */
82 Oid restrictionOid;
83 Oid joinOid;
84 Oid typeId[5]; /* only need up to 5 args here */
85 int nargs;
86 ListCell *pl;
88 /* Convert list of names to a name and namespace */
89 oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
91 /* Check we have creation rights in target namespace */
92 aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
93 if (aclresult != ACLCHECK_OK)
94 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
95 get_namespace_name(oprNamespace));
98 * loop over the definition list and extract the information we need.
100 foreach(pl, parameters)
102 DefElem *defel = (DefElem *) lfirst(pl);
104 if (pg_strcasecmp(defel->defname, "leftarg") == 0)
106 typeName1 = defGetTypeName(defel);
107 if (typeName1->setof)
108 ereport(ERROR,
109 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
110 errmsg("SETOF type not allowed for operator argument")));
112 else if (pg_strcasecmp(defel->defname, "rightarg") == 0)
114 typeName2 = defGetTypeName(defel);
115 if (typeName2->setof)
116 ereport(ERROR,
117 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
118 errmsg("SETOF type not allowed for operator argument")));
120 else if (pg_strcasecmp(defel->defname, "procedure") == 0)
121 functionName = defGetQualifiedName(defel);
122 else if (pg_strcasecmp(defel->defname, "commutator") == 0)
123 commutatorName = defGetQualifiedName(defel);
124 else if (pg_strcasecmp(defel->defname, "negator") == 0)
125 negatorName = defGetQualifiedName(defel);
126 else if (pg_strcasecmp(defel->defname, "restrict") == 0)
127 restrictionName = defGetQualifiedName(defel);
128 else if (pg_strcasecmp(defel->defname, "join") == 0)
129 joinName = defGetQualifiedName(defel);
130 else if (pg_strcasecmp(defel->defname, "hashes") == 0)
131 canHash = defGetBoolean(defel);
132 else if (pg_strcasecmp(defel->defname, "merges") == 0)
133 canMerge = defGetBoolean(defel);
134 /* These obsolete options are taken as meaning canMerge */
135 else if (pg_strcasecmp(defel->defname, "sort1") == 0)
136 canMerge = true;
137 else if (pg_strcasecmp(defel->defname, "sort2") == 0)
138 canMerge = true;
139 else if (pg_strcasecmp(defel->defname, "ltcmp") == 0)
140 canMerge = true;
141 else if (pg_strcasecmp(defel->defname, "gtcmp") == 0)
142 canMerge = true;
143 else
144 ereport(WARNING,
145 (errcode(ERRCODE_SYNTAX_ERROR),
146 errmsg("operator attribute \"%s\" not recognized",
147 defel->defname)));
151 * make sure we have our required definitions
153 if (functionName == NIL)
154 ereport(ERROR,
155 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
156 errmsg("operator procedure must be specified")));
158 /* Transform type names to type OIDs */
159 if (typeName1)
160 typeId1 = typenameTypeId(NULL, typeName1, NULL);
161 if (typeName2)
162 typeId2 = typenameTypeId(NULL, typeName2, NULL);
164 if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
165 ereport(ERROR,
166 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
167 errmsg("at least one of leftarg or rightarg must be specified")));
170 * Look up the operator's underlying function.
172 if (!OidIsValid(typeId1))
174 typeId[0] = typeId2;
175 nargs = 1;
177 else if (!OidIsValid(typeId2))
179 typeId[0] = typeId1;
180 nargs = 1;
182 else
184 typeId[0] = typeId1;
185 typeId[1] = typeId2;
186 nargs = 2;
188 functionOid = LookupFuncName(functionName, nargs, typeId, false);
191 * We require EXECUTE rights for the function. This isn't strictly
192 * necessary, since EXECUTE will be checked at any attempted use of the
193 * operator, but it seems like a good idea anyway.
195 aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE);
196 if (aclresult != ACLCHECK_OK)
197 aclcheck_error(aclresult, ACL_KIND_PROC,
198 NameListToString(functionName));
201 * Look up restriction estimator if specified
203 if (restrictionName)
205 typeId[0] = INTERNALOID; /* PlannerInfo */
206 typeId[1] = OIDOID; /* operator OID */
207 typeId[2] = INTERNALOID; /* args list */
208 typeId[3] = INT4OID; /* varRelid */
210 restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
212 /* estimators must return float8 */
213 if (get_func_rettype(restrictionOid) != FLOAT8OID)
214 ereport(ERROR,
215 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
216 errmsg("restriction estimator function %s must return type \"float8\"",
217 NameListToString(restrictionName))));
219 /* Require EXECUTE rights for the estimator */
220 aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE);
221 if (aclresult != ACLCHECK_OK)
222 aclcheck_error(aclresult, ACL_KIND_PROC,
223 NameListToString(restrictionName));
225 else
226 restrictionOid = InvalidOid;
229 * Look up join estimator if specified
231 if (joinName)
233 typeId[0] = INTERNALOID; /* PlannerInfo */
234 typeId[1] = OIDOID; /* operator OID */
235 typeId[2] = INTERNALOID; /* args list */
236 typeId[3] = INT2OID; /* jointype */
237 typeId[4] = INTERNALOID; /* SpecialJoinInfo */
240 * As of Postgres 8.4, the preferred signature for join estimators has
241 * 5 arguments, but we still allow the old 4-argument form. Try the
242 * preferred form first.
244 joinOid = LookupFuncName(joinName, 5, typeId, true);
245 if (!OidIsValid(joinOid))
246 joinOid = LookupFuncName(joinName, 4, typeId, true);
247 /* If not found, reference the 5-argument signature in error msg */
248 if (!OidIsValid(joinOid))
249 joinOid = LookupFuncName(joinName, 5, typeId, false);
251 /* estimators must return float8 */
252 if (get_func_rettype(joinOid) != FLOAT8OID)
253 ereport(ERROR,
254 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
255 errmsg("join estimator function %s must return type \"float8\"",
256 NameListToString(joinName))));
258 /* Require EXECUTE rights for the estimator */
259 aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
260 if (aclresult != ACLCHECK_OK)
261 aclcheck_error(aclresult, ACL_KIND_PROC,
262 NameListToString(joinName));
264 else
265 joinOid = InvalidOid;
268 * now have OperatorCreate do all the work..
270 OperatorCreate(oprName, /* operator name */
271 oprNamespace, /* namespace */
272 typeId1, /* left type id */
273 typeId2, /* right type id */
274 functionOid, /* function for operator */
275 commutatorName, /* optional commutator operator name */
276 negatorName, /* optional negator operator name */
277 restrictionOid, /* optional restrict. sel. procedure */
278 joinOid, /* optional join sel. procedure name */
279 canMerge, /* operator merges */
280 canHash); /* operator hashes */
285 * RemoveOperator
286 * Deletes an operator.
288 void
289 RemoveOperator(RemoveFuncStmt *stmt)
291 List *operatorName = stmt->name;
292 TypeName *typeName1 = (TypeName *) linitial(stmt->args);
293 TypeName *typeName2 = (TypeName *) lsecond(stmt->args);
294 Oid operOid;
295 HeapTuple tup;
296 ObjectAddress object;
298 Assert(list_length(stmt->args) == 2);
299 operOid = LookupOperNameTypeNames(NULL, operatorName,
300 typeName1, typeName2,
301 stmt->missing_ok, -1);
303 if (stmt->missing_ok && !OidIsValid(operOid))
305 ereport(NOTICE,
306 (errmsg("operator %s does not exist, skipping",
307 NameListToString(operatorName))));
308 return;
311 tup = SearchSysCache(OPEROID,
312 ObjectIdGetDatum(operOid),
313 0, 0, 0);
314 if (!HeapTupleIsValid(tup)) /* should not happen */
315 elog(ERROR, "cache lookup failed for operator %u", operOid);
317 /* Permission check: must own operator or its namespace */
318 if (!pg_oper_ownercheck(operOid, GetUserId()) &&
319 !pg_namespace_ownercheck(((Form_pg_operator) GETSTRUCT(tup))->oprnamespace,
320 GetUserId()))
321 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
322 NameListToString(operatorName));
324 ReleaseSysCache(tup);
327 * Do the deletion
329 object.classId = OperatorRelationId;
330 object.objectId = operOid;
331 object.objectSubId = 0;
333 performDeletion(&object, stmt->behavior);
337 * Guts of operator deletion.
339 void
340 RemoveOperatorById(Oid operOid)
342 Relation relation;
343 HeapTuple tup;
345 relation = heap_open(OperatorRelationId, RowExclusiveLock);
347 tup = SearchSysCache(OPEROID,
348 ObjectIdGetDatum(operOid),
349 0, 0, 0);
350 if (!HeapTupleIsValid(tup)) /* should not happen */
351 elog(ERROR, "cache lookup failed for operator %u", operOid);
353 simple_heap_delete(relation, &tup->t_self);
355 ReleaseSysCache(tup);
357 heap_close(relation, RowExclusiveLock);
360 void
361 AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId)
363 Relation rel;
365 rel = heap_open(OperatorRelationId, RowExclusiveLock);
367 AlterOperatorOwner_internal(rel, operOid, newOwnerId);
369 heap_close(rel, NoLock);
373 * change operator owner
375 void
376 AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
377 Oid newOwnerId)
379 Oid operOid;
380 Relation rel;
382 rel = heap_open(OperatorRelationId, RowExclusiveLock);
384 operOid = LookupOperNameTypeNames(NULL, name,
385 typeName1, typeName2,
386 false, -1);
388 AlterOperatorOwner_internal(rel, operOid, newOwnerId);
390 heap_close(rel, NoLock);
393 static void
394 AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
396 HeapTuple tup;
397 AclResult aclresult;
398 Form_pg_operator oprForm;
400 Assert(RelationGetRelid(rel) == OperatorRelationId);
402 tup = SearchSysCacheCopy(OPEROID,
403 ObjectIdGetDatum(operOid),
404 0, 0, 0);
405 if (!HeapTupleIsValid(tup)) /* should not happen */
406 elog(ERROR, "cache lookup failed for operator %u", operOid);
408 oprForm = (Form_pg_operator) GETSTRUCT(tup);
411 * If the new owner is the same as the existing owner, consider the
412 * command to have succeeded. This is for dump restoration purposes.
414 if (oprForm->oprowner != newOwnerId)
416 /* Superusers can always do it */
417 if (!superuser())
419 /* Otherwise, must be owner of the existing object */
420 if (!pg_oper_ownercheck(operOid, GetUserId()))
421 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
422 NameStr(oprForm->oprname));
424 /* Must be able to become new owner */
425 check_is_member_of_role(GetUserId(), newOwnerId);
427 /* New owner must have CREATE privilege on namespace */
428 aclresult = pg_namespace_aclcheck(oprForm->oprnamespace,
429 newOwnerId,
430 ACL_CREATE);
431 if (aclresult != ACLCHECK_OK)
432 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
433 get_namespace_name(oprForm->oprnamespace));
437 * Modify the owner --- okay to scribble on tup because it's a copy
439 oprForm->oprowner = newOwnerId;
441 simple_heap_update(rel, &tup->t_self, tup);
443 CatalogUpdateIndexes(rel, tup);
445 /* Update owner dependency reference */
446 changeDependencyOnOwner(OperatorRelationId, operOid, newOwnerId);
449 heap_freetuple(tup);