00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "postgres.h"
00016
00017 #include "access/heapam.h"
00018 #include "access/htup_details.h"
00019 #include "catalog/dependency.h"
00020 #include "catalog/indexing.h"
00021 #include "catalog/pg_aggregate.h"
00022 #include "catalog/pg_language.h"
00023 #include "catalog/pg_operator.h"
00024 #include "catalog/pg_proc.h"
00025 #include "catalog/pg_proc_fn.h"
00026 #include "catalog/pg_type.h"
00027 #include "miscadmin.h"
00028 #include "parser/parse_coerce.h"
00029 #include "parser/parse_func.h"
00030 #include "parser/parse_oper.h"
00031 #include "utils/acl.h"
00032 #include "utils/builtins.h"
00033 #include "utils/lsyscache.h"
00034 #include "utils/rel.h"
00035 #include "utils/syscache.h"
00036
00037
00038 static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
00039 Oid *rettype);
00040
00041
00042
00043
00044
00045 Oid
00046 AggregateCreate(const char *aggName,
00047 Oid aggNamespace,
00048 Oid *aggArgTypes,
00049 int numArgs,
00050 List *aggtransfnName,
00051 List *aggfinalfnName,
00052 List *aggsortopName,
00053 Oid aggTransType,
00054 const char *agginitval)
00055 {
00056 Relation aggdesc;
00057 HeapTuple tup;
00058 bool nulls[Natts_pg_aggregate];
00059 Datum values[Natts_pg_aggregate];
00060 Form_pg_proc proc;
00061 Oid transfn;
00062 Oid finalfn = InvalidOid;
00063 Oid sortop = InvalidOid;
00064 bool hasPolyArg;
00065 bool hasInternalArg;
00066 Oid rettype;
00067 Oid finaltype;
00068 Oid *fnArgs;
00069 int nargs_transfn;
00070 Oid procOid;
00071 TupleDesc tupDesc;
00072 int i;
00073 ObjectAddress myself,
00074 referenced;
00075 AclResult aclresult;
00076
00077
00078 if (!aggName)
00079 elog(ERROR, "no aggregate name supplied");
00080
00081 if (!aggtransfnName)
00082 elog(ERROR, "aggregate must have a transition function");
00083
00084
00085 hasPolyArg = false;
00086 hasInternalArg = false;
00087 for (i = 0; i < numArgs; i++)
00088 {
00089 if (IsPolymorphicType(aggArgTypes[i]))
00090 hasPolyArg = true;
00091 else if (aggArgTypes[i] == INTERNALOID)
00092 hasInternalArg = true;
00093 }
00094
00095
00096
00097
00098
00099 if (IsPolymorphicType(aggTransType) && !hasPolyArg)
00100 ereport(ERROR,
00101 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00102 errmsg("cannot determine transition data type"),
00103 errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
00104
00105
00106 nargs_transfn = numArgs + 1;
00107 fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid));
00108 fnArgs[0] = aggTransType;
00109 memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
00110 transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
00111 &rettype);
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123 if (rettype != aggTransType)
00124 ereport(ERROR,
00125 (errcode(ERRCODE_DATATYPE_MISMATCH),
00126 errmsg("return type of transition function %s is not %s",
00127 NameListToString(aggtransfnName),
00128 format_type_be(aggTransType))));
00129
00130 tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(transfn));
00131 if (!HeapTupleIsValid(tup))
00132 elog(ERROR, "cache lookup failed for function %u", transfn);
00133 proc = (Form_pg_proc) GETSTRUCT(tup);
00134
00135
00136
00137
00138
00139
00140 if (proc->proisstrict && agginitval == NULL)
00141 {
00142 if (numArgs < 1 ||
00143 !IsBinaryCoercible(aggArgTypes[0], aggTransType))
00144 ereport(ERROR,
00145 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00146 errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
00147 }
00148 ReleaseSysCache(tup);
00149
00150
00151 if (aggfinalfnName)
00152 {
00153 fnArgs[0] = aggTransType;
00154 finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs,
00155 &finaltype);
00156 }
00157 else
00158 {
00159
00160
00161
00162 finaltype = aggTransType;
00163 }
00164 Assert(OidIsValid(finaltype));
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174 if (IsPolymorphicType(finaltype) && !hasPolyArg)
00175 ereport(ERROR,
00176 (errcode(ERRCODE_DATATYPE_MISMATCH),
00177 errmsg("cannot determine result data type"),
00178 errdetail("An aggregate returning a polymorphic type "
00179 "must have at least one polymorphic argument.")));
00180
00181
00182
00183
00184
00185
00186
00187 if (finaltype == INTERNALOID && !hasInternalArg)
00188 ereport(ERROR,
00189 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00190 errmsg("unsafe use of pseudo-type \"internal\""),
00191 errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
00192
00193
00194 if (aggsortopName)
00195 {
00196 if (numArgs != 1)
00197 ereport(ERROR,
00198 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00199 errmsg("sort operator can only be specified for single-argument aggregates")));
00200 sortop = LookupOperName(NULL, aggsortopName,
00201 aggArgTypes[0], aggArgTypes[0],
00202 false, -1);
00203 }
00204
00205
00206
00207
00208 for (i = 0; i < numArgs; i++)
00209 {
00210 aclresult = pg_type_aclcheck(aggArgTypes[i], GetUserId(), ACL_USAGE);
00211 if (aclresult != ACLCHECK_OK)
00212 aclcheck_error_type(aclresult, aggArgTypes[i]);
00213 }
00214
00215 aclresult = pg_type_aclcheck(aggTransType, GetUserId(), ACL_USAGE);
00216 if (aclresult != ACLCHECK_OK)
00217 aclcheck_error_type(aclresult, aggTransType);
00218
00219 aclresult = pg_type_aclcheck(finaltype, GetUserId(), ACL_USAGE);
00220 if (aclresult != ACLCHECK_OK)
00221 aclcheck_error_type(aclresult, finaltype);
00222
00223
00224
00225
00226
00227
00228
00229 procOid = ProcedureCreate(aggName,
00230 aggNamespace,
00231 false,
00232 false,
00233 finaltype,
00234 GetUserId(),
00235 INTERNALlanguageId,
00236 InvalidOid,
00237 "aggregate_dummy",
00238 NULL,
00239 true,
00240 false,
00241 false,
00242
00243 false,
00244 false,
00245 PROVOLATILE_IMMUTABLE,
00246
00247 buildoidvector(aggArgTypes,
00248 numArgs),
00249 PointerGetDatum(NULL),
00250 PointerGetDatum(NULL),
00251 PointerGetDatum(NULL),
00252 NIL,
00253 PointerGetDatum(NULL),
00254 1,
00255 0);
00256
00257
00258
00259
00260
00261
00262 for (i = 0; i < Natts_pg_aggregate; i++)
00263 {
00264 nulls[i] = false;
00265 values[i] = (Datum) NULL;
00266 }
00267 values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
00268 values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
00269 values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
00270 values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
00271 values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
00272 if (agginitval)
00273 values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
00274 else
00275 nulls[Anum_pg_aggregate_agginitval - 1] = true;
00276
00277 aggdesc = heap_open(AggregateRelationId, RowExclusiveLock);
00278 tupDesc = aggdesc->rd_att;
00279
00280 tup = heap_form_tuple(tupDesc, values, nulls);
00281 simple_heap_insert(aggdesc, tup);
00282
00283 CatalogUpdateIndexes(aggdesc, tup);
00284
00285 heap_close(aggdesc, RowExclusiveLock);
00286
00287
00288
00289
00290
00291
00292 myself.classId = ProcedureRelationId;
00293 myself.objectId = procOid;
00294 myself.objectSubId = 0;
00295
00296
00297 referenced.classId = ProcedureRelationId;
00298 referenced.objectId = transfn;
00299 referenced.objectSubId = 0;
00300 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00301
00302
00303 if (OidIsValid(finalfn))
00304 {
00305 referenced.classId = ProcedureRelationId;
00306 referenced.objectId = finalfn;
00307 referenced.objectSubId = 0;
00308 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00309 }
00310
00311
00312 if (OidIsValid(sortop))
00313 {
00314 referenced.classId = OperatorRelationId;
00315 referenced.objectId = sortop;
00316 referenced.objectSubId = 0;
00317 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00318 }
00319
00320 return procOid;
00321 }
00322
00323
00324
00325
00326 static Oid
00327 lookup_agg_function(List *fnName,
00328 int nargs,
00329 Oid *input_types,
00330 Oid *rettype)
00331 {
00332 Oid fnOid;
00333 bool retset;
00334 int nvargs;
00335 Oid *true_oid_array;
00336 FuncDetailCode fdresult;
00337 AclResult aclresult;
00338 int i;
00339
00340
00341
00342
00343
00344
00345
00346
00347 fdresult = func_get_detail(fnName, NIL, NIL,
00348 nargs, input_types, false, false,
00349 &fnOid, rettype, &retset, &nvargs,
00350 &true_oid_array, NULL);
00351
00352
00353 if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
00354 ereport(ERROR,
00355 (errcode(ERRCODE_UNDEFINED_FUNCTION),
00356 errmsg("function %s does not exist",
00357 func_signature_string(fnName, nargs,
00358 NIL, input_types))));
00359 if (retset)
00360 ereport(ERROR,
00361 (errcode(ERRCODE_DATATYPE_MISMATCH),
00362 errmsg("function %s returns a set",
00363 func_signature_string(fnName, nargs,
00364 NIL, input_types))));
00365
00366
00367
00368
00369
00370
00371 *rettype = enforce_generic_type_consistency(input_types,
00372 true_oid_array,
00373 nargs,
00374 *rettype,
00375 true);
00376
00377
00378
00379
00380
00381 for (i = 0; i < nargs; i++)
00382 {
00383 if (!IsPolymorphicType(true_oid_array[i]) &&
00384 !IsBinaryCoercible(input_types[i], true_oid_array[i]))
00385 ereport(ERROR,
00386 (errcode(ERRCODE_DATATYPE_MISMATCH),
00387 errmsg("function %s requires run-time type coercion",
00388 func_signature_string(fnName, nargs,
00389 NIL, true_oid_array))));
00390 }
00391
00392
00393 aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE);
00394 if (aclresult != ACLCHECK_OK)
00395 aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fnOid));
00396
00397 return fnOid;
00398 }