summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2002-04-11 20:00:18 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2002-04-11 20:00:18 +0000
commit902a6a0a4bc62d619a5ccd1ef0ff7fb3a5d897f1 (patch)
treec5cc85818d8a3ffae03a23bacd3e679945a41dbd /src/backend
parent3f6299df6c7d905bdef44eb3a4b19f248ebc14dc (diff)
downloadpostgresql-902a6a0a4bc62d619a5ccd1ef0ff7fb3a5d897f1.tar.gz
Restructure representation of aggregate functions so that they have pg_proc
entries, per pghackers discussion. This fixes aggregates to live in namespaces, and also simplifies/speeds up lookup in parse_func.c. Also, add a 'proimplicit' flag to pg_proc that controls whether a type coercion function may be invoked implicitly, or only explicitly. The current settings of these flags are more permissive than I would like, but we will need to debate and refine the behavior; for now, I avoided breaking regression tests as much as I could.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/aclchk.c30
-rw-r--r--src/backend/catalog/heap.c4
-rw-r--r--src/backend/catalog/indexing.c4
-rw-r--r--src/backend/catalog/pg_aggregate.c126
-rw-r--r--src/backend/catalog/pg_proc.c69
-rw-r--r--src/backend/commands/comment.c25
-rw-r--r--src/backend/commands/define.c91
-rw-r--r--src/backend/commands/indexcmds.c6
-rw-r--r--src/backend/commands/remove.c54
-rw-r--r--src/backend/executor/nodeAgg.c97
-rw-r--r--src/backend/nodes/copyfuncs.c8
-rw-r--r--src/backend/nodes/equalfuncs.c6
-rw-r--r--src/backend/nodes/outfuncs.c8
-rw-r--r--src/backend/nodes/readfuncs.c12
-rw-r--r--src/backend/optimizer/util/var.c6
-rw-r--r--src/backend/parser/parse_agg.c80
-rw-r--r--src/backend/parser/parse_coerce.c74
-rw-r--r--src/backend/parser/parse_expr.c5
-rw-r--r--src/backend/parser/parse_func.c357
-rw-r--r--src/backend/parser/parse_node.c11
-rw-r--r--src/backend/parser/parse_oper.c5
-rw-r--r--src/backend/parser/parse_target.c18
-rw-r--r--src/backend/rewrite/rewriteHandler.c4
-rw-r--r--src/backend/utils/Gen_fmgrtab.sh13
-rw-r--r--src/backend/utils/adt/ruleutils.c55
-rw-r--r--src/backend/utils/adt/sets.c11
-rw-r--r--src/backend/utils/adt/varchar.c15
-rw-r--r--src/backend/utils/cache/syscache.c18
28 files changed, 546 insertions, 666 deletions
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 04f98449bd..e440489a34 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.63 2002/04/11 05:32:02 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.64 2002/04/11 19:59:56 tgl Exp $
*
* NOTES
* See acl.h.
@@ -733,7 +733,7 @@ pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode)
Acl *acl;
/*
- * Validate userid, find out if he is superuser
+ * Validate userid, find out if he is superuser, also get usecatupd
*/
tuple = SearchSysCache(SHADOWSYSID,
ObjectIdGetDatum(userid),
@@ -1035,29 +1035,3 @@ pg_proc_ownercheck(Oid proc_oid, Oid userid)
return userid == owner_id;
}
-
-/*
- * Ownership check for an aggregate function (specified by OID).
- */
-bool
-pg_aggr_ownercheck(Oid aggr_oid, Oid userid)
-{
- HeapTuple tuple;
- AclId owner_id;
-
- /* Superusers bypass all permission checking. */
- if (superuser_arg(userid))
- return true;
-
- tuple = SearchSysCache(AGGOID,
- ObjectIdGetDatum(aggr_oid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "pg_aggr_ownercheck: aggregate %u not found", aggr_oid);
-
- owner_id = ((Form_pg_aggregate) GETSTRUCT(tuple))->aggowner;
-
- ReleaseSysCache(tuple);
-
- return userid == owner_id;
-}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 61688793cf..1d225cc082 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.194 2002/03/31 06:26:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.195 2002/04/11 19:59:56 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -1791,7 +1791,7 @@ cookDefault(ParseState *pstate,
if (type_id != atttypid)
{
if (CoerceTargetExpr(pstate, expr, type_id,
- atttypid, atttypmod) == NULL)
+ atttypid, atttypmod, false) == NULL)
elog(ERROR, "Column \"%s\" is of type %s"
" but default expression is of type %s"
"\n\tYou will need to rewrite or cast the expression",
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index 7ceed7890d..f0c7ef5a0d 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.87 2002/04/05 00:31:24 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.88 2002/04/11 19:59:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,7 +32,7 @@
*/
char *Name_pg_aggregate_indices[Num_pg_aggregate_indices] =
-{AggregateNameTypeIndex, AggregateOidIndex};
+{AggregateFnoidIndex};
char *Name_pg_am_indices[Num_pg_am_indices] =
{AmNameIndex, AmOidIndex};
char *Name_pg_amop_indices[Num_pg_amop_indices] =
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index a9f270fccf..951e45c5ff 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.43 2002/04/09 20:35:47 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.44 2002/04/11 19:59:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,15 +19,16 @@
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_aggregate.h"
+#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
-#include "catalog/pg_type.h"
-#include "miscadmin.h"
+#include "optimizer/cost.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
+
/*
* AggregateCreate
*/
@@ -50,7 +51,7 @@ AggregateCreate(const char *aggName,
Oid finaltype;
Oid fnArgs[FUNC_MAX_ARGS];
int nargs;
- NameData aname;
+ Oid procOid;
TupleDesc tupDesc;
int i;
@@ -61,15 +62,6 @@ AggregateCreate(const char *aggName,
if (!aggtransfnName)
elog(ERROR, "aggregate must have a transition function");
- /* make sure there is no existing agg of same name and base type */
- if (SearchSysCacheExists(AGGNAME,
- PointerGetDatum(aggName),
- ObjectIdGetDatum(aggBaseType),
- 0, 0))
- elog(ERROR,
- "aggregate function \"%s\" with base type %s already exists",
- aggName, typeidTypeName(aggBaseType));
-
/* handle transfn */
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
fnArgs[0] = aggTransType;
@@ -109,8 +101,8 @@ AggregateCreate(const char *aggName,
/* handle finalfn, if supplied */
if (aggfinalfnName)
{
+ MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
fnArgs[0] = aggTransType;
- fnArgs[1] = 0;
finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
if (!OidIsValid(finalfn))
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
@@ -132,21 +124,47 @@ AggregateCreate(const char *aggName,
}
Assert(OidIsValid(finaltype));
+ /*
+ * Everything looks okay. Try to create the pg_proc entry for the
+ * aggregate. (This could fail if there's already a conflicting entry.)
+ */
+ MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
+ fnArgs[0] = aggBaseType;
+
+ procOid = ProcedureCreate(aggName,
+ aggNamespace,
+ false, /* no replacement */
+ false, /* doesn't return a set */
+ finaltype, /* returnType */
+ INTERNALlanguageId, /* languageObjectId */
+ "aggregate_dummy", /* placeholder proc */
+ "-", /* probin */
+ true, /* isAgg */
+ true, /* (obsolete "trusted") */
+ false, /* isImplicit */
+ false, /* isStrict (not needed for agg) */
+ PROVOLATILE_IMMUTABLE, /* volatility (not needed for agg) */
+ BYTE_PCT, /* default cost values */
+ PERBYTE_CPU,
+ PERCALL_CPU,
+ OUTIN_RATIO,
+ 1, /* parameterCount */
+ fnArgs); /* parameterTypes */
+
+ /*
+ * Okay to create the pg_aggregate entry.
+ */
+
/* initialize nulls and values */
for (i = 0; i < Natts_pg_aggregate; i++)
{
nulls[i] = ' ';
values[i] = (Datum) NULL;
}
- namestrcpy(&aname, aggName);
- values[Anum_pg_aggregate_aggname - 1] = NameGetDatum(&aname);
- values[Anum_pg_aggregate_aggowner - 1] = Int32GetDatum(GetUserId());
+ values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
- values[Anum_pg_aggregate_aggbasetype - 1] = ObjectIdGetDatum(aggBaseType);
values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
- values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(finaltype);
-
if (agginitval)
values[Anum_pg_aggregate_agginitval - 1] =
DirectFunctionCall1(textin, CStringGetDatum(agginitval));
@@ -155,12 +173,9 @@ AggregateCreate(const char *aggName,
aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock);
tupDesc = aggdesc->rd_att;
- if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
- values,
- nulls)))
- elog(ERROR, "AggregateCreate: heap_formtuple failed");
- if (!OidIsValid(heap_insert(aggdesc, tup)))
- elog(ERROR, "AggregateCreate: heap_insert failed");
+
+ tup = heap_formtuple(tupDesc, values, nulls);
+ heap_insert(aggdesc, tup);
if (RelationGetForm(aggdesc)->relhasindex)
{
@@ -173,62 +188,3 @@ AggregateCreate(const char *aggName,
heap_close(aggdesc, RowExclusiveLock);
}
-
-Datum
-AggNameGetInitVal(char *aggName, Oid basetype, bool *isNull)
-{
- HeapTuple tup;
- Oid transtype,
- typinput,
- typelem;
- Datum textInitVal;
- char *strInitVal;
- Datum initVal;
-
- Assert(PointerIsValid(aggName));
- Assert(PointerIsValid(isNull));
-
- tup = SearchSysCache(AGGNAME,
- PointerGetDatum(aggName),
- ObjectIdGetDatum(basetype),
- 0, 0);
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
- aggName);
- transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype;
-
- /*
- * initval is potentially null, so don't try to access it as a struct
- * field. Must do it the hard way with SysCacheGetAttr.
- */
- textInitVal = SysCacheGetAttr(AGGNAME, tup,
- Anum_pg_aggregate_agginitval,
- isNull);
- if (*isNull)
- {
- ReleaseSysCache(tup);
- return (Datum) 0;
- }
-
- strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal));
-
- ReleaseSysCache(tup);
-
- tup = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(transtype),
- 0, 0, 0);
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type %u", transtype);
-
- typinput = ((Form_pg_type) GETSTRUCT(tup))->typinput;
- typelem = ((Form_pg_type) GETSTRUCT(tup))->typelem;
- ReleaseSysCache(tup);
-
- initVal = OidFunctionCall3(typinput,
- CStringGetDatum(strInitVal),
- ObjectIdGetDatum(typelem),
- Int32GetDatum(-1));
-
- pfree(strInitVal);
- return initVal;
-}
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 8cb89b979c..e9fcdc5d8e 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.69 2002/04/09 20:35:47 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.70 2002/04/11 19:59:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,7 +19,6 @@
#include "catalog/indexing.h"
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
-#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "parser/parse_coerce.h"
@@ -48,24 +47,25 @@ ProcedureCreate(const char *procedureName,
Oid languageObjectId,
const char *prosrc,
const char *probin,
+ bool isAgg,
bool trusted,
+ bool isImplicit,
bool isStrict,
char volatility,
int32 byte_pct,
int32 perbyte_cpu,
int32 percall_cpu,
int32 outin_ratio,
- List *argList)
+ int parameterCount,
+ const Oid *parameterTypes)
{
int i;
Relation rel;
HeapTuple tup;
HeapTuple oldtup;
- uint16 parameterCount;
char nulls[Natts_pg_proc];
Datum values[Natts_pg_proc];
char replaces[Natts_pg_proc];
- List *x;
List *querytree_list;
Oid typev[FUNC_MAX_ARGS];
Oid relid;
@@ -79,43 +79,14 @@ ProcedureCreate(const char *procedureName,
Assert(PointerIsValid(prosrc));
Assert(PointerIsValid(probin));
- parameterCount = 0;
- MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
- foreach(x, argList)
- {
- TypeName *t = (TypeName *) lfirst(x);
- Oid toid;
-
- if (parameterCount >= FUNC_MAX_ARGS)
- elog(ERROR, "functions cannot have more than %d arguments",
- FUNC_MAX_ARGS);
-
- toid = LookupTypeName(t);
- if (OidIsValid(toid))
- {
- if (!get_typisdefined(toid))
- elog(WARNING, "Argument type \"%s\" is only a shell",
- TypeNameToString(t));
- }
- else
- {
- char *typnam = TypeNameToString(t);
-
- if (strcmp(typnam, "opaque") == 0)
- {
- if (languageObjectId == SQLlanguageId)
- elog(ERROR, "SQL functions cannot have arguments of type \"opaque\"");
- toid = InvalidOid;
- }
- else
- elog(ERROR, "Type \"%s\" does not exist", typnam);
- }
+ if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)
+ elog(ERROR, "functions cannot have more than %d arguments",
+ FUNC_MAX_ARGS);
- if (t->setof)
- elog(ERROR, "functions cannot accept set arguments");
-
- typev[parameterCount++] = toid;
- }
+ /* Make sure we have a zero-padded param type array */
+ MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
+ if (parameterCount > 0)
+ memcpy(typev, parameterTypes, parameterCount * sizeof(Oid));
if (languageObjectId == SQLlanguageId)
{
@@ -248,12 +219,13 @@ ProcedureCreate(const char *procedureName,
values[i++] = ObjectIdGetDatum(procNamespace); /* pronamespace */
values[i++] = Int32GetDatum(GetUserId()); /* proowner */
values[i++] = ObjectIdGetDatum(languageObjectId); /* prolang */
- values[i++] = BoolGetDatum(false); /* proisinh (unused) */
+ values[i++] = BoolGetDatum(isAgg); /* proisagg */
values[i++] = BoolGetDatum(trusted); /* proistrusted */
+ values[i++] = BoolGetDatum(isImplicit); /* proimplicit */
values[i++] = BoolGetDatum(isStrict); /* proisstrict */
+ values[i++] = BoolGetDatum(returnsSet); /* proretset */
values[i++] = CharGetDatum(volatility); /* provolatile */
values[i++] = UInt16GetDatum(parameterCount); /* pronargs */
- values[i++] = BoolGetDatum(returnsSet); /* proretset */
values[i++] = ObjectIdGetDatum(returnType); /* prorettype */
values[i++] = PointerGetDatum(typev); /* proargtypes */
values[i++] = Int32GetDatum(byte_pct); /* probyte_pct */
@@ -298,6 +270,17 @@ ProcedureCreate(const char *procedureName,
elog(ERROR, "ProcedureCreate: cannot change return type of existing function."
"\n\tUse DROP FUNCTION first.");
+ /* Can't change aggregate status, either */
+ if (oldproc->proisagg != isAgg)
+ {
+ if (oldproc->proisagg)
+ elog(ERROR, "function %s is an aggregate",
+ procedureName);
+ else
+ elog(ERROR, "function %s is not an aggregate",
+ procedureName);
+ }
+
/* do not change existing permissions, either */
replaces[Anum_pg_proc_proacl-1] = ' ';
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index b7d57f6cce..b7de77067d 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -7,7 +7,7 @@
* Copyright (c) 1999-2001, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.39 2002/04/09 20:35:47 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.40 2002/04/11 19:59:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,15 +25,11 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_trigger.h"
-#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "miscadmin.h"
-#include "nodes/makefuncs.h"
-#include "parser/parse_agg.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "parser/parse.h"
-#include "rewrite/rewriteRemove.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -573,7 +569,6 @@ CommentAggregate(List *aggregate, List *arguments, char *comment)
TypeName *aggtype = (TypeName *) lfirst(arguments);
Oid baseoid,
oid;
- Oid classoid;
/* First, attempt to determine the base aggregate oid */
if (aggtype)
@@ -581,18 +576,13 @@ CommentAggregate(List *aggregate, List *arguments, char *comment)
else
baseoid = InvalidOid;
- /* Now, attempt to find the actual tuple in pg_aggregate */
+ /* Now, attempt to find the actual tuple in pg_proc */
- oid = GetSysCacheOid(AGGNAME,
- PointerGetDatum(strVal(lfirst(aggregate))), /* XXX */
- ObjectIdGetDatum(baseoid),
- 0, 0);
- if (!OidIsValid(oid))
- agg_error("CommentAggregate", aggregate, baseoid);
+ oid = find_aggregate_func("CommentAggregate", aggregate, baseoid);
/* Next, validate the user's attempt to comment */
- if (!pg_aggr_ownercheck(oid, GetUserId()))
+ if (!pg_proc_ownercheck(oid, GetUserId()))
{
if (baseoid == InvalidOid)
elog(ERROR, "you are not permitted to comment on aggregate %s for all types",
@@ -602,14 +592,9 @@ CommentAggregate(List *aggregate, List *arguments, char *comment)
NameListToString(aggregate), format_type_be(baseoid));
}
- /* pg_aggregate doesn't have a hard-coded OID, so must look it up */
-
- classoid = get_relname_relid(AggregateRelationName, PG_CATALOG_NAMESPACE);
- Assert(OidIsValid(classoid));
-
/* Call CreateComments() to create/drop the comments */
- CreateComments(oid, classoid, 0, comment);
+ CreateComments(oid, RelOid_pg_proc, 0, comment);
}
/*
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index cccbcdfaa5..692fc9f957 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.74 2002/04/09 20:35:47 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.75 2002/04/11 19:59:57 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -141,13 +141,56 @@ compute_return_type(TypeName *returnType, Oid languageOid,
*returnsSet_p = returnType->setof;
}
-
-static void
-compute_full_attributes(List *parameters,
- int32 *byte_pct_p, int32 *perbyte_cpu_p,
- int32 *percall_cpu_p, int32 *outin_ratio_p,
- bool *isStrict_p, char *volatility_p)
+/*
+ * Interpret the argument-types list of the CREATE FUNCTION statement.
+ */
+static int
+compute_parameter_types(List *argTypes, Oid languageOid,
+ Oid *parameterTypes)
{
+ int parameterCount = 0;
+ List *x;
+
+ MemSet(parameterTypes, 0, FUNC_MAX_ARGS * sizeof(Oid));
+ foreach(x, argTypes)
+ {
+ TypeName *t = (TypeName *) lfirst(x);
+ Oid toid;
+
+ if (parameterCount >= FUNC_MAX_ARGS)
+ elog(ERROR, "functions cannot have more than %d arguments",
+ FUNC_MAX_ARGS);
+
+ toid = LookupTypeName(t);
+ if (OidIsValid(toid))
+ {
+ if (!get_typisdefined(toid))
+ elog(WARNING, "Argument type \"%s\" is only a shell",
+ TypeNameToString(t));
+ }
+ else
+ {
+ char *typnam = TypeNameToString(t);
+
+ if (strcmp(typnam, "opaque") == 0)
+ {
+ if (languageOid == SQLlanguageId)
+ elog(ERROR, "SQL functions cannot have arguments of type \"opaque\"");
+ toid = InvalidOid;
+ }
+ else
+ elog(ERROR, "Type \"%s\" does not exist", typnam);
+ }
+
+ if (t->setof)
+ elog(ERROR, "functions cannot accept set arguments");
+
+ parameterTypes[parameterCount++] = toid;
+ }
+
+ return parameterCount;
+}
+
/*-------------
* Interpret the parameters *parameters and return their contents as
* *byte_pct_p, etc.
@@ -155,7 +198,10 @@ compute_full_attributes(List *parameters,
* These parameters supply optional information about a function.
* All have defaults if not specified.
*
- * Note: currently, only two of these parameters actually do anything:
+ * Note: currently, only three of these parameters actually do anything:
+ *
+ * * isImplicit means the function may be used as an implicit type
+ * coercion.
*
* * isStrict means the function should not be called when any NULL
* inputs are present; instead a NULL result value should be assumed.
@@ -168,6 +214,13 @@ compute_full_attributes(List *parameters,
* for a long time.
*------------
*/
+static void
+compute_full_attributes(List *parameters,
+ int32 *byte_pct_p, int32 *perbyte_cpu_p,
+ int32 *percall_cpu_p, int32 *outin_ratio_p,
+ bool *isImplicit_p, bool *isStrict_p,
+ char *volatility_p)
+{
List *pl;
/* the defaults */
@@ -175,6 +228,7 @@ compute_full_attributes(List *parameters,
*perbyte_cpu_p = PERBYTE_CPU;
*percall_cpu_p = PERCALL_CPU;
*outin_ratio_p = OUTIN_RATIO;
+ *isImplicit_p = false;
*isStrict_p = false;
*volatility_p = PROVOLATILE_VOLATILE;
@@ -182,7 +236,9 @@ compute_full_attributes(List *parameters,
{
DefElem *param = (DefElem *) lfirst(pl);
- if (strcasecmp(param->defname, "isstrict") == 0)
+ if (strcasecmp(param->defname, "implicitcoercion") == 0)
+ *isImplicit_p = true;
+ else if (strcasecmp(param->defname, "isstrict") == 0)
*isStrict_p = true;
else if (strcasecmp(param->defname, "isimmutable") == 0)
*volatility_p = PROVOLATILE_IMMUTABLE;
@@ -276,11 +332,14 @@ CreateFunction(ProcedureStmt *stmt)
Oid languageOid;
char *funcname;
Oid namespaceId;
+ int parameterCount;
+ Oid parameterTypes[FUNC_MAX_ARGS];
int32 byte_pct,
perbyte_cpu,
percall_cpu,
outin_ratio;
- bool isStrict;
+ bool isImplicit,
+ isStrict;
char volatility;
HeapTuple languageTuple;
Form_pg_language languageStruct;
@@ -316,9 +375,13 @@ CreateFunction(ProcedureStmt *stmt)
compute_return_type(stmt->returnType, languageOid,
&prorettype, &returnsSet);
+ parameterCount = compute_parameter_types(stmt->argTypes, languageOid,
+ parameterTypes);
+
compute_full_attributes(stmt->withClause,
&byte_pct, &perbyte_cpu, &percall_cpu,
- &outin_ratio, &isStrict, &volatility);
+ &outin_ratio, &isImplicit, &isStrict,
+ &volatility);
interpret_AS_clause(languageOid, languageName, stmt->as,
&prosrc_str, &probin_str);
@@ -335,18 +398,20 @@ CreateFunction(ProcedureStmt *stmt)
languageOid,
prosrc_str, /* converted to text later */
probin_str, /* converted to text later */
+ false, /* not an aggregate */
true, /* (obsolete "trusted") */
+ isImplicit,
isStrict,
volatility,
byte_pct,
perbyte_cpu,
percall_cpu,
outin_ratio,
- stmt->argTypes);
+ parameterCount,
+ parameterTypes);
}
-
/*
* DefineOperator
* this function extracts all the information from the
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 43539cd625..0a8ddc1807 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.68 2002/04/09 20:35:47 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.69 2002/04/11 19:59:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -303,7 +303,9 @@ FuncIndexArgs(IndexInfo *indexInfo,
&true_typeids);
if (fdresult != FUNCDETAIL_NORMAL)
{
- if (fdresult == FUNCDETAIL_COERCION)
+ if (fdresult == FUNCDETAIL_AGGREGATE)
+ elog(ERROR, "DefineIndex: functional index may not use an aggregate function");
+ else if (fdresult == FUNCDETAIL_COERCION)
elog(ERROR, "DefineIndex: functional index must use a real function, not a type coercion"
"\n\tTry specifying the index opclass you want to use, instead");
else
diff --git a/src/backend/commands/remove.c b/src/backend/commands/remove.c
index 8969b9cdc1..c32d2b215c 100644
--- a/src/backend/commands/remove.c
+++ b/src/backend/commands/remove.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.73 2002/04/09 20:35:48 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.74 2002/04/11 19:59:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -24,7 +24,6 @@
#include "commands/defrem.h"
#include "miscadmin.h"
#include "parser/parse.h"
-#include "parser/parse_agg.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
@@ -381,6 +380,11 @@ RemoveFunction(List *functionName, /* function name to be removed */
elog(ERROR, "RemoveFunction: function '%s': permission denied",
NameListToString(functionName));
+ if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
+ elog(ERROR, "RemoveFunction: function '%s' is an aggregate"
+ "\n\tUse DROP AGGREGATE to remove it",
+ NameListToString(functionName));
+
if (((Form_pg_proc) GETSTRUCT(tup))->prolang == INTERNALlanguageId)
{
/* "Helpful" WARNING when removing a builtin function ... */
@@ -404,6 +408,7 @@ RemoveAggregate(List *aggName, TypeName *aggType)
Relation relation;
HeapTuple tup;
Oid basetypeID;
+ Oid procOid;
/*
* if a basetype is passed in, then attempt to find an aggregate for
@@ -413,23 +418,16 @@ RemoveAggregate(List *aggName, TypeName *aggType)
* a basetype of zero. This is valid. It means that the aggregate is
* to apply to all basetypes (eg, COUNT).
*/
-
if (aggType)
basetypeID = typenameTypeId(aggType);
else
basetypeID = InvalidOid;
- relation = heap_openr(AggregateRelationName, RowExclusiveLock);
+ procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID);
- tup = SearchSysCache(AGGNAME,
- PointerGetDatum(strVal(llast(aggName))),
- ObjectIdGetDatum(basetypeID),
- 0, 0);
+ /* Permission check */
- if (!HeapTupleIsValid(tup))
- agg_error("RemoveAggregate", aggName, basetypeID);
-
- if (!pg_aggr_ownercheck(tup->t_data->t_oid, GetUserId()))
+ if (!pg_proc_ownercheck(procOid, GetUserId()))
{
if (basetypeID == InvalidOid)
elog(ERROR, "RemoveAggregate: aggregate %s for all types: permission denied",
@@ -439,8 +437,36 @@ RemoveAggregate(List *aggName, TypeName *aggType)
NameListToString(aggName), format_type_be(basetypeID));
}
- /* Remove any comments related to this aggregate */
- DeleteComments(tup->t_data->t_oid, RelationGetRelid(relation));
+ /* Remove the pg_proc tuple */
+
+ relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
+
+ tup = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(procOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "RemoveAggregate: couldn't find pg_proc tuple for %s",
+ NameListToString(aggName));
+
+ /* Delete any comments associated with this function */
+ DeleteComments(procOid, RelationGetRelid(relation));
+
+ simple_heap_delete(relation, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(relation, RowExclusiveLock);
+
+ /* Remove the pg_aggregate tuple */
+
+ relation = heap_openr(AggregateRelationName, RowExclusiveLock);
+
+ tup = SearchSysCache(AGGFNOID,
+ ObjectIdGetDatum(procOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "RemoveAggregate: couldn't find pg_aggregate tuple for %s",
+ NameListToString(aggName));
simple_heap_delete(relation, &tup->t_self);
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index e7da9e5af5..3ccbcd8efc 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -46,7 +46,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.80 2002/03/20 19:43:54 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.81 2002/04/11 19:59:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -63,11 +63,13 @@
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
+#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/tuplesort.h"
#include "utils/datum.h"
+
/*
* AggStatePerAggData - per-aggregate working state for the Agg scan
*/
@@ -160,6 +162,7 @@ static void process_sorted_aggregate(AggState *aggstate,
AggStatePerAgg peraggstate);
static void finalize_aggregate(AggStatePerAgg peraggstate,
Datum *resultVal, bool *resultIsNull);
+static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
/*
@@ -244,7 +247,7 @@ advance_transition_function(AggStatePerAgg peraggstate,
* transValue has not been initialized. This is the first
* non-NULL input value. We use it as the initial value for
* transValue. (We already checked that the agg's input type
- * is binary- compatible with its transtype, so straight copy
+ * is binary-compatible with its transtype, so straight copy
* here is OK.)
*
* We had better copy the datum if it is pass-by-ref, since the
@@ -838,11 +841,11 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
{
Aggref *aggref = (Aggref *) lfirst(alist);
AggStatePerAgg peraggstate = &peragg[++aggno];
- char *aggname = aggref->aggname;
HeapTuple aggTuple;
Form_pg_aggregate aggform;
Oid transfn_oid,
finalfn_oid;
+ Datum textInitVal;
/* Mark Aggref node with its associated index in the result array */
aggref->aggno = aggno;
@@ -850,28 +853,34 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
/* Fill in the peraggstate data */
peraggstate->aggref = aggref;
- aggTuple = SearchSysCache(AGGNAME,
- PointerGetDatum(aggname),
- ObjectIdGetDatum(aggref->basetype),
- 0, 0);
+ aggTuple = SearchSysCache(AGGFNOID,
+ ObjectIdGetDatum(aggref->aggfnoid),
+ 0, 0, 0);
if (!HeapTupleIsValid(aggTuple))
- elog(ERROR, "ExecAgg: cache lookup failed for aggregate %s(%s)",
- aggname,
- aggref->basetype ?
- typeidTypeName(aggref->basetype) : (char *) "");
+ elog(ERROR, "ExecAgg: cache lookup failed for aggregate %u",
+ aggref->aggfnoid);
aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
- get_typlenbyval(aggform->aggfinaltype,
+ get_typlenbyval(aggref->aggtype,
&peraggstate->resulttypeLen,
&peraggstate->resulttypeByVal);
get_typlenbyval(aggform->aggtranstype,
&peraggstate->transtypeLen,
&peraggstate->transtypeByVal);
- peraggstate->initValue =
- AggNameGetInitVal(aggname,
- aggform->aggbasetype,
- &peraggstate->initValueIsNull);
+ /*
+ * initval is potentially null, so don't try to access it as a struct
+ * field. Must do it the hard way with SysCacheGetAttr.
+ */
+ textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple,
+ Anum_pg_aggregate_agginitval,
+ &peraggstate->initValueIsNull);
+
+ if (peraggstate->initValueIsNull)
+ peraggstate->initValue = (Datum) 0;
+ else
+ peraggstate->initValue = GetAggInitVal(textInitVal,
+ aggform->aggtranstype);
peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
@@ -891,21 +900,21 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
{
/*
* Note: use the type from the input expression here, not
- * aggform->aggbasetype, because the latter might be 0.
+ * from pg_proc.proargtypes, because the latter might be 0.
* (Consider COUNT(*).)
*/
Oid inputType = exprType(aggref->target);
if (!IsBinaryCompatible(inputType, aggform->aggtranstype))
- elog(ERROR, "Aggregate %s needs to have compatible input type and transition type",
- aggname);
+ elog(ERROR, "Aggregate %u needs to have compatible input type and transition type",
+ aggref->aggfnoid);
}
if (aggref->aggdistinct)
{
/*
* Note: use the type from the input expression here, not
- * aggform->aggbasetype, because the latter might be 0.
+ * from pg_proc.proargtypes, because the latter might be 0.
* (Consider COUNT(*).)
*/
Oid inputType = exprType(aggref->target);
@@ -932,6 +941,36 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
return TRUE;
}
+static Datum
+GetAggInitVal(Datum textInitVal, Oid transtype)
+{
+ char *strInitVal;
+ HeapTuple tup;
+ Oid typinput,
+ typelem;
+ Datum initVal;
+
+ strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal));
+
+ tup = SearchSysCache(TYPEOID,
+ ObjectIdGetDatum(transtype),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "GetAggInitVal: cache lookup failed on aggregate transition function return type %u", transtype);
+
+ typinput = ((Form_pg_type) GETSTRUCT(tup))->typinput;
+ typelem = ((Form_pg_type) GETSTRUCT(tup))->typelem;
+ ReleaseSysCache(tup);
+
+ initVal = OidFunctionCall3(typinput,
+ CStringGetDatum(strInitVal),
+ ObjectIdGetDatum(typelem),
+ Int32GetDatum(-1));
+
+ pfree(strInitVal);
+ return initVal;
+}
+
int
ExecCountSlotsAgg(Agg *node)
{
@@ -985,3 +1024,21 @@ ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent)
if (((Plan *) node)->lefttree->chgParam == NULL)
ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
}
+
+/*
+ * aggregate_dummy - dummy execution routine for aggregate functions
+ *
+ * This function is listed as the implementation (prosrc field) of pg_proc
+ * entries for aggregate functions. Its only purpose is to throw an error
+ * if someone mistakenly executes such a function in the normal way.
+ *
+ * Perhaps someday we could assign real meaning to the prosrc field of
+ * an aggregate?
+ */
+Datum
+aggregate_dummy(PG_FUNCTION_ARGS)
+{
+ elog(ERROR, "Aggregate function %u called as normal function",
+ fcinfo->flinfo->fn_oid);
+ return (Datum) 0; /* keep compiler quiet */
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 873658774c..5eed3a33f5 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.176 2002/04/09 20:35:49 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.177 2002/04/11 19:59:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -852,11 +852,7 @@ _copyAggref(Aggref *from)
{
Aggref *newnode = makeNode(Aggref);
- /*
- * copy remainder of node
- */
- newnode->aggname = pstrdup(from->aggname);
- newnode->basetype = from->basetype;
+ newnode->aggfnoid = from->aggfnoid;
newnode->aggtype = from->aggtype;
Node_Copy(from, newnode, target);
newnode->aggstar = from->aggstar;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 9458ebc5b9..cafd77a822 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.124 2002/04/09 20:35:50 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.125 2002/04/11 19:59:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -220,9 +220,7 @@ _equalFunc(Func *a, Func *b)
static bool
_equalAggref(Aggref *a, Aggref *b)
{
- if (strcmp(a->aggname, b->aggname) != 0)
- return false;
- if (a->basetype != b->basetype)
+ if (a->aggfnoid != b->aggfnoid)
return false;
if (a->aggtype != b->aggtype)
return false;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index a495f5ed10..09a3eb767f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.153 2002/04/09 20:35:50 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.154 2002/04/11 19:59:59 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@@ -785,10 +785,8 @@ _outConst(StringInfo str, Const *node)
static void
_outAggref(StringInfo str, Aggref *node)
{
- appendStringInfo(str, " AGGREG :aggname ");
- _outToken(str, node->aggname);
- appendStringInfo(str, " :basetype %u :aggtype %u :target ",
- node->basetype, node->aggtype);
+ appendStringInfo(str, " AGGREG :aggfnoid %u :aggtype %u :target ",
+ node->aggfnoid, node->aggtype);
_outNode(str, node->target);
appendStringInfo(str, " :aggstar %s :aggdistinct %s ",
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 150e98d6e4..f91ba36e5d 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.118 2002/03/22 02:56:32 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.119 2002/04/11 20:00:00 tgl Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
@@ -1159,13 +1159,9 @@ _readAggref(void)
local_node = makeNode(Aggref);
- token = pg_strtok(&length); /* eat :aggname */
- token = pg_strtok(&length); /* get aggname */
- local_node->aggname = debackslash(token, length);
-
- token = pg_strtok(&length); /* eat :basetype */
- token = pg_strtok(&length); /* get basetype */
- local_node->basetype = atooid(token);
+ token = pg_strtok(&length); /* eat :aggfnoid */
+ token = pg_strtok(&length); /* get aggfnoid */
+ local_node->aggfnoid = atooid(token);
token = pg_strtok(&length); /* eat :aggtype */
token = pg_strtok(&length); /* get aggtype */
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 568e024d20..81aad81c0f 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.34 2002/03/12 00:51:51 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.35 2002/04/11 20:00:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -485,7 +485,7 @@ flatten_join_alias_var(Var *var, Query *root, int expandRTI)
if (subtype != vartype)
{
l_var = coerce_type(NULL, l_var, subtype,
- vartype, vartypmod);
+ vartype, vartypmod, false);
l_var = coerce_type_typmod(NULL, l_var,
vartype, vartypmod);
}
@@ -504,7 +504,7 @@ flatten_join_alias_var(Var *var, Query *root, int expandRTI)
if (subtype != vartype)
{
r_var = coerce_type(NULL, r_var, subtype,
- vartype, vartypmod);
+ vartype, vartypmod, false);
r_var = coerce_type_typmod(NULL, r_var,
vartype, vartypmod);
}
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 3812579a4d..114296baf2 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -8,24 +8,17 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.48 2002/04/09 20:35:52 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.49 2002/04/11 20:00:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
-
#include "postgres.h"
-#include "catalog/namespace.h"
-#include "catalog/pg_aggregate.h"
+
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "parser/parse_agg.h"
-#include "parser/parse_coerce.h"
-#include "parser/parse_expr.h"
#include "parser/parsetree.h"
-#include "parser/parse_type.h"
-#include "utils/builtins.h"
-#include "utils/lsyscache.h"
-#include "utils/syscache.h"
+
typedef struct
{
@@ -185,70 +178,3 @@ parseCheckAggregates(ParseState *pstate, Query *qry, Node *qual)
/* Release the list storage (but not the pointed-to expressions!) */
freeList(groupClauses);
}
-
-
-Aggref *
-ParseAgg(ParseState *pstate, List *aggname, Oid basetype,
- List *args, bool agg_star, bool agg_distinct)
-{
- HeapTuple aggtuple;
- Form_pg_aggregate aggform;
- Aggref *aggref;
-
- aggtuple = SearchSysCache(AGGNAME,
- PointerGetDatum(strVal(llast(aggname))),
- ObjectIdGetDatum(basetype),
- 0, 0);
- /* shouldn't happen --- caller should have checked already */
- if (!HeapTupleIsValid(aggtuple))
- agg_error("ParseAgg", aggname, basetype);
- aggform = (Form_pg_aggregate) GETSTRUCT(aggtuple);
-
- /*
- * There used to be a really ugly hack for count(*) here.
- *
- * It's gone. Now, the grammar transforms count(*) into count(1), which
- * does the right thing. (It didn't use to do the right thing,
- * because the optimizer had the wrong ideas about semantics of
- * queries without explicit variables. Fixed as of Oct 1999 --- tgl.)
- */
-
- /*
- * We assume caller has already checked that given args are compatible
- * with the agg's basetype.
- */
-
- aggref = makeNode(Aggref);
- aggref->aggname = pstrdup(strVal(llast(aggname)));
- aggref->basetype = aggform->aggbasetype;
- aggref->aggtype = aggform->aggfinaltype;
- aggref->target = lfirst(args);
- aggref->aggstar = agg_star;
- aggref->aggdistinct = agg_distinct;
-
- ReleaseSysCache(aggtuple);
-
- pstate->p_hasAggs = true;
-
- return aggref;
-}
-
-/*
- * Error message when aggregate lookup fails that gives details of the
- * basetype
- */
-void
-agg_error(const char *caller, List *aggname, Oid basetypeID)
-{
- /*
- * basetypeID that is Invalid (zero) means aggregate over all types.
- * (count)
- */
-
- if (basetypeID == InvalidOid)
- elog(ERROR, "%s: aggregate '%s' for all types does not exist",
- caller, NameListToString(aggname));
- else
- elog(ERROR, "%s: aggregate '%s' for type %s does not exist",
- caller, NameListToString(aggname), format_type_be(basetypeID));
-}
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 4dd5777e1e..aca5afc124 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.69 2002/04/09 20:35:52 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.70 2002/04/11 20:00:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,7 +32,7 @@ Oid PromoteTypeToNext(Oid inType);
static Oid PreferredType(CATEGORY category, Oid type);
static Node *build_func_call(Oid funcid, Oid rettype, List *args);
static Oid find_coercion_function(Oid targetTypeId, Oid inputTypeId,
- Oid secondArgType);
+ Oid secondArgType, bool isExplicit);
/* coerce_type()
@@ -40,7 +40,7 @@ static Oid find_coercion_function(Oid targetTypeId, Oid inputTypeId,
*/
Node *
coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
- Oid targetTypeId, int32 atttypmod)
+ Oid targetTypeId, int32 atttypmod, bool isExplicit)
{
Node *result;
@@ -131,7 +131,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
funcId = find_coercion_function(baseTypeId,
getBaseType(inputTypeId),
- InvalidOid);
+ InvalidOid,
+ isExplicit);
if (!OidIsValid(funcId))
elog(ERROR, "coerce_type: no conversion function from %s to %s",
format_type_be(inputTypeId), format_type_be(targetTypeId));
@@ -171,13 +172,18 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
*
* There are a few types which are known apriori to be convertible.
* We will check for those cases first, and then look for possible
- * conversion functions.
+ * conversion functions.
+ *
+ * We must be told whether this is an implicit or explicit coercion
+ * (explicit being a CAST construct, explicit function call, etc).
+ * We will accept a wider set of coercion cases for an explicit coercion.
*
* Notes:
* This uses the same mechanism as the CAST() SQL construct in gram.y.
*/
bool
-can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
+can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids,
+ bool isExplicit)
{
int i;
@@ -230,7 +236,7 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
return false;
/*
- * Else, try for explicit conversion using functions: look for a
+ * Else, try for run-time conversion using functions: look for a
* single-argument function named with the target type name and
* accepting the source type.
*
@@ -238,7 +244,8 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
*/
funcId = find_coercion_function(getBaseType(targetTypeId),
getBaseType(inputTypeId),
- InvalidOid);
+ InvalidOid,
+ isExplicit);
if (!OidIsValid(funcId))
return false;
}
@@ -279,7 +286,8 @@ coerce_type_typmod(ParseState *pstate, Node *node,
/* If given type is a domain, use base type instead */
baseTypeId = getBaseType(targetTypeId);
- funcId = find_coercion_function(baseTypeId, baseTypeId, INT4OID);
+ /* Note this is always implicit coercion */
+ funcId = find_coercion_function(baseTypeId, baseTypeId, INT4OID, false);
if (OidIsValid(funcId))
{
@@ -321,9 +329,10 @@ coerce_to_boolean(ParseState *pstate, Node **pnode)
if (inputTypeId == BOOLOID)
return true; /* no work */
targetTypeId = BOOLOID;
- if (!can_coerce_type(1, &inputTypeId, &targetTypeId))
+ if (!can_coerce_type(1, &inputTypeId, &targetTypeId, false))
return false; /* fail, but let caller choose error msg */
- *pnode = coerce_type(pstate, *pnode, inputTypeId, targetTypeId, -1);
+ *pnode = coerce_type(pstate, *pnode, inputTypeId, targetTypeId, -1,
+ false);
return true;
}
@@ -378,7 +387,7 @@ select_common_type(List *typeids, const char *context)
}
else if (IsPreferredType(pcategory, ntype)
&& !IsPreferredType(pcategory, ptype)
- && can_coerce_type(1, &ptype, &ntype))
+ && can_coerce_type(1, &ptype, &ntype, false))
{
/*
* new one is preferred and can convert? then take it...
@@ -424,8 +433,9 @@ coerce_to_common_type(ParseState *pstate, Node *node,
if (inputTypeId == targetTypeId)
return node; /* no work */
- if (can_coerce_type(1, &inputTypeId, &targetTypeId))
- node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1);
+ if (can_coerce_type(1, &inputTypeId, &targetTypeId, false))
+ node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1,
+ false);
else
{
elog(ERROR, "%s unable to convert to type \"%s\"",
@@ -659,6 +669,9 @@ PreferredType(CATEGORY category, Oid type)
* A coercion function must be named after (the internal name of) its
* result type, and must accept exactly the specified input type. We
* also require it to be defined in the same namespace as its result type.
+ * Furthermore, unless we are doing explicit coercion the function must
+ * be marked as usable for implicit coercion --- this allows coercion
+ * functions to be provided that aren't implicitly invokable.
*
* This routine is also used to look for length-coercion functions, which
* are similar but accept a second argument. secondArgType is the type
@@ -668,16 +681,16 @@ PreferredType(CATEGORY category, Oid type)
* If a function is found, return its pg_proc OID; else return InvalidOid.
*/
static Oid
-find_coercion_function(Oid targetTypeId, Oid inputTypeId, Oid secondArgType)
+find_coercion_function(Oid targetTypeId, Oid inputTypeId, Oid secondArgType,
+ bool isExplicit)
{
+ Oid funcid = InvalidOid;
Type targetType;
char *typname;
Oid typnamespace;
Oid oid_array[FUNC_MAX_ARGS];
int nargs;
HeapTuple ftup;
- Form_pg_proc pform;
- Oid funcid;
targetType = typeidType(targetTypeId);
typname = NameStr(((Form_pg_type) GETSTRUCT(targetType))->typname);
@@ -698,21 +711,24 @@ find_coercion_function(Oid targetTypeId, Oid inputTypeId, Oid secondArgType)
Int16GetDatum(nargs),
PointerGetDatum(oid_array),
ObjectIdGetDatum(typnamespace));
- if (!HeapTupleIsValid(ftup))
- {
- ReleaseSysCache(targetType);
- return InvalidOid;
- }
- /* Make sure the function's result type is as expected, too */
- pform = (Form_pg_proc) GETSTRUCT(ftup);
- if (pform->prorettype != targetTypeId)
+ if (HeapTupleIsValid(ftup))
{
+ Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup);
+
+ /* Make sure the function's result type is as expected */
+ if (pform->prorettype == targetTypeId && !pform->proretset &&
+ !pform->proisagg)
+ {
+ /* If needed, make sure it can be invoked implicitly */
+ if (isExplicit || pform->proimplicit)
+ {
+ /* Okay to use it */
+ funcid = ftup->t_data->t_oid;
+ }
+ }
ReleaseSysCache(ftup);
- ReleaseSysCache(targetType);
- return InvalidOid;
}
- funcid = ftup->t_data->t_oid;
- ReleaseSysCache(ftup);
+
ReleaseSysCache(targetType);
return funcid;
}
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index f844971606..916c1da4a6 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.113 2002/04/09 20:35:52 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.114 2002/04/11 20:00:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1174,7 +1174,8 @@ parser_typecast_expression(ParseState *pstate,
if (inputType != targetType)
{
expr = CoerceTargetExpr(pstate, expr, inputType,
- targetType, typename->typmod);
+ targetType, typename->typmod,
+ true); /* explicit coercion */
if (expr == NULL)
elog(ERROR, "Cannot cast type '%s' to '%s'",
format_type_be(inputType),
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index a86195126f..2ab9c9a689 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,23 +8,18 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.125 2002/04/09 20:35:53 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.126 2002/04/11 20:00:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/catname.h"
-#include "catalog/indexing.h"
#include "catalog/namespace.h"
-#include "catalog/pg_aggregate.h"
#include "catalog/pg_inherits.h"
-#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "nodes/makefuncs.h"
-#include "parser/parse_agg.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
@@ -35,6 +30,7 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+
static Node *ParseComplexProjection(ParseState *pstate,
char *funcname,
Node *first_arg);
@@ -54,9 +50,6 @@ static int match_argtypes(int nargs,
static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid);
static FuncCandidateList func_select_candidate(int nargs, Oid *input_typeids,
FuncCandidateList candidates);
-static int agg_get_candidates(List *aggname, Oid typeId,
- FuncCandidateList *candidates);
-static Oid agg_select_candidate(Oid typeid, FuncCandidateList candidates);
/*
@@ -89,14 +82,10 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
char *refname;
int nargs = length(fargs);
int argn;
- Func *funcnode;
Oid oid_array[FUNC_MAX_ARGS];
Oid *true_oid_array;
Node *retval;
bool retset;
- bool must_be_agg = agg_star || agg_distinct;
- bool could_be_agg;
- Expr *expr;
FuncDetailCode fdresult;
/*
@@ -123,7 +112,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* then the "function call" could be a projection. We also check
* that there wasn't any aggregate decoration.
*/
- if (nargs == 1 && !must_be_agg && length(funcname) == 1)
+ if (nargs == 1 && !agg_star && !agg_distinct && length(funcname) == 1)
{
char *cname = strVal(lfirst(funcname));
@@ -152,84 +141,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
}
/*
- * See if it's an aggregate.
- */
- if (must_be_agg)
- {
- /* We don't presently cope with, eg, foo(DISTINCT x,y) */
- if (nargs != 1)
- elog(ERROR, "Aggregate functions may only have one parameter");
- /* Agg's argument can't be a relation name, either */
- if (IsA(first_arg, RangeVar))
- elog(ERROR, "Aggregate functions cannot be applied to relation names");
- could_be_agg = true;
- }
- else
- {
- /* Try to parse as an aggregate if above-mentioned checks are OK */
- could_be_agg = (nargs == 1) && !(IsA(first_arg, RangeVar));
- }
-
- if (could_be_agg)
- {
- Oid basetype = exprType(lfirst(fargs));
- int ncandidates;
- FuncCandidateList candidates;
-
- /* try for exact match first... */
- if (SearchSysCacheExists(AGGNAME,
- PointerGetDatum(strVal(llast(funcname))),
- ObjectIdGetDatum(basetype),
- 0, 0))
- return (Node *) ParseAgg(pstate, funcname, basetype,
- fargs, agg_star, agg_distinct);
-
- /* check for aggregate-that-accepts-any-type (eg, COUNT) */
- if (SearchSysCacheExists(AGGNAME,
- PointerGetDatum(strVal(llast(funcname))),
- ObjectIdGetDatum(0),
- 0, 0))
- return (Node *) ParseAgg(pstate, funcname, 0,
- fargs, agg_star, agg_distinct);
-
- /*
- * No exact match yet, so see if there is another entry in the
- * aggregate table that is compatible. - thomas 1998-12-05
- */
- ncandidates = agg_get_candidates(funcname, basetype, &candidates);
- if (ncandidates > 0)
- {
- Oid type;
-
- type = agg_select_candidate(basetype, candidates);
- if (OidIsValid(type))
- {
- lfirst(fargs) = coerce_type(pstate, lfirst(fargs),
- basetype, type, -1);
- basetype = type;
- return (Node *) ParseAgg(pstate, funcname, basetype,
- fargs, agg_star, agg_distinct);
- }
- else
- {
- /* Multiple possible matches --- give up */
- elog(ERROR, "Unable to select an aggregate function %s(%s)",
- NameListToString(funcname), format_type_be(basetype));
- }
- }
-
- if (must_be_agg)
- {
- /*
- * No matching agg, but we had '*' or DISTINCT, so a plain
- * function could not have been meant.
- */
- elog(ERROR, "There is no aggregate function %s(%s)",
- NameListToString(funcname), format_type_be(basetype));
- }
- }
-
- /*
* Okay, it's not a column projection, so it must really be a function.
* Extract arg type info and transform RangeVar arguments into varnodes
* of the appropriate form.
@@ -321,9 +232,22 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* these cases, so why duplicate code...
*/
return coerce_type(pstate, lfirst(fargs),
- oid_array[0], rettype, -1);
+ oid_array[0], rettype, -1, true);
}
- if (fdresult != FUNCDETAIL_NORMAL)
+ else if (fdresult == FUNCDETAIL_NORMAL)
+ {
+ /*
+ * Normal function found; was there anything indicating it must be
+ * an aggregate?
+ */
+ if (agg_star)
+ elog(ERROR, "%s(*) specified, but %s is not an aggregate function",
+ NameListToString(funcname), NameListToString(funcname));
+ if (agg_distinct)
+ elog(ERROR, "DISTINCT specified, but %s is not an aggregate function",
+ NameListToString(funcname));
+ }
+ else if (fdresult != FUNCDETAIL_AGGREGATE)
{
/*
* Oops. Time to die.
@@ -341,165 +265,62 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
"\n\tYou may need to add explicit typecasts");
}
- /* got it */
- funcnode = makeNode(Func);
- funcnode->funcid = funcid;
- funcnode->functype = rettype;
- funcnode->func_fcache = NULL;
-
/* perform the necessary typecasting of arguments */
make_arguments(pstate, nargs, fargs, oid_array, true_oid_array);
- expr = makeNode(Expr);
- expr->typeOid = rettype;
- expr->opType = FUNC_EXPR;
- expr->oper = (Node *) funcnode;
- expr->args = fargs;
- retval = (Node *) expr;
-
- /*
- * if the function returns a set of values, then we need to iterate
- * over all the returned values in the executor, so we stick an iter
- * node here. if it returns a singleton, then we don't need the iter
- * node.
- */
- if (retset)
+ /* build the appropriate output structure */
+ if (fdresult == FUNCDETAIL_NORMAL)
{
- Iter *iter = makeNode(Iter);
-
- iter->itertype = rettype;
- iter->iterexpr = retval;
- retval = (Node *) iter;
- }
+ Expr *expr = makeNode(Expr);
+ Func *funcnode = makeNode(Func);
- return retval;
-}
-
-
-static int
-agg_get_candidates(List *aggname,
- Oid typeId,
- FuncCandidateList *candidates)
-{
- Relation pg_aggregate_desc;
- SysScanDesc pg_aggregate_scan;
- HeapTuple tup;
- int ncandidates = 0;
- ScanKeyData aggKey[1];
+ funcnode->funcid = funcid;
+ funcnode->functype = rettype;
+ funcnode->func_fcache = NULL;
- *candidates = NULL;
+ expr->typeOid = rettype;
+ expr->opType = FUNC_EXPR;
+ expr->oper = (Node *) funcnode;
+ expr->args = fargs;
- ScanKeyEntryInitialize(&aggKey[0], 0,
- Anum_pg_aggregate_aggname,
- F_NAMEEQ,
- NameGetDatum(strVal(llast(aggname))));
-
- pg_aggregate_desc = heap_openr(AggregateRelationName, AccessShareLock);
- pg_aggregate_scan = systable_beginscan(pg_aggregate_desc,
- AggregateNameTypeIndex, true,
- SnapshotNow,
- 1, aggKey);
-
- while (HeapTupleIsValid(tup = systable_getnext(pg_aggregate_scan)))
- {
- Form_pg_aggregate agg = (Form_pg_aggregate) GETSTRUCT(tup);
- FuncCandidateList current_candidate;
-
- current_candidate = (FuncCandidateList)
- palloc(sizeof(struct _FuncCandidateList));
- current_candidate->args[0] = agg->aggbasetype;
- current_candidate->next = *candidates;
- *candidates = current_candidate;
- ncandidates++;
- }
-
- systable_endscan(pg_aggregate_scan);
- heap_close(pg_aggregate_desc, AccessShareLock);
-
- return ncandidates;
-} /* agg_get_candidates() */
-
-/* agg_select_candidate()
- *
- * Try to choose only one candidate aggregate function from a list of
- * possible matches. Return value is Oid of input type of aggregate
- * if successful, else InvalidOid.
- */
-static Oid
-agg_select_candidate(Oid typeid, FuncCandidateList candidates)
-{
- FuncCandidateList current_candidate;
- FuncCandidateList last_candidate;
- Oid current_typeid;
- int ncandidates;
- CATEGORY category,
- current_category;
+ retval = (Node *) expr;
- /*
- * First look for exact matches or binary compatible matches. (Of
- * course exact matches shouldn't even get here, but anyway.)
- */
- ncandidates = 0;
- last_candidate = NULL;
- for (current_candidate = candidates;
- current_candidate != NULL;
- current_candidate = current_candidate->next)
- {
- current_typeid = current_candidate->args[0];
-
- if (IsBinaryCompatible(current_typeid, typeid))
+ /*
+ * if the function returns a set of values, then we need to iterate
+ * over all the returned values in the executor, so we stick an iter
+ * node here. if it returns a singleton, then we don't need the iter
+ * node.
+ */
+ if (retset)
{
- last_candidate = current_candidate;
- ncandidates++;
+ Iter *iter = makeNode(Iter);
+
+ iter->itertype = rettype;
+ iter->iterexpr = retval;
+ retval = (Node *) iter;
}
}
- if (ncandidates == 1)
- return last_candidate->args[0];
-
- /*
- * If no luck that way, look for candidates which allow coercion and
- * have a preferred type. Keep all candidates if none match.
- */
- category = TypeCategory(typeid);
- ncandidates = 0;
- last_candidate = NULL;
- for (current_candidate = candidates;
- current_candidate != NULL;
- current_candidate = current_candidate->next)
+ else
{
- current_typeid = current_candidate->args[0];
- current_category = TypeCategory(current_typeid);
+ /* aggregate function */
+ Aggref *aggref = makeNode(Aggref);
- if (current_category == category
- && IsPreferredType(current_category, current_typeid)
- && can_coerce_type(1, &typeid, &current_typeid))
- {
- /* only one so far? then keep it... */
- if (last_candidate == NULL)
- {
- candidates = current_candidate;
- last_candidate = current_candidate;
- ncandidates = 1;
- }
- /* otherwise, keep this one too... */
- else
- {
- last_candidate->next = current_candidate;
- last_candidate = current_candidate;
- ncandidates++;
- }
- }
- /* otherwise, don't bother keeping this one around... */
- }
+ aggref->aggfnoid = funcid;
+ aggref->aggtype = rettype;
+ aggref->target = lfirst(fargs);
+ aggref->aggstar = agg_star;
+ aggref->aggdistinct = agg_distinct;
- if (last_candidate) /* terminate rebuilt list */
- last_candidate->next = NULL;
+ retval = (Node *) aggref;
- if (ncandidates == 1)
- return candidates->args[0];
+ if (retset)
+ elog(ERROR, "Aggregates may not return sets");
- return InvalidOid;
-} /* agg_select_candidate() */
+ pstate->p_hasAggs = true;
+ }
+
+ return retval;
+}
/* match_argtypes()
@@ -529,7 +350,8 @@ match_argtypes(int nargs,
current_candidate = next_candidate)
{
next_candidate = current_candidate->next;
- if (can_coerce_type(nargs, input_typeids, current_candidate->args))
+ if (can_coerce_type(nargs, input_typeids, current_candidate->args,
+ false))
{
current_candidate->next = *candidates;
*candidates = current_candidate;
@@ -1014,6 +836,7 @@ func_get_detail(List *funcname,
{
HeapTuple ftup;
Form_pg_proc pform;
+ FuncDetailCode result;
*funcid = best_candidate->oid;
*true_typeids = best_candidate->args;
@@ -1026,8 +849,9 @@ func_get_detail(List *funcname,
pform = (Form_pg_proc) GETSTRUCT(ftup);
*rettype = pform->prorettype;
*retset = pform->proretset;
+ result = pform->proisagg ? FUNCDETAIL_AGGREGATE : FUNCDETAIL_NORMAL;
ReleaseSysCache(ftup);
- return FUNCDETAIL_NORMAL;
+ return result;
}
return FUNCDETAIL_NOTFOUND;
@@ -1294,7 +1118,8 @@ make_arguments(ParseState *pstate,
lfirst(current_fargs) = coerce_type(pstate,
lfirst(current_fargs),
input_typeids[i],
- function_typeids[i], -1);
+ function_typeids[i], -1,
+ false);
}
}
}
@@ -1451,6 +1276,58 @@ func_error(const char *caller, List *funcname,
}
/*
+ * find_aggregate_func
+ * Convenience routine to check that a function exists and is an
+ * aggregate.
+ *
+ * Note: basetype is InvalidOid if we are looking for an aggregate on
+ * all types.
+ */
+Oid
+find_aggregate_func(const char *caller, List *aggname, Oid basetype)
+{
+ Oid oid;
+ HeapTuple ftup;
+ Form_pg_proc pform;
+
+ oid = LookupFuncName(aggname, 1, &basetype);
+
+ if (!OidIsValid(oid))
+ {
+ if (basetype == InvalidOid)
+ elog(ERROR, "%s: aggregate '%s' for all types does not exist",
+ caller, NameListToString(aggname));
+ else
+ elog(ERROR, "%s: aggregate '%s' for type %s does not exist",
+ caller, NameListToString(aggname),
+ format_type_be(basetype));
+ }
+
+ /* Make sure it's an aggregate */
+ ftup = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(ftup)) /* should not happen */
+ elog(ERROR, "function %u not found", oid);
+ pform = (Form_pg_proc) GETSTRUCT(ftup);
+
+ if (!pform->proisagg)
+ {
+ if (basetype == InvalidOid)
+ elog(ERROR, "%s: function %s(*) is not an aggregate",
+ caller, NameListToString(aggname));
+ else
+ elog(ERROR, "%s: function %s(%s) is not an aggregate",
+ caller, NameListToString(aggname),
+ format_type_be(basetype));
+ }
+
+ ReleaseSysCache(ftup);
+
+ return oid;
+}
+
+/*
* LookupFuncName
* Given a possibly-qualified function name and a set of argument types,
* look up the function. Returns InvalidOid if no such function.
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 8b259e97c1..0868f3f0bb 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.60 2002/03/21 16:01:07 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.61 2002/04/11 20:00:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -73,7 +73,8 @@ make_operand(char *opname,
{
/* must coerce? */
if (target_typeId != orig_typeId)
- result = coerce_type(NULL, tree, orig_typeId, target_typeId, -1);
+ result = coerce_type(NULL, tree, orig_typeId, target_typeId, -1,
+ false);
else
result = tree;
}
@@ -288,7 +289,7 @@ transformArraySubscripts(ParseState *pstate,
subexpr = transformExpr(pstate, ai->lidx);
/* If it's not int4 already, try to coerce */
subexpr = CoerceTargetExpr(pstate, subexpr, exprType(subexpr),
- INT4OID, -1);
+ INT4OID, -1, false);
if (subexpr == NULL)
elog(ERROR, "array index expressions must be integers");
}
@@ -308,7 +309,7 @@ transformArraySubscripts(ParseState *pstate,
subexpr = transformExpr(pstate, ai->uidx);
/* If it's not int4 already, try to coerce */
subexpr = CoerceTargetExpr(pstate, subexpr, exprType(subexpr),
- INT4OID, -1);
+ INT4OID, -1, false);
if (subexpr == NULL)
elog(ERROR, "array index expressions must be integers");
upperIndexpr = lappend(upperIndexpr, subexpr);
@@ -329,7 +330,7 @@ transformArraySubscripts(ParseState *pstate,
/* XXX fixme: need to get the array's atttypmod? */
assignFrom = CoerceTargetExpr(pstate, assignFrom,
typesource, typeneeded,
- -1);
+ -1, false);
if (assignFrom == NULL)
elog(ERROR, "Array assignment requires type '%s'"
" but expression is of type '%s'"
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 8495f9f9e6..028bfab431 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.53 2002/03/20 19:44:31 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.54 2002/04/11 20:00:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -265,7 +265,8 @@ oper_select_candidate(int nargs,
current_candidate != NULL;
current_candidate = current_candidate->next)
{
- if (can_coerce_type(nargs, input_typeids, current_candidate->args))
+ if (can_coerce_type(nargs, input_typeids, current_candidate->args,
+ false))
{
if (last_candidate == NULL)
{
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 83c53de5d1..9f97ab0f4c 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.83 2002/04/09 20:35:53 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.84 2002/04/11 20:00:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -290,7 +290,8 @@ updateTargetListEntry(ParseState *pstate,
if (type_id != attrtype)
{
tle->expr = CoerceTargetExpr(pstate, tle->expr, type_id,
- attrtype, attrtypmod);
+ attrtype, attrtypmod,
+ false);
if (tle->expr == NULL)
elog(ERROR, "column \"%s\" is of type '%s'"
" but expression is of type '%s'"
@@ -327,10 +328,12 @@ CoerceTargetExpr(ParseState *pstate,
Node *expr,
Oid type_id,
Oid attrtype,
- int32 attrtypmod)
+ int32 attrtypmod,
+ bool isExplicit)
{
- if (can_coerce_type(1, &type_id, &attrtype))
- expr = coerce_type(pstate, expr, type_id, attrtype, attrtypmod);
+ if (can_coerce_type(1, &type_id, &attrtype, isExplicit))
+ expr = coerce_type(pstate, expr, type_id, attrtype, attrtypmod,
+ isExplicit);
#ifndef DISABLE_STRING_HACKS
@@ -345,8 +348,9 @@ CoerceTargetExpr(ParseState *pstate,
if (type_id == TEXTOID)
{
}
- else if (can_coerce_type(1, &type_id, &text_id))
- expr = coerce_type(pstate, expr, type_id, text_id, attrtypmod);
+ else if (can_coerce_type(1, &type_id, &text_id, isExplicit))
+ expr = coerce_type(pstate, expr, type_id, text_id, attrtypmod,
+ isExplicit);
else
expr = NULL;
}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 08d9350249..9fe530818b 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.101 2002/04/05 05:47:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.102 2002/04/11 20:00:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -476,7 +476,7 @@ build_column_default(Relation rel, int attrno)
if (exprtype != atttype)
{
expr = CoerceTargetExpr(NULL, expr, exprtype,
- atttype, atttypmod);
+ atttype, atttypmod, false);
/*
* This really shouldn't fail; should have checked the
diff --git a/src/backend/utils/Gen_fmgrtab.sh b/src/backend/utils/Gen_fmgrtab.sh
index c5034c1cb8..7855a65b7a 100644
--- a/src/backend/utils/Gen_fmgrtab.sh
+++ b/src/backend/utils/Gen_fmgrtab.sh
@@ -9,7 +9,7 @@
#
#
# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/backend/utils/Attic/Gen_fmgrtab.sh,v 1.22 2002/04/05 00:31:28 tgl Exp $
+# $Header: /cvsroot/pgsql/src/backend/utils/Attic/Gen_fmgrtab.sh,v 1.23 2002/04/11 20:00:04 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -88,6 +88,8 @@ trap 'echo "Caught signal." ; cleanup ; exit 1' 1 2 15
# deal with preprocessor statements first (before we sort the
# function table by oid).
#
+# Note assumption here that prolang == $5 and INTERNALlanguageId == 12.
+#
$AWK '
BEGIN { raw = 0; }
/^DATA/ { print; next; }
@@ -161,6 +163,8 @@ cat > "$$-$OIDSFILE" <<FuNkYfMgRsTuFf
*/
FuNkYfMgRsTuFf
+# Note assumption here that prosrc == $(NF-2).
+
tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' < $RAWFILE | \
$AWK '
BEGIN { OFS = ""; }
@@ -209,6 +213,8 @@ cat > "$$-$TABLEFILE" <<FuNkYfMgRtAbStUfF
FuNkYfMgRtAbStUfF
+# Note assumption here that prosrc == $(NF-2).
+
$AWK '{ print "extern Datum", $(NF-2), "(PG_FUNCTION_ARGS);"; }' $RAWFILE >> "$$-$TABLEFILE"
if [ $? -ne 0 ]; then
@@ -226,13 +232,16 @@ FuNkYfMgRtAbStUfF
# Note: using awk arrays to translate from pg_proc values to fmgrtab values
# may seem tedious, but avoid the temptation to write a quick x?y:z
# conditional expression instead. Not all awks have conditional expressions.
+#
+# Note assumptions here that prosrc == $(NF-2), pronargs == $12,
+# proisstrict == $9, proretset == $10
$AWK 'BEGIN {
Bool["t"] = "true"
Bool["f"] = "false"
}
{ printf (" { %d, \"%s\", %d, %s, %s, %s },\n"), \
- $1, $(NF-2), $10, Bool[$8], Bool[$11], $(NF-2)
+ $1, $(NF-2), $12, Bool[$9], Bool[$10], $(NF-2)
}' $RAWFILE >> "$$-$TABLEFILE"
if [ $? -ne 0 ]; then
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index d834023439..501d5a2095 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.95 2002/03/22 02:56:35 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.96 2002/04/11 20:00:04 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -130,6 +130,7 @@ static void get_names_for_var(Var *var, deparse_context *context,
char **refname, char **attname);
static void get_rule_expr(Node *node, deparse_context *context);
static void get_func_expr(Expr *expr, deparse_context *context);
+static void get_agg_expr(Aggref *aggref, deparse_context *context);
static Node *strip_type_coercion(Node *expr, Oid resultType);
static void get_tle_expr(TargetEntry *tle, deparse_context *context);
static void get_const_expr(Const *constval, deparse_context *context);
@@ -1694,18 +1695,7 @@ get_rule_expr(Node *node, deparse_context *context)
break;
case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
-
- appendStringInfo(buf, "%s(%s",
- quote_identifier(aggref->aggname),
- aggref->aggdistinct ? "DISTINCT " : "");
- if (aggref->aggstar)
- appendStringInfo(buf, "*");
- else
- get_rule_expr(aggref->target, context);
- appendStringInfoChar(buf, ')');
- }
+ get_agg_expr((Aggref *) node, context);
break;
case T_Iter:
@@ -2000,6 +1990,45 @@ get_func_expr(Expr *expr, deparse_context *context)
ReleaseSysCache(proctup);
}
+/* ----------
+ * get_agg_expr - Parse back an Aggref node
+ * ----------
+ */
+static void
+get_agg_expr(Aggref *aggref, deparse_context *context)
+{
+ StringInfo buf = context->buf;
+ HeapTuple proctup;
+ Form_pg_proc procStruct;
+ char *proname;
+
+ /*
+ * Get the aggregate's pg_proc tuple
+ */
+ proctup = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(aggref->aggfnoid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(proctup))
+ elog(ERROR, "cache lookup for proc %u failed", aggref->aggfnoid);
+
+ procStruct = (Form_pg_proc) GETSTRUCT(proctup);
+ proname = NameStr(procStruct->proname);
+
+ /*
+ * Display it
+ */
+ appendStringInfo(buf, "%s(%s",
+ quote_identifier(proname),
+ aggref->aggdistinct ? "DISTINCT " : "");
+ if (aggref->aggstar)
+ appendStringInfo(buf, "*");
+ else
+ get_rule_expr(aggref->target, context);
+ appendStringInfoChar(buf, ')');
+
+ ReleaseSysCache(proctup);
+}
+
/*
* strip_type_coercion
diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c
index 2302928624..a040361552 100644
--- a/src/backend/utils/adt/sets.c
+++ b/src/backend/utils/adt/sets.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.42 2002/04/05 00:31:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.43 2002/04/11 20:00:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -58,16 +58,19 @@ SetDefine(char *querystr, Oid elemType)
true, /* returnsSet */
elemType, /* returnType */
SQLlanguageId, /* language */
- querystr, /* sourceCode */
- fileName, /* fileName */
+ querystr, /* prosrc */
+ fileName, /* probin */
+ false, /* not aggregate */
true, /* trusted */
+ false, /* not implicit coercion */
false, /* isStrict (irrelevant, no args) */
PROVOLATILE_VOLATILE, /* assume unsafe */
100, /* byte_pct */
0, /* perbyte_cpu */
0, /* percall_cpu */
100, /* outin_ratio */
- NIL); /* argList */
+ 0, /* parameterCount */
+ NULL); /* parameterTypes */
/*
* Since we're still inside this command of the transaction, we can't
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index e384b3f622..f2d318b820 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.88 2002/03/09 17:35:36 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.89 2002/04/11 20:00:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -307,19 +307,6 @@ _bpchar(PG_FUNCTION_ARGS)
}
-/* bpchar_char()
- * Convert bpchar(1) to char.
- *
- * If input is multiple chars, only the first is returned.
- */
-Datum
-bpchar_char(PG_FUNCTION_ARGS)
-{
- BpChar *s = PG_GETARG_BPCHAR_P(0);
-
- PG_RETURN_CHAR(*VARDATA(s));
-}
-
/* char_bpchar()
* Convert char to bpchar(1).
*/
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 36535bd7fa..6808c07f4b 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.75 2002/04/09 20:35:54 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.76 2002/04/11 20:00:06 tgl Exp $
*
* NOTES
* These routines allow the parser/planner/executor to perform
@@ -93,22 +93,12 @@ struct cachedesc
};
static const struct cachedesc cacheinfo[] = {
- {AggregateRelationName, /* AGGNAME */
- AggregateNameTypeIndex,
- 0,
- 2,
- {
- Anum_pg_aggregate_aggname,
- Anum_pg_aggregate_aggbasetype,
- 0,
- 0
- }},
- {AggregateRelationName, /* AGGOID */
- AggregateOidIndex,
+ {AggregateRelationName, /* AGGFNOID */
+ AggregateFnoidIndex,
0,
1,
{
- ObjectIdAttributeNumber,
+ Anum_pg_aggregate_aggfnoid,
0,
0,
0