mirror of
https://github.com/postgres/postgres.git
synced 2025-06-14 18:42:34 +03:00
Define routines and catalog entries for string min()/max() functions.
Extend new type coersion techniques to aggregates. Clean up a few elog() messages.
This commit is contained in:
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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_expr.h"
|
||||||
#include "parser/parse_node.h"
|
#include "parser/parse_node.h"
|
||||||
#include "parser/parse_target.h"
|
#include "parser/parse_target.h"
|
||||||
|
#include "parser/parse_coerce.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
|
||||||
@ -149,7 +150,7 @@ tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause)
|
|||||||
if (tle->resdom->resno == grpcl->entry->resdom->resno)
|
if (tle->resdom->resno == grpcl->entry->resdom->resno)
|
||||||
{
|
{
|
||||||
if (contain_agg_clause((Node *) expr))
|
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;
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,7 +190,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
|
|||||||
* non-group column in target list may fail.)
|
* non-group column in target list may fail.)
|
||||||
*/
|
*/
|
||||||
if (contain_agg_clause(qry->qual))
|
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
|
* the target list can only contain aggregates, group columns and
|
||||||
@ -201,7 +202,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
|
|||||||
|
|
||||||
if (!tleIsAggOrGroupCol(tle, qry->groupClause))
|
if (!tleIsAggOrGroupCol(tle, qry->groupClause))
|
||||||
elog(ERROR,
|
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))
|
if (!exprIsAggOrGroupCol(qry->havingQual, qry->groupClause))
|
||||||
elog(ERROR,
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +234,7 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
|
|||||||
ObjectIdGetDatum(basetype),
|
ObjectIdGetDatum(basetype),
|
||||||
0, 0);
|
0, 0);
|
||||||
if (!HeapTupleIsValid(theAggTuple))
|
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.
|
* We do a major hack for count(*) here.
|
||||||
@ -309,16 +310,17 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
|
|||||||
else
|
else
|
||||||
vartype = ((Expr *) lfirst(target))->typeOid;
|
vartype = ((Expr *) lfirst(target))->typeOid;
|
||||||
|
|
||||||
if (basetype != vartype)
|
if ((basetype != vartype)
|
||||||
|
&& (! IS_BINARY_COMPATIBLE(basetype, vartype)))
|
||||||
{
|
{
|
||||||
Type tp1,
|
Type tp1,
|
||||||
tp2;
|
tp2;
|
||||||
|
|
||||||
tp1 = typeidType(basetype);
|
tp1 = typeidType(basetype);
|
||||||
tp2 = typeidType(vartype);
|
tp2 = typeidType(vartype);
|
||||||
elog(NOTICE, "Aggregate type mismatch:");
|
elog(ERROR, "Aggregate type mismatch"
|
||||||
elog(ERROR, "%s works on %s, not %s", aggname,
|
"\n\t%s() works on %s, not on %s",
|
||||||
typeTypeName(tp1), typeTypeName(tp2));
|
aggname, typeTypeName(tp1), typeTypeName(tp2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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_inherits.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
|
#include "catalog/pg_aggregate.h"
|
||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
#include "lib/dllist.h"
|
#include "lib/dllist.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
@ -76,6 +77,8 @@ static List *setup_tlist(char *attname, Oid relid);
|
|||||||
static List *setup_base_tlist(Oid typeid);
|
static List *setup_base_tlist(Oid typeid);
|
||||||
static Oid *func_select_candidate(int nargs, Oid *input_typeids,
|
static Oid *func_select_candidate(int nargs, Oid *input_typeids,
|
||||||
CandidateList candidates);
|
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)
|
#define ISCOMPLEX(type) (typeidTypeRelid(type) ? true : false)
|
||||||
|
|
||||||
@ -130,6 +133,108 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int pre
|
|||||||
return retval;
|
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
|
* parse function
|
||||||
*/
|
*/
|
||||||
@ -250,8 +355,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
|||||||
/*
|
/*
|
||||||
* Parsing aggregates.
|
* Parsing aggregates.
|
||||||
*/
|
*/
|
||||||
Type tp;
|
Type tp;
|
||||||
Oid basetype;
|
Oid basetype;
|
||||||
|
int ncandidates;
|
||||||
|
CandidateList candidates;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* the aggregate COUNT is a special case, ignore its base
|
* the aggregate COUNT is a special case, ignore its base
|
||||||
@ -261,6 +369,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
|||||||
basetype = 0;
|
basetype = 0;
|
||||||
else
|
else
|
||||||
basetype = exprType(lfirst(fargs));
|
basetype = exprType(lfirst(fargs));
|
||||||
|
|
||||||
|
/* try for exact match first... */
|
||||||
if (SearchSysCacheTuple(AGGNAME,
|
if (SearchSysCacheTuple(AGGNAME,
|
||||||
PointerGetDatum(funcname),
|
PointerGetDatum(funcname),
|
||||||
ObjectIdGetDatum(basetype),
|
ObjectIdGetDatum(basetype),
|
||||||
@ -268,16 +378,47 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
|||||||
return (Node *) ParseAgg(pstate, funcname, basetype,
|
return (Node *) ParseAgg(pstate, funcname, basetype,
|
||||||
fargs, precedence);
|
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
|
* See if this is a single argument function with the function
|
||||||
* name also a type name and the input argument and type name
|
* name also a type name and the input argument and type name
|
||||||
* binary compatible...
|
* 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,
|
if ((HeapTupleIsValid(tp = SearchSysCacheTuple(TYPNAME,
|
||||||
PointerGetDatum(funcname),
|
PointerGetDatum(funcname),
|
||||||
0, 0, 0)))
|
0, 0, 0)))
|
||||||
&& IS_BINARY_COMPATIBLE(typeTypeId(tp), basetype))
|
&& IS_BINARY_COMPATIBLE(typeTypeId(tp), basetype))
|
||||||
|
{
|
||||||
return ((Node *) lfirst(fargs));
|
return ((Node *) lfirst(fargs));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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++;
|
nmatch++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* take this one as the best choice so far? */
|
||||||
if ((nmatch > nbestMatch) || (last_candidate == NULL))
|
if ((nmatch > nbestMatch) || (last_candidate == NULL))
|
||||||
{
|
{
|
||||||
nbestMatch = nmatch;
|
nbestMatch = nmatch;
|
||||||
@ -217,12 +218,14 @@ oper_select_candidate(int nargs,
|
|||||||
last_candidate = current_candidate;
|
last_candidate = current_candidate;
|
||||||
ncandidates = 1;
|
ncandidates = 1;
|
||||||
}
|
}
|
||||||
|
/* no worse than the last choice, so keep this one too? */
|
||||||
else if (nmatch == nbestMatch)
|
else if (nmatch == nbestMatch)
|
||||||
{
|
{
|
||||||
last_candidate->next = current_candidate;
|
last_candidate->next = current_candidate;
|
||||||
last_candidate = current_candidate;
|
last_candidate = current_candidate;
|
||||||
ncandidates++;
|
ncandidates++;
|
||||||
}
|
}
|
||||||
|
/* otherwise, don't bother keeping this one... */
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
last_candidate->next = NULL;
|
last_candidate->next = NULL;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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);
|
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
|
* byteaGetSize
|
||||||
*
|
*
|
||||||
|
Reference in New Issue
Block a user