diff options
| author | Tom Lane <tgl@sss.pgh.pa.us> | 2002-04-11 20:00:18 +0000 |
|---|---|---|
| committer | Tom Lane <tgl@sss.pgh.pa.us> | 2002-04-11 20:00:18 +0000 |
| commit | 902a6a0a4bc62d619a5ccd1ef0ff7fb3a5d897f1 (patch) | |
| tree | c5cc85818d8a3ffae03a23bacd3e679945a41dbd /src/backend | |
| parent | 3f6299df6c7d905bdef44eb3a4b19f248ebc14dc (diff) | |
| download | postgresql-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')
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, ¤t_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 |
