1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_aggregate relation
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
13 *-------------------------------------------------------------------------
17 #include "access/heapam.h"
18 #include "catalog/dependency.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_aggregate.h"
21 #include "catalog/pg_language.h"
22 #include "catalog/pg_operator.h"
23 #include "catalog/pg_proc.h"
24 #include "catalog/pg_proc_fn.h"
25 #include "catalog/pg_type.h"
26 #include "miscadmin.h"
27 #include "parser/parse_coerce.h"
28 #include "parser/parse_func.h"
29 #include "parser/parse_oper.h"
30 #include "utils/acl.h"
31 #include "utils/builtins.h"
32 #include "utils/lsyscache.h"
33 #include "utils/rel.h"
34 #include "utils/syscache.h"
37 static Oid
lookup_agg_function(List
*fnName
, int nargs
, Oid
*input_types
,
45 AggregateCreate(const char *aggName
,
53 const char *agginitval
)
57 bool nulls
[Natts_pg_aggregate
];
58 Datum values
[Natts_pg_aggregate
];
61 Oid finalfn
= InvalidOid
; /* can be omitted */
62 Oid sortop
= InvalidOid
; /* can be omitted */
74 /* sanity checks (caller should have caught these) */
76 elog(ERROR
, "no aggregate name supplied");
79 elog(ERROR
, "aggregate must have a transition function");
81 /* check for polymorphic arguments */
83 for (i
= 0; i
< numArgs
; i
++)
85 if (IsPolymorphicType(aggArgTypes
[i
]))
93 * If transtype is polymorphic, must have polymorphic argument also; else
94 * we will have no way to deduce the actual transtype.
96 if (IsPolymorphicType(aggTransType
) && !hasPolyArg
)
98 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
99 errmsg("cannot determine transition data type"),
100 errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
102 /* find the transfn */
103 nargs_transfn
= numArgs
+ 1;
104 fnArgs
= (Oid
*) palloc(nargs_transfn
* sizeof(Oid
));
105 fnArgs
[0] = aggTransType
;
106 memcpy(fnArgs
+ 1, aggArgTypes
, numArgs
* sizeof(Oid
));
107 transfn
= lookup_agg_function(aggtransfnName
, nargs_transfn
, fnArgs
,
111 * Return type of transfn (possibly after refinement by
112 * enforce_generic_type_consistency, if transtype isn't polymorphic) must
113 * exactly match declared transtype.
115 * In the non-polymorphic-transtype case, it might be okay to allow a
116 * rettype that's binary-coercible to transtype, but I'm not quite
117 * convinced that it's either safe or useful. When transtype is
118 * polymorphic we *must* demand exact equality.
120 if (rettype
!= aggTransType
)
122 (errcode(ERRCODE_DATATYPE_MISMATCH
),
123 errmsg("return type of transition function %s is not %s",
124 NameListToString(aggtransfnName
),
125 format_type_be(aggTransType
))));
127 tup
= SearchSysCache(PROCOID
,
128 ObjectIdGetDatum(transfn
),
130 if (!HeapTupleIsValid(tup
))
131 elog(ERROR
, "cache lookup failed for function %u", transfn
);
132 proc
= (Form_pg_proc
) GETSTRUCT(tup
);
135 * If the transfn is strict and the initval is NULL, make sure first input
136 * type and transtype are the same (or at least binary-compatible), so
137 * that it's OK to use the first input value as the initial transValue.
139 if (proc
->proisstrict
&& agginitval
== NULL
)
142 !IsBinaryCoercible(aggArgTypes
[0], aggTransType
))
144 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
145 errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
147 ReleaseSysCache(tup
);
149 /* handle finalfn, if supplied */
152 fnArgs
[0] = aggTransType
;
153 finalfn
= lookup_agg_function(aggfinalfnName
, 1, fnArgs
,
159 * If no finalfn, aggregate result type is type of the state value
161 finaltype
= aggTransType
;
163 Assert(OidIsValid(finaltype
));
166 * If finaltype (i.e. aggregate return type) is polymorphic, inputs must
167 * be polymorphic also, else parser will fail to deduce result type.
168 * (Note: given the previous test on transtype and inputs, this cannot
169 * happen, unless someone has snuck a finalfn definition into the catalogs
170 * that itself violates the rule against polymorphic result with no
171 * polymorphic input.)
173 if (IsPolymorphicType(finaltype
) && !hasPolyArg
)
175 (errcode(ERRCODE_DATATYPE_MISMATCH
),
176 errmsg("cannot determine result data type"),
177 errdetail("An aggregate returning a polymorphic type "
178 "must have at least one polymorphic argument.")));
180 /* handle sortop, if supplied */
185 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
186 errmsg("sort operator can only be specified for single-argument aggregates")));
187 sortop
= LookupOperName(NULL
, aggsortopName
,
188 aggArgTypes
[0], aggArgTypes
[0],
193 * Everything looks okay. Try to create the pg_proc entry for the
194 * aggregate. (This could fail if there's already a conflicting entry.)
197 procOid
= ProcedureCreate(aggName
,
199 false, /* no replacement */
200 false, /* doesn't return a set */
201 finaltype
, /* returnType */
202 INTERNALlanguageId
, /* languageObjectId */
203 InvalidOid
, /* no validator */
204 "aggregate_dummy", /* placeholder proc */
207 false, /* security invoker (currently not
208 * definable for agg) */
209 false, /* isStrict (not needed for agg) */
210 PROVOLATILE_IMMUTABLE
, /* volatility (not
212 buildoidvector(aggArgTypes
,
213 numArgs
), /* paramTypes */
214 PointerGetDatum(NULL
), /* allParamTypes */
215 PointerGetDatum(NULL
), /* parameterModes */
216 PointerGetDatum(NULL
), /* parameterNames */
217 PointerGetDatum(NULL
), /* proconfig */
222 * Okay to create the pg_aggregate entry.
225 /* initialize nulls and values */
226 for (i
= 0; i
< Natts_pg_aggregate
; i
++)
229 values
[i
] = (Datum
) NULL
;
231 values
[Anum_pg_aggregate_aggfnoid
- 1] = ObjectIdGetDatum(procOid
);
232 values
[Anum_pg_aggregate_aggtransfn
- 1] = ObjectIdGetDatum(transfn
);
233 values
[Anum_pg_aggregate_aggfinalfn
- 1] = ObjectIdGetDatum(finalfn
);
234 values
[Anum_pg_aggregate_aggsortop
- 1] = ObjectIdGetDatum(sortop
);
235 values
[Anum_pg_aggregate_aggtranstype
- 1] = ObjectIdGetDatum(aggTransType
);
237 values
[Anum_pg_aggregate_agginitval
- 1] = CStringGetTextDatum(agginitval
);
239 nulls
[Anum_pg_aggregate_agginitval
- 1] = true;
241 aggdesc
= heap_open(AggregateRelationId
, RowExclusiveLock
);
242 tupDesc
= aggdesc
->rd_att
;
244 tup
= heap_form_tuple(tupDesc
, values
, nulls
);
245 simple_heap_insert(aggdesc
, tup
);
247 CatalogUpdateIndexes(aggdesc
, tup
);
249 heap_close(aggdesc
, RowExclusiveLock
);
252 * Create dependencies for the aggregate (above and beyond those already
253 * made by ProcedureCreate). Note: we don't need an explicit dependency
254 * on aggTransType since we depend on it indirectly through transfn.
256 myself
.classId
= ProcedureRelationId
;
257 myself
.objectId
= procOid
;
258 myself
.objectSubId
= 0;
260 /* Depends on transition function */
261 referenced
.classId
= ProcedureRelationId
;
262 referenced
.objectId
= transfn
;
263 referenced
.objectSubId
= 0;
264 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
266 /* Depends on final function, if any */
267 if (OidIsValid(finalfn
))
269 referenced
.classId
= ProcedureRelationId
;
270 referenced
.objectId
= finalfn
;
271 referenced
.objectSubId
= 0;
272 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
275 /* Depends on sort operator, if any */
276 if (OidIsValid(sortop
))
278 referenced
.classId
= OperatorRelationId
;
279 referenced
.objectId
= sortop
;
280 referenced
.objectSubId
= 0;
281 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
286 * lookup_agg_function -- common code for finding both transfn and finalfn
289 lookup_agg_function(List
*fnName
,
298 FuncDetailCode fdresult
;
303 * func_get_detail looks up the function in the catalogs, does
304 * disambiguation for polymorphic functions, handles inheritance, and
305 * returns the funcid and type and set or singleton status of the
306 * function's return value. it also returns the true argument types to
309 fdresult
= func_get_detail(fnName
, NIL
, nargs
, input_types
, false,
310 &fnOid
, rettype
, &retset
, &nvargs
,
313 /* only valid case is a normal function not returning a set */
314 if (fdresult
!= FUNCDETAIL_NORMAL
|| !OidIsValid(fnOid
))
316 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
317 errmsg("function %s does not exist",
318 func_signature_string(fnName
, nargs
, input_types
))));
321 (errcode(ERRCODE_DATATYPE_MISMATCH
),
322 errmsg("function %s returns a set",
323 func_signature_string(fnName
, nargs
, input_types
))));
326 * If there are any polymorphic types involved, enforce consistency, and
327 * possibly refine the result type. It's OK if the result is still
328 * polymorphic at this point, though.
330 *rettype
= enforce_generic_type_consistency(input_types
,
337 * func_get_detail will find functions requiring run-time argument type
338 * coercion, but nodeAgg.c isn't prepared to deal with that
340 for (i
= 0; i
< nargs
; i
++)
342 if (!IsPolymorphicType(true_oid_array
[i
]) &&
343 !IsBinaryCoercible(input_types
[i
], true_oid_array
[i
]))
345 (errcode(ERRCODE_DATATYPE_MISMATCH
),
346 errmsg("function %s requires run-time type coercion",
347 func_signature_string(fnName
, nargs
, true_oid_array
))));
350 /* Check aggregate creator has permission to call the function */
351 aclresult
= pg_proc_aclcheck(fnOid
, GetUserId(), ACL_EXECUTE
);
352 if (aclresult
!= ACLCHECK_OK
)
353 aclcheck_error(aclresult
, ACL_KIND_PROC
, get_func_name(fnOid
));