~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/backend/commands/aggregatecmds.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * aggregatecmds.c
 
4
 *
 
5
 *        Routines for aggregate-manipulation commands
 
6
 *
 
7
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
 
8
 * Portions Copyright (c) 1994, Regents of the University of California
 
9
 *
 
10
 *
 
11
 * IDENTIFICATION
 
12
 *        $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.22 2004-12-31 21:59:41 pgsql Exp $
 
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
 *-------------------------------------------------------------------------
 
22
 */
 
23
#include "postgres.h"
 
24
 
 
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"
 
41
 
 
42
 
 
43
/*
 
44
 *      DefineAggregate
 
45
 */
 
46
void
 
47
DefineAggregate(List *names, List *parameters)
 
48
{
 
49
        char       *aggName;
 
50
        Oid                     aggNamespace;
 
51
        AclResult       aclresult;
 
52
        List       *transfuncName = NIL;
 
53
        List       *finalfuncName = NIL;
 
54
        TypeName   *baseType = NULL;
 
55
        TypeName   *transType = NULL;
 
56
        char       *initval = NULL;
 
57
        Oid                     baseTypeId;
 
58
        Oid                     transTypeId;
 
59
        ListCell   *pl;
 
60
 
 
61
        /* Convert list of names to a name and namespace */
 
62
        aggNamespace = QualifiedNameGetCreationNamespace(names, &aggName);
 
63
 
 
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));
 
69
 
 
70
        foreach(pl, parameters)
 
71
        {
 
72
                DefElem    *defel = (DefElem *) lfirst(pl);
 
73
 
 
74
                /*
 
75
                 * sfunc1, stype1, and initcond1 are accepted as obsolete
 
76
                 * spellings for sfunc, stype, initcond.
 
77
                 */
 
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);
 
94
                else
 
95
                        ereport(WARNING,
 
96
                                        (errcode(ERRCODE_SYNTAX_ERROR),
 
97
                                         errmsg("aggregate attribute \"%s\" not recognized",
 
98
                                                        defel->defname)));
 
99
        }
 
100
 
 
101
        /*
 
102
         * make sure we have our required definitions
 
103
         */
 
104
        if (baseType == NULL)
 
105
                ereport(ERROR,
 
106
                                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
 
107
                                 errmsg("aggregate basetype must be specified")));
 
108
        if (transType == NULL)
 
109
                ereport(ERROR,
 
110
                                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
 
111
                                 errmsg("aggregate stype must be specified")));
 
112
        if (transfuncName == NIL)
 
113
                ereport(ERROR,
 
114
                                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
 
115
                                 errmsg("aggregate sfunc must be specified")));
 
116
 
 
117
        /*
 
118
         * look up the aggregate's base type (input datatype) and transtype.
 
119
         *
 
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.
 
122
         *
 
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).
 
126
         */
 
127
        if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0)
 
128
                baseTypeId = ANYOID;
 
129
        else
 
130
                baseTypeId = typenameTypeId(baseType);
 
131
 
 
132
        transTypeId = typenameTypeId(transType);
 
133
        if (get_typtype(transTypeId) == 'p' &&
 
134
                transTypeId != ANYARRAYOID &&
 
135
                transTypeId != ANYELEMENTOID)
 
136
                ereport(ERROR,
 
137
                                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
 
138
                                 errmsg("aggregate transition data type cannot be %s",
 
139
                                                format_type_be(transTypeId))));
 
140
 
 
141
        /*
 
142
         * Most of the argument-checking is done inside of AggregateCreate
 
143
         */
 
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 */
 
151
}
 
152
 
 
153
 
 
154
/*
 
155
 * RemoveAggregate
 
156
 *              Deletes an aggregate.
 
157
 */
 
158
void
 
159
RemoveAggregate(RemoveAggrStmt *stmt)
 
160
{
 
161
        List       *aggName = stmt->aggname;
 
162
        TypeName   *aggType = stmt->aggtype;
 
163
        Oid                     basetypeID;
 
164
        Oid                     procOid;
 
165
        HeapTuple       tup;
 
166
        ObjectAddress object;
 
167
 
 
168
        /*
 
169
         * if a basetype is passed in, then attempt to find an aggregate for
 
170
         * that specific type.
 
171
         *
 
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).
 
174
         */
 
175
        if (aggType)
 
176
                basetypeID = typenameTypeId(aggType);
 
177
        else
 
178
                basetypeID = ANYOID;
 
179
 
 
180
        procOid = find_aggregate_func(aggName, basetypeID, false);
 
181
 
 
182
        /*
 
183
         * Find the function tuple, do permissions and validity checks
 
184
         */
 
185
        tup = SearchSysCache(PROCOID,
 
186
                                                 ObjectIdGetDatum(procOid),
 
187
                                                 0, 0, 0);
 
188
        if (!HeapTupleIsValid(tup)) /* should not happen */
 
189
                elog(ERROR, "cache lookup failed for function %u", procOid);
 
190
 
 
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,
 
194
                                                                 GetUserId()))
 
195
                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
 
196
                                           NameListToString(aggName));
 
197
 
 
198
        /* find_aggregate_func already checked it is an aggregate */
 
