diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index eef2808f50da9dd94747cfbe5c9252aabda2eb21..8aa940413228f19e4c6f3dfee94ec5df3d84c1dc 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.14 1998/09/09 03:48:17 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.15 1998/12/08 06:18:56 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -27,6 +27,7 @@ #include "parser/parse_expr.h" #include "parser/parse_node.h" #include "parser/parse_target.h" +#include "parser/parse_coerce.h" #include "utils/syscache.h" #include "utils/lsyscache.h" @@ -149,7 +150,7 @@ tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause) if (tle->resdom->resno == grpcl->entry->resdom->resno) { if (contain_agg_clause((Node *) expr)) - elog(ERROR, "parser: aggregates not allowed in GROUP BY clause"); + elog(ERROR, "Aggregates not allowed in GROUP BY clause"); return TRUE; } } @@ -189,7 +190,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry) * non-group column in target list may fail.) */ if (contain_agg_clause(qry->qual)) - elog(ERROR, "parser: aggregates not allowed in WHERE clause"); + elog(ERROR, "Aggregates not allowed in WHERE clause"); /* * the target list can only contain aggregates, group columns and @@ -201,7 +202,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry) if (!tleIsAggOrGroupCol(tle, qry->groupClause)) elog(ERROR, - "parser: illegal use of aggregates or non-group column in target list"); + "Illegal use of aggregates or non-group column in target list"); } /* @@ -211,7 +212,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry) if (!exprIsAggOrGroupCol(qry->havingQual, qry->groupClause)) elog(ERROR, - "parser: illegal use of aggregates or non-group column in HAVING clause"); + "Illegal use of aggregates or non-group column in HAVING clause"); return; } @@ -233,7 +234,7 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype, ObjectIdGetDatum(basetype), 0, 0); if (!HeapTupleIsValid(theAggTuple)) - elog(ERROR, "aggregate %s does not exist", aggname); + elog(ERROR, "Aggregate %s does not exist", aggname); /* * We do a major hack for count(*) here. @@ -309,16 +310,17 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype, else vartype = ((Expr *) lfirst(target))->typeOid; - if (basetype != vartype) + if ((basetype != vartype) + && (! IS_BINARY_COMPATIBLE(basetype, vartype))) { Type tp1, tp2; tp1 = typeidType(basetype); tp2 = typeidType(vartype); - elog(NOTICE, "Aggregate type mismatch:"); - elog(ERROR, "%s works on %s, not %s", aggname, - typeTypeName(tp1), typeTypeName(tp2)); + elog(ERROR, "Aggregate type mismatch" + "\n\t%s() works on %s, not on %s", + aggname, typeTypeName(tp1), typeTypeName(tp2)); } } diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 81b887e8a5fd2329801813427070264aca44dabc..e1a625f9ef2611d50489d8139fdcf6d92ff6f0e2 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.31 1998/11/27 19:52:13 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.32 1998/12/08 06:18:56 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -25,6 +25,7 @@ #include "catalog/pg_inherits.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" +#include "catalog/pg_aggregate.h" #include "fmgr.h" #include "lib/dllist.h" #include "miscadmin.h" @@ -76,6 +77,8 @@ static List *setup_tlist(char *attname, Oid relid); static List *setup_base_tlist(Oid typeid); static Oid *func_select_candidate(int nargs, Oid *input_typeids, CandidateList candidates); +static int agg_get_candidates(char *aggname, Oid typeId, CandidateList *candidates); +static Oid agg_select_candidate(Oid typeid, CandidateList candidates); #define ISCOMPLEX(type) (typeidTypeRelid(type) ? true : false) @@ -130,6 +133,108 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int pre return retval; } +static int +agg_get_candidates(char *aggname, + Oid typeId, + CandidateList *candidates) +{ + CandidateList current_candidate; + Relation pg_aggregate_desc; + HeapScanDesc pg_aggregate_scan; + HeapTuple tup; + Form_pg_aggregate agg; + int ncandidates = 0; + + static ScanKeyData aggKey[1] = { + {0, Anum_pg_aggregate_aggname, F_NAMEEQ}}; + + *candidates = NULL; + + fmgr_info(F_NAMEEQ, (FmgrInfo *) &aggKey[0].sk_func); + aggKey[0].sk_argument = NameGetDatum(aggname); + + pg_aggregate_desc = heap_openr(AggregateRelationName); + pg_aggregate_scan = heap_beginscan(pg_aggregate_desc, + 0, + SnapshotSelf, /* ??? */ + 1, + aggKey); + + while (HeapTupleIsValid(tup = heap_getnext(pg_aggregate_scan, 0))) + { + current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); + current_candidate->args = (Oid *) palloc(sizeof(Oid)); + + agg = (Form_pg_aggregate) GETSTRUCT(tup); + current_candidate->args[0] = agg->aggbasetype; + current_candidate->next = *candidates; + *candidates = current_candidate; + ncandidates++; + } + + heap_endscan(pg_aggregate_scan); + heap_close(pg_aggregate_desc); + + return ncandidates; +} /* agg_get_candidates() */ + +/* agg_select_candidate() + * Try to choose only one candidate aggregate function from a list of possibles. + */ +static Oid +agg_select_candidate(Oid typeid, CandidateList candidates) +{ + CandidateList current_candidate; + CandidateList last_candidate; + Oid current_typeid; + int ncandidates; + CATEGORY category, + current_category; + +/* + * Look for candidates which allow coersion 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) + { + current_typeid = current_candidate->args[0]; + current_category = TypeCategory(current_typeid); + + 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... */ + else + { + last_candidate->next = NULL; + } + } + + return ((ncandidates == 1) ? candidates->args[0] : 0); +} /* agg_select_candidate() */ + + /* * parse function */ @@ -250,8 +355,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, /* * Parsing aggregates. */ - Type tp; - Oid basetype; + Type tp; + Oid basetype; + int ncandidates; + CandidateList candidates; + /* * the aggregate COUNT is a special case, ignore its base @@ -261,6 +369,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, basetype = 0; else basetype = exprType(lfirst(fargs)); + + /* try for exact match first... */ if (SearchSysCacheTuple(AGGNAME, PointerGetDatum(funcname), ObjectIdGetDatum(basetype), @@ -268,16 +378,47 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, return (Node *) ParseAgg(pstate, funcname, basetype, fargs, precedence); + /* + * No exact match yet, so see if there is another entry + * in the aggregate table which 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); + basetype = type; + + return (Node *) ParseAgg(pstate, funcname, basetype, + fargs, precedence); + } + else + { + elog(ERROR,"Unable to select an aggregate function for type %s", + typeidTypeName(basetype)); + } + } + /* * See if this is a single argument function with the function * name also a type name and the input argument and type name * binary compatible... + * This means that you are trying for a type conversion which does not + * need to take place, so we'll just pass through the argument itself. + * (make this clearer with some extra brackets - thomas 1998-12-05) */ if ((HeapTupleIsValid(tp = SearchSysCacheTuple(TYPNAME, - PointerGetDatum(funcname), + PointerGetDatum(funcname), 0, 0, 0))) && IS_BINARY_COMPATIBLE(typeTypeId(tp), basetype)) + { return ((Node *) lfirst(fargs)); + } } } diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index f9e13f160421965bd89af64febb2227de647def9..6ee4d216099423605606a613f4cb512b23a6f1d9 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.21 1998/11/27 19:52:14 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.22 1998/12/08 06:18:57 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -210,6 +210,7 @@ oper_select_candidate(int nargs, nmatch++; } + /* take this one as the best choice so far? */ if ((nmatch > nbestMatch) || (last_candidate == NULL)) { nbestMatch = nmatch; @@ -217,12 +218,14 @@ oper_select_candidate(int nargs, last_candidate = current_candidate; ncandidates = 1; } + /* no worse than the last choice, so keep this one too? */ else if (nmatch == nbestMatch) { last_candidate->next = current_candidate; last_candidate = current_candidate; ncandidates++; } + /* otherwise, don't bother keeping this one... */ else { last_candidate->next = NULL; diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index 4be36bbba82f8972e387763f1320c854a19cae45..963abf97b3d29941486968598fbe3f4e6501da1e 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.44 1998/10/08 18:30:12 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.45 1998/12/08 06:19:15 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -577,6 +577,38 @@ text_ge(text *arg1, text *arg2) return (bool) !text_lt(arg1, arg2); } +text * +text_larger(text *arg1, text *arg2) +{ + text *result; + text *temp; + + temp = ((text_cmp(arg1, arg2) <= 0)? arg2: arg1); + + /* Make a copy */ + + result = (text *) palloc(VARSIZE(temp)); + memmove((char *) result, (char *) temp, VARSIZE(temp)); + + return (result); +} + +text * +text_smaller(text *arg1, text *arg2) +{ + text *result; + text *temp; + + temp = ((text_cmp(arg1, arg2) > 0)? arg2: arg1); + + /* Make a copy */ + + result = (text *) palloc(VARSIZE(temp)); + memmove((char *) result, (char *) temp, VARSIZE(temp)); + + return (result); +} + /*------------------------------------------------------------- * byteaGetSize *