1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_aggregate relation
6 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.56 2002/09/18 21:35:20 tgl Exp $
13 *-------------------------------------------------------------------------
17 #include "access/heapam.h"
18 #include "catalog/catname.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/namespace.h"
22 #include "catalog/pg_aggregate.h"
23 #include "catalog/pg_language.h"
24 #include "catalog/pg_proc.h"
25 #include "optimizer/cost.h"
26 #include "parser/parse_coerce.h"
27 #include "parser/parse_func.h"
28 #include "utils/builtins.h"
29 #include "utils/syscache.h"
36 AggregateCreate(const char *aggName,
42 const char *agginitval)
46 char nulls[Natts_pg_aggregate];
47 Datum values[Natts_pg_aggregate];
50 Oid finalfn = InvalidOid; /* can be omitted */
52 Oid fnArgs[FUNC_MAX_ARGS];
62 elog(ERROR, "no aggregate name supplied");
65 elog(ERROR, "aggregate must have a transition function");
68 MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
69 fnArgs[0] = aggTransType;
70 if (aggBaseType == ANYOID)
74 fnArgs[1] = aggBaseType;
77 transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
78 if (!OidIsValid(transfn))
79 func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
80 tup = SearchSysCache(PROCOID,
81 ObjectIdGetDatum(transfn),
83 if (!HeapTupleIsValid(tup))
84 func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
85 proc = (Form_pg_proc) GETSTRUCT(tup);
86 if (proc->prorettype != aggTransType)
87 elog(ERROR, "return type of transition function %s is not %s",
88 NameListToString(aggtransfnName), format_type_be(aggTransType));
91 * If the transfn is strict and the initval is NULL, make sure input
92 * type and transtype are the same (or at least binary-compatible), so
93 * that it's OK to use the first input value as the initial
96 if (proc->proisstrict && agginitval == NULL)
98 if (!IsBinaryCoercible(aggBaseType, aggTransType))
99 elog(ERROR, "must not omit initval when transfn is strict and transtype is not compatible with input type");
101 ReleaseSysCache(tup);
103 /* handle finalfn, if supplied */
106 MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
107 fnArgs[0] = aggTransType;
108 finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
109 if (!OidIsValid(finalfn))
110 func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
111 tup = SearchSysCache(PROCOID,
112 ObjectIdGetDatum(finalfn),
114 if (!HeapTupleIsValid(tup))
115 func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
116 proc = (Form_pg_proc) GETSTRUCT(tup);
117 finaltype = proc->prorettype;
118 ReleaseSysCache(tup);
123 * If no finalfn, aggregate result type is type of the state value
125 finaltype = aggTransType;
127 Assert(OidIsValid(finaltype));
130 * Everything looks okay. Try to create the pg_proc entry for the
131 * aggregate. (This could fail if there's already a conflicting
134 MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
135 fnArgs[0] = aggBaseType;
137 procOid = ProcedureCreate(aggName,
139 false, /* no replacement */
140 false, /* doesn't return a set */
141 finaltype, /* returnType */
142 INTERNALlanguageId, /* languageObjectId */
144 "aggregate_dummy", /* placeholder proc */
147 false, /* security invoker (currently not
148 * definable for agg) */
149 false, /* isStrict (not needed for agg) */
150 PROVOLATILE_IMMUTABLE, /* volatility (not
152 1, /* parameterCount */
153 fnArgs); /* parameterTypes */
156 * Okay to create the pg_aggregate entry.
159 /* initialize nulls and values */
160 for (i = 0; i < Natts_pg_aggregate; i++)
163 values[i] = (Datum) NULL;
165 values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
166 values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
167 values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
168 values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
170 values[Anum_pg_aggregate_agginitval - 1] =
171 DirectFunctionCall1(textin, CStringGetDatum(agginitval));
173 nulls[Anum_pg_aggregate_agginitval - 1] = 'n';
175 aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock);
176 tupDesc = aggdesc->rd_att;
178 tup = heap_formtuple(tupDesc, values, nulls);
179 simple_heap_insert(aggdesc, tup);
181 CatalogUpdateIndexes(aggdesc, tup);
183 heap_close(aggdesc, RowExclusiveLock);
186 * Create dependencies for the aggregate (above and beyond those
187 * already made by ProcedureCreate). Note: we don't need an explicit
188 * dependency on aggTransType since we depend on it indirectly through
191 myself.classId = RelOid_pg_proc;
192 myself.objectId = procOid;
193 myself.objectSubId = 0;
195 /* Depends on transition function */
196 referenced.classId = RelOid_pg_proc;
197 referenced.objectId = transfn;
198 referenced.objectSubId = 0;
199 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
201 /* Depends on final function, if any */
202 if (OidIsValid(finalfn))
204 referenced.classId = RelOid_pg_proc;
205 referenced.objectId = finalfn;
206 referenced.objectSubId = 0;
207 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);