199
 
 
200
        ReleaseSysCache(tup);
 
201
 
 
202
        /*
 
203
         * Do the deletion
 
204
         */
 
205
        object.classId = RelOid_pg_proc;
 
206
        object.objectId = procOid;
 
207
        object.objectSubId = 0;
 
208
 
 
209
        performDeletion(&object, stmt->behavior);
 
210
}
 
211
 
 
212
 
 
213
void
 
214
RenameAggregate(List *name, TypeName *basetype, const char *newname)
 
215
{
 
216
        Oid                     basetypeOid;
 
217
        Oid                     procOid;
 
218
        Oid                     namespaceOid;
 
219
        HeapTuple       tup;
 
220
        Form_pg_proc procForm;
 
221
        Relation        rel;
 
222
        AclResult       aclresult;
 
223
 
 
224
        /*
 
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).
 
229
         */
 
230
        if (basetype)
 
231
                basetypeOid = typenameTypeId(basetype);
 
232
        else
 
233
                basetypeOid = ANYOID;
 
234
 
 
235
        rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
 
236
 
 
237
        procOid = find_aggregate_func(name, basetypeOid, false);
 
238
 
 
239
        tup = SearchSysCacheCopy(PROCOID,
 
240
                                                         ObjectIdGetDatum(procOid),
 
241
                                                         0, 0, 0);
 
242
        if (!HeapTupleIsValid(tup)) /* should not happen */
 
243
                elog(ERROR, "cache lookup failed for function %u", procOid);
 
244
        procForm = (Form_pg_proc) GETSTRUCT(tup);
 
245
 
 
246
        namespaceOid = procForm->pronamespace;
 
247
 
 
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)))
 
254
        {
 
255
                if (basetypeOid == ANYOID)
 
256
                        ereport(ERROR,
 
257
                                        (errcode(ERRCODE_DUPLICATE_FUNCTION),
 
258
                                 errmsg("function %s(*) already exists in schema \"%s\"",
 
259
                                                newname,
 
260
                                                get_namespace_name(namespaceOid))));
 
261
                else
 
262
                        ereport(ERROR,
 
263
                                        (errcode(ERRCODE_DUPLICATE_FUNCTION),
 
264
                                         errmsg("function %s already exists in schema \"%s\"",
 
265
                                                        funcname_signature_string(newname,
 
266
                                                                                                          procForm->pronargs,
 
267
                                                                                                  procForm->proargtypes),
 
268
                                                        get_namespace_name(namespaceOid))));
 
269
        }
 
270
 
 
271
        /* must be owner */
 
272
        if (!pg_proc_ownercheck(procOid, GetUserId()))
 
273
                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
 
274
                                           NameListToString(name));
 
275
 
 
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));
 
281
 
 
282
        /* rename */
 
283
        namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname);
 
284
        simple_heap_update(rel, &tup->t_self, tup);
 
285
        CatalogUpdateIndexes(rel, tup);
 
286
 
 
287
        heap_close(rel, NoLock);
 
288
        heap_freetuple(tup);
 
289
}
 
290
 
 
291
/*
 
292
 * Change aggregate owner
 
293
 */
 
294
void
 
295
AlterAggregateOwner(List *name, TypeName *basetype, AclId newOwnerSysId)
 
296
{
 
297
        Oid                     basetypeOid;
 
298
        Oid                     procOid;
 
299
        HeapTuple       tup;
 
300
        Form_pg_proc procForm;
 
301
        Relation        rel;
 
302
 
 
303
        /*
 
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).
 
308
         */
 
309
        if (basetype)
 
310
                basetypeOid = typenameTypeId(basetype);
 
311
        else
 
312
                basetypeOid = ANYOID;
 
313
 
 
314
        rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
 
315
 
 
316
        procOid = find_aggregate_func(name, basetypeOid, false);
 
317
 
 
318
        tup = SearchSysCacheCopy(PROCOID,
 
319
                                                         ObjectIdGetDatum(procOid),
 
320
                                                         0, 0, 0);
 
321
        if (!HeapTupleIsValid(tup)) /* should not happen */
 
322
                elog(ERROR, "cache lookup failed for function %u", procOid);
 
323
        procForm = (Form_pg_proc) GETSTRUCT(tup);
 
324
 
 
325
        /*
 
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.
 
328
         */
 
329
        if (procForm->proowner != newOwnerSysId)
 
330
        {
 
331
                /* Otherwise, must be superuser to change object ownership */
 
332
                if (!superuser())
 
333
                        ereport(ERROR,
 
334
                                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 
335
                                         errmsg("must be superuser to change owner")));
 
336
 
 
337
                /*
 
338
                 * Modify the owner --- okay to scribble on tup because it's a
 
339
                 * copy
 
340
                 */
 
341
                procForm->proowner = newOwnerSysId;
 
342
 
 
343
                simple_heap_update(rel, &tup->t_self, tup);
 
344
                CatalogUpdateIndexes(rel, tup);
 
345
        }
 
346
 
 
347
        heap_close(rel, NoLock);
 
348
        heap_freetuple(tup);
 
349
}