1
/*-------------------------------------------------------------------------
5
* Routines for aggregate-manipulation commands
7
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
8
* Portions Copyright (c) 1994, Regents of the University of California
12
* $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.22 2004-12-31 21:59:41 pgsql Exp $
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
*-------------------------------------------------------------------------
25
#include "access/heapam.h"
26
#include "catalog/catname.h"
27
#include "catalog/dependency.h"
28
#include "catalog/indexing.h"
29
#include "catalog/namespace.h"
30
#include "catalog/pg_aggregate.h"
31
#include "catalog/pg_proc.h"
32
#include "catalog/pg_type.h"
33
#include "commands/defrem.h"
34
#include "miscadmin.h"
35
#include "parser/parse_func.h"
36
#include "parser/parse_type.h"
37
#include "utils/acl.h"
38
#include "utils/builtins.h"
39
#include "utils/lsyscache.h"
40
#include "utils/syscache.h"
47
DefineAggregate(List *names, List *parameters)
52
List *transfuncName = NIL;
53
List *finalfuncName = NIL;
54
TypeName *baseType = NULL;
55
TypeName *transType = NULL;
61
/* Convert list of names to a name and namespace */
62
aggNamespace = QualifiedNameGetCreationNamespace(names, &aggName);
64
/* Check we have creation rights in target namespace */
65
aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE);
66
if (aclresult != ACLCHECK_OK)
67
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
68
get_namespace_name(aggNamespace));
70
foreach(pl, parameters)
72
DefElem *defel = (DefElem *) lfirst(pl);
75
* sfunc1, stype1, and initcond1 are accepted as obsolete
76
* spellings for sfunc, stype, initcond.
78
if (pg_strcasecmp(defel->defname, "sfunc") == 0)
79
transfuncName = defGetQualifiedName(defel);
80
else if (pg_strcasecmp(defel->defname, "sfunc1") == 0)
81
transfuncName = defGetQualifiedName(defel);
82
else if (pg_strcasecmp(defel->defname, "finalfunc") == 0)
83
finalfuncName = defGetQualifiedName(defel);
84
else if (pg_strcasecmp(defel->defname, "basetype") == 0)
85
baseType = defGetTypeName(defel);
86
else if (pg_strcasecmp(defel->defname, "stype") == 0)
87
transType = defGetTypeName(defel);
88
else if (pg_strcasecmp(defel->defname, "stype1") == 0)
89
transType = defGetTypeName(defel);
90
else if (pg_strcasecmp(defel->defname, "initcond") == 0)
91
initval = defGetString(defel);
92
else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
93
initval = defGetString(defel);
96
(errcode(ERRCODE_SYNTAX_ERROR),
97
errmsg("aggregate attribute \"%s\" not recognized",
102
* make sure we have our required definitions
104
if (baseType == NULL)
106
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
107
errmsg("aggregate basetype must be specified")));
108
if (transType == NULL)
110
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
111
errmsg("aggregate stype must be specified")));
112
if (transfuncName == NIL)
114
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
115
errmsg("aggregate sfunc must be specified")));
118
* look up the aggregate's base type (input datatype) and transtype.
120
* We have historically allowed the command to look like basetype = 'ANY'
121
* so we must do a case-insensitive comparison for the name ANY. Ugh.
123
* basetype can be a pseudo-type, but transtype can't, since we need to
124
* be able to store values of the transtype. However, we can allow
125
* polymorphic transtype in some cases (AggregateCreate will check).
127
if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0)
130
baseTypeId = typenameTypeId(baseType);
132
transTypeId = typenameTypeId(transType);
133
if (get_typtype(transTypeId) == 'p' &&
134
transTypeId != ANYARRAYOID &&
135
transTypeId != ANYELEMENTOID)
137
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
138
errmsg("aggregate transition data type cannot be %s",
139
format_type_be(transTypeId))));
142
* Most of the argument-checking is done inside of AggregateCreate
144
AggregateCreate(aggName, /* aggregate name */
145
aggNamespace, /* namespace */
146
transfuncName, /* step function name */
147
finalfuncName, /* final function name */
148
baseTypeId, /* type of data being aggregated */
149
transTypeId, /* transition data type */
150
initval); /* initial condition */
156
* Deletes an aggregate.
159
RemoveAggregate(RemoveAggrStmt *stmt)
161
List *aggName = stmt->aggname;
162
TypeName *aggType = stmt->aggtype;
166
ObjectAddress object;
169
* if a basetype is passed in, then attempt to find an aggregate for
170
* that specific type.
172
* else attempt to find an aggregate with a basetype of ANYOID. This
173
* means that the aggregate is to apply to all basetypes (eg, COUNT).
176
basetypeID = typenameTypeId(aggType);
180
procOid = find_aggregate_func(aggName, basetypeID, false);
183
* Find the function tuple, do permissions and validity checks
185
tup = SearchSysCache(PROCOID,
186
ObjectIdGetDatum(procOid),
188
if (!HeapTupleIsValid(tup)) /* should not happen */
189
elog(ERROR, "cache lookup failed for function %u", procOid);
191
/* Permission check: must own agg or its namespace */
192
if (!pg_proc_ownercheck(procOid, GetUserId()) &&
193
!pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
195
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
196
NameListToString(aggName));
198
/* find_aggregate_func already checked it is an aggregate */
200
ReleaseSysCache(tup);
205
object.classId = RelOid_pg_proc;
206
object.objectId = procOid;
207
object.objectSubId = 0;
209
performDeletion(&object, stmt->behavior);
214
RenameAggregate(List *name, TypeName *basetype, const char *newname)
220
Form_pg_proc procForm;
225
* if a basetype is passed in, then attempt to find an aggregate for
226
* that specific type; else attempt to find an aggregate with a
227
* basetype of ANYOID. This means that the aggregate applies to all
228
* basetypes (eg, COUNT).
231
basetypeOid = typenameTypeId(basetype);
233
basetypeOid = ANYOID;
235
rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
237
procOid = find_aggregate_func(name, basetypeOid, false);
239
tup = SearchSysCacheCopy(PROCOID,
240
ObjectIdGetDatum(procOid),
242
if (!HeapTupleIsValid(tup)) /* should not happen */
243
elog(ERROR, "cache lookup failed for function %u", procOid);
244
procForm = (Form_pg_proc) GETSTRUCT(tup);
246
namespaceOid = procForm->pronamespace;
248
/* make sure the new name doesn't exist */
249
if (SearchSysCacheExists(PROCNAMENSP,
250
CStringGetDatum(newname),
251
Int16GetDatum(procForm->pronargs),
252
PointerGetDatum(procForm->proargtypes),
253
ObjectIdGetDatum(namespaceOid)))
255
if (basetypeOid == ANYOID)
257
(errcode(ERRCODE_DUPLICATE_FUNCTION),
258
errmsg("function %s(*) already exists in schema \"%s\"",
260
get_namespace_name(namespaceOid))));
263
(errcode(ERRCODE_DUPLICATE_FUNCTION),
264
errmsg("function %s already exists in schema \"%s\"",
265
funcname_signature_string(newname,
267
procForm->proargtypes),
268
get_namespace_name(namespaceOid))));
272
if (!pg_proc_ownercheck(procOid, GetUserId()))
273
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
274
NameListToString(name));
276
/* must have CREATE privilege on namespace */
277
aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
278
if (aclresult != ACLCHECK_OK)
279
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
280
get_namespace_name(namespaceOid));
283
namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname);
284
simple_heap_update(rel, &tup->t_self, tup);
285
CatalogUpdateIndexes(rel, tup);
287
heap_close(rel, NoLock);
292
* Change aggregate owner
295
AlterAggregateOwner(List *name, TypeName *basetype, AclId newOwnerSysId)
300
Form_pg_proc procForm;
304
* if a basetype is passed in, then attempt to find an aggregate for
305
* that specific type; else attempt to find an aggregate with a
306
* basetype of ANYOID. This means that the aggregate applies to all
307
* basetypes (eg, COUNT).
310
basetypeOid = typenameTypeId(basetype);
312
basetypeOid = ANYOID;
314
rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
316
procOid = find_aggregate_func(name, basetypeOid, false);
318
tup = SearchSysCacheCopy(PROCOID,
319
ObjectIdGetDatum(procOid),
321
if (!HeapTupleIsValid(tup)) /* should not happen */
322
elog(ERROR, "cache lookup failed for function %u", procOid);
323
procForm = (Form_pg_proc) GETSTRUCT(tup);
326
* If the new owner is the same as the existing owner, consider the
327
* command to have succeeded. This is for dump restoration purposes.
329
if (procForm->proowner != newOwnerSysId)
331
/* Otherwise, must be superuser to change object ownership */
334
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
335
errmsg("must be superuser to change owner")));
338
* Modify the owner --- okay to scribble on tup because it's a
341
procForm->proowner = newOwnerSysId;
343
simple_heap_update(rel, &tup->t_self, tup);
344
CatalogUpdateIndexes(rel, tup);
347
heap_close(rel, NoLock);