1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-10 17:42:29 +03:00

Create a 'type cache' that keeps track of the data needed for any particular

datatype by array_eq and array_cmp; use this to solve problems with memory
leaks in array indexing support.  The parser's equality_oper and ordering_oper
routines also use the cache.  Change the operator search algorithms to look
for appropriate btree or hash index opclasses, instead of assuming operators
named '<' or '=' have the right semantics.  (ORDER BY ASC/DESC now also look
at opclasses, instead of assuming '<' and '>' are the right things.)  Add
several more index opclasses so that there is no regression in functionality
for base datatypes.  initdb forced due to catalog additions.
This commit is contained in:
Tom Lane
2003-08-17 19:58:06 +00:00
parent d89578ccbe
commit ec646dbc65
40 changed files with 968 additions and 495 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.58 2003/08/04 02:39:58 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.59 2003/08/17 19:58:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -390,7 +390,6 @@ examine_attribute(Relation onerel, int attnum)
{
Form_pg_attribute attr = onerel->rd_att->attrs[attnum - 1];
Operator func_operator;
Oid oprrest;
HeapTuple typtuple;
Oid eqopr = InvalidOid;
Oid eqfunc = InvalidOid;
@@ -409,12 +408,8 @@ examine_attribute(Relation onerel, int attnum)
func_operator = equality_oper(attr->atttypid, true);
if (func_operator != NULL)
{
oprrest = ((Form_pg_operator) GETSTRUCT(func_operator))->oprrest;
if (oprrest == F_EQSEL)
{
eqopr = oprid(func_operator);
eqfunc = oprfuncid(func_operator);
}
eqopr = oprid(func_operator);
eqfunc = oprfuncid(func_operator);
ReleaseSysCache(func_operator);
}
if (!OidIsValid(eqfunc))
@@ -447,9 +442,7 @@ examine_attribute(Relation onerel, int attnum)
func_operator = ordering_oper(attr->atttypid, true);
if (func_operator != NULL)
{
oprrest = ((Form_pg_operator) GETSTRUCT(func_operator))->oprrest;
if (oprrest == F_SCALARLTSEL)
ltopr = oprid(func_operator);
ltopr = oprid(func_operator);
ReleaseSysCache(func_operator);
}
stats->ltopr = ltopr;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.105 2003/08/04 02:39:58 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.106 2003/08/17 19:58:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -529,7 +529,8 @@ GetDefaultOpClass(Oid attrType, Oid accessMethodId)
* than one exact match, then someone put bogus entries in pg_opclass.
*
* The initial search is done by namespace.c so that we only consider
* opclasses visible in the current namespace search path.
* opclasses visible in the current namespace search path. (See also
* typcache.c, which applies the same logic, but over all opclasses.)
*/
for (opclass = OpclassGetCandidates(accessMethodId);
opclass != NULL;

View File

@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/opclasscmds.c,v 1.17 2003/08/04 02:39:58 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/opclasscmds.c,v 1.18 2003/08/17 19:58:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -261,7 +261,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
/*
* If we are creating a default opclass, check there isn't one
* already. (XXX should we restrict this test to visible opclasses?)
* already. (Note we do not restrict this test to visible opclasses;
* this ensures that typcache.c can find unique solutions to its
* questions.)
*/
if (stmt->isDefault)
{

View File

@@ -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.263 2003/08/08 21:41:43 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.264 2003/08/17 19:58:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1402,11 +1402,12 @@ _copyTypeName(TypeName *from)
return newnode;
}
static SortGroupBy *
_copySortGroupBy(SortGroupBy *from)
static SortBy *
_copySortBy(SortBy *from)
{
SortGroupBy *newnode = makeNode(SortGroupBy);
SortBy *newnode = makeNode(SortBy);
COPY_SCALAR_FIELD(sortby_kind);
COPY_NODE_FIELD(useOp);
COPY_NODE_FIELD(node);
@@ -2924,8 +2925,8 @@ copyObject(void *from)
case T_TypeCast:
retval = _copyTypeCast(from);
break;
case T_SortGroupBy:
retval = _copySortGroupBy(from);
case T_SortBy:
retval = _copySortBy(from);
break;
case T_RangeSubselect:
retval = _copyRangeSubselect(from);

View File

@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.207 2003/08/08 21:41:43 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.208 2003/08/17 19:58:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1480,8 +1480,9 @@ _equalTypeCast(TypeCast *a, TypeCast *b)
}
static bool
_equalSortGroupBy(SortGroupBy *a, SortGroupBy *b)
_equalSortBy(SortBy *a, SortBy *b)
{
COMPARE_SCALAR_FIELD(sortby_kind);
COMPARE_NODE_FIELD(useOp);
COMPARE_NODE_FIELD(node);
@@ -2045,8 +2046,8 @@ equal(void *a, void *b)
case T_TypeCast:
retval = _equalTypeCast(a, b);
break;
case T_SortGroupBy:
retval = _equalSortGroupBy(a, b);
case T_SortBy:
retval = _equalSortBy(a, b);
break;
case T_RangeSubselect:
retval = _equalRangeSubselect(a, b);

View File

@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.154 2003/08/11 20:46:46 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.155 2003/08/17 19:58:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -629,8 +629,9 @@ create_unique_plan(Query *root, UniquePath *best_path)
tle = get_tle_by_resno(my_tlist, groupColIdx[groupColPos]);
Assert(tle != NULL);
sortList = addTargetToSortList(NULL, tle, sortList,
my_tlist, NIL, false);
sortList = addTargetToSortList(NULL, tle,
sortList, my_tlist,
SORTBY_ASC, NIL, false);
}
plan = (Plan *) make_sort_from_sortclauses(root, my_tlist,
subplan, sortList);

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.159 2003/08/04 02:40:01 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.160 2003/08/17 19:58:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1327,7 +1327,9 @@ hash_safe_grouping(Query *parse)
Operator optup;
bool oprcanhash;
optup = equality_oper(tle->resdom->restype, false);
optup = equality_oper(tle->resdom->restype, true);
if (!optup)
return false;
oprcanhash = ((Form_pg_operator) GETSTRUCT(optup))->oprcanhash;
ReleaseSysCache(optup);
if (!oprcanhash)

View File

@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.428 2003/08/04 02:40:01 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.429 2003/08/17 19:58:05 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -115,7 +115,7 @@ static void doNegateFloat(Value *v);
TypeName *typnam;
DefElem *defelt;
SortGroupBy *sortgroupby;
SortBy *sortby;
JoinExpr *jexpr;
IndexElem *ielem;
Alias *alias;
@@ -189,7 +189,7 @@ static void doNegateFloat(Value *v);
database_name access_method_clause access_method attr_name
index_name name function_name file_name
%type <list> func_name handler_name qual_Op qual_all_Op OptUseOp
%type <list> func_name handler_name qual_Op qual_all_Op
opt_class opt_validator
%type <range> qualified_name OptConstrFromTable
@@ -278,7 +278,7 @@ static void doNegateFloat(Value *v);
%type <value> NumericOnly FloatOnly IntegerOnly
%type <columnref> columnref
%type <alias> alias_clause
%type <sortgroupby> sortby
%type <sortby> sortby
%type <ielem> index_elem
%type <node> table_ref
%type <jexpr> joined_table
@@ -4577,21 +4577,34 @@ sortby_list:
| sortby_list ',' sortby { $$ = lappend($1, $3); }
;
sortby: a_expr OptUseOp
sortby: a_expr USING qual_all_Op
{
$$ = makeNode(SortGroupBy);
$$ = makeNode(SortBy);
$$->node = $1;
$$->useOp = $2;
$$->sortby_kind = SORTBY_USING;
$$->useOp = $3;
}
| a_expr ASC
{
$$ = makeNode(SortBy);
$$->node = $1;
$$->sortby_kind = SORTBY_ASC;
$$->useOp = NIL;
}
| a_expr DESC
{
$$ = makeNode(SortBy);
$$->node = $1;
$$->sortby_kind = SORTBY_DESC;
$$->useOp = NIL;
}
| a_expr
{
$$ = makeNode(SortBy);
$$->node = $1;
$$->sortby_kind = SORTBY_ASC; /* default */
$$->useOp = NIL;
}
;
OptUseOp: USING qual_all_Op { $$ = $2; }
| ASC
{ $$ = makeList1(makeString("<")); }
| DESC
{ $$ = makeList1(makeString(">")); }
| /*EMPTY*/
{ $$ = makeList1(makeString("<")); /*default*/ }
;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.121 2003/08/07 19:20:22 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.122 2003/08/17 19:58:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1295,7 +1295,7 @@ transformSortClause(ParseState *pstate,
foreach(olitem, orderlist)
{
SortGroupBy *sortby = lfirst(olitem);
SortBy *sortby = lfirst(olitem);
TargetEntry *tle;
tle = findTargetlistEntry(pstate, sortby->node,
@@ -1303,7 +1303,9 @@ transformSortClause(ParseState *pstate,
sortlist = addTargetToSortList(pstate, tle,
sortlist, targetlist,
sortby->useOp, resolveUnknown);
sortby->sortby_kind,
sortby->useOp,
resolveUnknown);
}
return sortlist;
@@ -1409,7 +1411,7 @@ transformDistinctClause(ParseState *pstate, List *distinctlist,
{
*sortClause = addTargetToSortList(pstate, tle,
*sortClause, targetlist,
NIL, true);
SORTBY_ASC, NIL, true);
/*
* Probably, the tle should always have been added at the
@@ -1457,7 +1459,8 @@ addAllTargetsToSortList(ParseState *pstate, List *sortlist,
if (!tle->resdom->resjunk)
sortlist = addTargetToSortList(pstate, tle,
sortlist, targetlist,
NIL, resolveUnknown);
SORTBY_ASC, NIL,
resolveUnknown);
}
return sortlist;
}
@@ -1478,7 +1481,8 @@ addAllTargetsToSortList(ParseState *pstate, List *sortlist,
List *
addTargetToSortList(ParseState *pstate, TargetEntry *tle,
List *sortlist, List *targetlist,
List *opname, bool resolveUnknown)
int sortby_kind, List *sortby_opname,
bool resolveUnknown)
{
/* avoid making duplicate sortlist entries */
if (!targetIsInSortList(tle, sortlist))
@@ -1499,13 +1503,25 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
sortcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist);
if (opname)
sortcl->sortop = compatible_oper_opid(opname,
restype,
restype,
false);
else
sortcl->sortop = ordering_oper_opid(restype);
switch (sortby_kind)
{
case SORTBY_ASC:
sortcl->sortop = ordering_oper_opid(restype);
break;
case SORTBY_DESC:
sortcl->sortop = reverse_ordering_oper_opid(restype);
break;
case SORTBY_USING:
Assert(sortby_opname != NIL);
sortcl->sortop = compatible_oper_opid(sortby_opname,
restype,
restype,
false);
break;
default:
elog(ERROR, "unrecognized sortby_kind: %d", sortby_kind);
break;
}
sortlist = lappend(sortlist, sortcl);
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.73 2003/08/04 02:40:02 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.74 2003/08/17 19:58:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -26,6 +26,7 @@
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
static Oid binary_oper_exact(Oid arg1, Oid arg2,
@@ -135,52 +136,49 @@ LookupOperNameTypeNames(List *opername, TypeName *oprleft,
Operator
equality_oper(Oid argtype, bool noError)
{
TypeCacheEntry *typentry;
Oid oproid;
Operator optup;
Oid elem_type;
/*
* Look for an "=" operator for the datatype. We require it to be
* an exact or binary-compatible match, since most callers are not
* prepared to cope with adding any run-time type coercion steps.
*/
typentry = lookup_type_cache(argtype, TYPECACHE_EQ_OPR);
oproid = typentry->eq_opr;
/*
* If the datatype is an array, then we can use array_eq ... but only
* if there is a suitable equality operator for the element type. (We
* must run this test first, since compatible_oper will find array_eq,
* but would not notice the lack of an element operator.)
* if there is a suitable equality operator for the element type.
* (This check is not in the raw typcache.c code ... should it be?)
*/
elem_type = get_element_type(argtype);
if (OidIsValid(elem_type))
if (oproid == ARRAY_EQ_OP)
{
optup = equality_oper(elem_type, true);
if (optup != NULL)
Oid elem_type = get_element_type(argtype);
if (OidIsValid(elem_type))
{
ReleaseSysCache(optup);
return SearchSysCache(OPEROID,
ObjectIdGetDatum(ARRAY_EQ_OP),
0, 0, 0);
optup = equality_oper(elem_type, true);
if (optup != NULL)
ReleaseSysCache(optup);
else
oproid = InvalidOid; /* element type has no "=" */
}
else
oproid = InvalidOid; /* bogus array type? */
}
else
if (OidIsValid(oproid))
{
/*
* Look for an "=" operator for the datatype. We require it to be
* an exact or binary-compatible match, since most callers are not
* prepared to cope with adding any run-time type coercion steps.
*/
optup = compatible_oper(makeList1(makeString("=")),
argtype, argtype, true);
if (optup != NULL)
{
/*
* Only believe that it's equality if it's mergejoinable,
* hashjoinable, or uses eqsel() as oprrest.
*/
Form_pg_operator pgopform = (Form_pg_operator) GETSTRUCT(optup);
if (OidIsValid(pgopform->oprlsortop) ||
pgopform->oprcanhash ||
pgopform->oprrest == F_EQSEL)
return optup;
ReleaseSysCache(optup);
}
optup = SearchSysCache(OPEROID,
ObjectIdGetDatum(oproid),
0, 0, 0);
if (optup == NULL) /* should not fail */
elog(ERROR, "cache lookup failed for operator %u", oproid);
return optup;
}
if (!noError)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -197,53 +195,119 @@ equality_oper(Oid argtype, bool noError)
Operator
ordering_oper(Oid argtype, bool noError)
{
TypeCacheEntry *typentry;
Oid oproid;
Operator optup;
Oid elem_type;
/*
* Look for a "<" operator for the datatype. We require it to be
* an exact or binary-compatible match, since most callers are not
* prepared to cope with adding any run-time type coercion steps.
*
* Note: the search algorithm used by typcache.c ensures that if a "<"
* operator is returned, it will be consistent with the "=" operator
* returned by equality_oper. This is critical for sorting and grouping
* purposes.
*/
typentry = lookup_type_cache(argtype, TYPECACHE_LT_OPR);
oproid = typentry->lt_opr;
/*
* If the datatype is an array, then we can use array_lt ... but only
* if there is a suitable ordering operator for the element type. (We
* must run this test first, since the code below would find array_lt
* if there's an element = operator, but would not notice the lack of
* an element < operator.)
* if there is a suitable less-than operator for the element type.
* (This check is not in the raw typcache.c code ... should it be?)
*/
elem_type = get_element_type(argtype);
if (OidIsValid(elem_type))
if (oproid == ARRAY_LT_OP)
{
optup = ordering_oper(elem_type, true);
if (optup != NULL)
{
ReleaseSysCache(optup);
return SearchSysCache(OPEROID,
ObjectIdGetDatum(ARRAY_LT_OP),
0, 0, 0);
}
}
else
{
/*
* Find the type's equality operator, and use its lsortop (it
* *must* be mergejoinable). We use this definition because for
* sorting and grouping purposes, it's important that the equality
* and ordering operators are consistent.
*/
optup = equality_oper(argtype, noError);
if (optup != NULL)
{
Oid lsortop;
Oid elem_type = get_element_type(argtype);
lsortop = ((Form_pg_operator) GETSTRUCT(optup))->oprlsortop;
ReleaseSysCache(optup);
if (OidIsValid(lsortop))
{
optup = SearchSysCache(OPEROID,
ObjectIdGetDatum(lsortop),
0, 0, 0);
if (optup != NULL)
return optup;
}
if (OidIsValid(elem_type))
{
optup = ordering_oper(elem_type, true);
if (optup != NULL)
ReleaseSysCache(optup);
else
oproid = InvalidOid; /* element type has no "<" */
}
else
oproid = InvalidOid; /* bogus array type? */
}
if (OidIsValid(oproid))
{
optup = SearchSysCache(OPEROID,
ObjectIdGetDatum(oproid),
0, 0, 0);
if (optup == NULL) /* should not fail */
elog(ERROR, "cache lookup failed for operator %u", oproid);
return optup;
}
if (!noError)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not identify an ordering operator for type %s",
format_type_be(argtype)),
errhint("Use an explicit ordering operator or modify the query.")));
return NULL;
}
/*
* reverse_ordering_oper - identify DESC sort operator (">") for a datatype
*
* On failure, return NULL if noError, else report a standard error
*/
Operator
reverse_ordering_oper(Oid argtype, bool noError)
{
TypeCacheEntry *typentry;
Oid oproid;
Operator optup;
/*
* Look for a ">" operator for the datatype. We require it to be
* an exact or binary-compatible match, since most callers are not
* prepared to cope with adding any run-time type coercion steps.
*
* Note: the search algorithm used by typcache.c ensures that if a ">"
* operator is returned, it will be consistent with the "=" operator
* returned by equality_oper. This is critical for sorting and grouping
* purposes.
*/
typentry = lookup_type_cache(argtype, TYPECACHE_GT_OPR);
oproid = typentry->gt_opr;
/*
* If the datatype is an array, then we can use array_gt ... but only
* if there is a suitable greater-than operator for the element type.
* (This check is not in the raw typcache.c code ... should it be?)
*/
if (oproid == ARRAY_GT_OP)
{
Oid elem_type = get_element_type(argtype);
if (OidIsValid(elem_type))
{
optup = reverse_ordering_oper(elem_type, true);
if (optup != NULL)
ReleaseSysCache(optup);
else
oproid = InvalidOid; /* element type has no ">" */
}
else
oproid = InvalidOid; /* bogus array type? */
}
if (OidIsValid(oproid))
{
optup = SearchSysCache(OPEROID,
ObjectIdGetDatum(oproid),
0, 0, 0);
if (optup == NULL) /* should not fail */
elog(ERROR, "cache lookup failed for operator %u", oproid);
return optup;
}
if (!noError)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -286,16 +350,16 @@ ordering_oper_opid(Oid argtype)
}
/*
* ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
* reverse_ordering_oper_opid - convenience routine for oprid(reverse_ordering_oper())
*/
Oid
ordering_oper_funcid(Oid argtype)
reverse_ordering_oper_opid(Oid argtype)
{
Operator optup;
Oid result;
optup = ordering_oper(argtype, false);
result = oprfuncid(optup);
optup = reverse_ordering_oper(argtype, false);
result = oprid(optup);
ReleaseSysCache(optup);
return result;
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.95 2003/08/14 14:19:07 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.96 2003/08/17 19:58:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -480,6 +480,23 @@ aclitem_eq(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
/*
* aclitem hash function
*
* We make aclitems hashable not so much because anyone is likely to hash
* them, as because we want array equality to work on aclitem arrays, and
* with the typcache mechanism we must have a hash or btree opclass.
*/
Datum
hash_aclitem(PG_FUNCTION_ARGS)
{
AclItem *a = PG_GETARG_ACLITEM_P(0);
/* not very bright, but avoids any issue of padding in struct */
PG_RETURN_UINT32((uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor));
}
/*
* acldefault() --- create an ACL describing default access permissions
*

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.98 2003/08/15 00:22:26 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.99 2003/08/17 19:58:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,6 +28,7 @@
#include "utils/memutils.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
/*----------
@@ -2341,6 +2342,9 @@ deconstruct_array(ArrayType *array,
* compares two arrays for equality
* result :
* returns true if the arrays are equal, false otherwise.
*
* Note: we do not use array_cmp here, since equality may be meaningful in
* datatypes that don't have a total ordering (and hence no btree support).
*-----------------------------------------------------------------------------
*/
Datum
@@ -2357,13 +2361,12 @@ array_eq(PG_FUNCTION_ARGS)
int nitems1 = ArrayGetNItems(ndims1, dims1);
int nitems2 = ArrayGetNItems(ndims2, dims2);
Oid element_type = ARR_ELEMTYPE(array1);
FmgrInfo *ae_fmgr_info = fcinfo->flinfo;
bool result = true;
TypeCacheEntry *typentry;
int typlen;
bool typbyval;
char typalign;
int i;
ArrayMetaState *my_extra;
FunctionCallInfoData locfcinfo;
if (element_type != ARR_ELEMTYPE(array2))
@@ -2379,38 +2382,31 @@ array_eq(PG_FUNCTION_ARGS)
/*
* We arrange to look up the equality function only once per
* series of calls, assuming the element type doesn't change
* underneath us.
* underneath us. The typcache is used so that we have no
* memory leakage when being used as an index support function.
*/
my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
if (my_extra == NULL)
typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
if (typentry == NULL ||
typentry->type_id != element_type)
{
ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
my_extra->element_type = InvalidOid;
typentry = lookup_type_cache(element_type,
TYPECACHE_EQ_OPR_FINFO);
if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not identify an equality operator for type %s",
format_type_be(element_type))));
fcinfo->flinfo->fn_extra = (void *) typentry;
}
if (my_extra->element_type != element_type)
{
Oid opfuncid = equality_oper_funcid(element_type);
get_typlenbyvalalign(element_type,
&my_extra->typlen,
&my_extra->typbyval,
&my_extra->typalign);
fmgr_info_cxt(opfuncid, &my_extra->proc,
ae_fmgr_info->fn_mcxt);
my_extra->element_type = element_type;
}
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
typlen = typentry->typlen;
typbyval = typentry->typbyval;
typalign = typentry->typalign;
/*
* apply the operator to each pair of array elements.
*/
MemSet(&locfcinfo, 0, sizeof(locfcinfo));
locfcinfo.flinfo = &my_extra->proc;
locfcinfo.flinfo = &typentry->eq_opr_finfo;
locfcinfo.nargs = 2;
/* Loop over source data */
@@ -2519,23 +2515,14 @@ array_cmp(FunctionCallInfo fcinfo)
int nitems1 = ArrayGetNItems(ndims1, dims1);
int nitems2 = ArrayGetNItems(ndims2, dims2);
Oid element_type = ARR_ELEMTYPE(array1);
FmgrInfo *ac_fmgr_info = fcinfo->flinfo;
int result = 0;
TypeCacheEntry *typentry;
int typlen;
bool typbyval;
char typalign;
int min_nitems;
int i;
typedef struct
{
Oid element_type;
int16 typlen;
bool typbyval;
char typalign;
FmgrInfo eqproc;
FmgrInfo ordproc;
} ac_extra;
ac_extra *my_extra;
FunctionCallInfoData locfcinfo;
if (element_type != ARR_ELEMTYPE(array2))
ereport(ERROR,
@@ -2543,37 +2530,34 @@ array_cmp(FunctionCallInfo fcinfo)
errmsg("cannot compare arrays of different element types")));
/*
* We arrange to look up the element type info and related functions
* only once per series of calls, assuming the element type doesn't
* change underneath us.
* We arrange to look up the comparison function only once per series of
* calls, assuming the element type doesn't change underneath us.
* The typcache is used so that we have no memory leakage when being used
* as an index support function.
*/
my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
if (my_extra == NULL)
typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
if (typentry == NULL ||
typentry->type_id != element_type)
{
ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
sizeof(ac_extra));
my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
my_extra->element_type = InvalidOid;
typentry = lookup_type_cache(element_type,
TYPECACHE_CMP_PROC_FINFO);
if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not identify a comparison function for type %s",
format_type_be(element_type))));
fcinfo->flinfo->fn_extra = (void *) typentry;
}
typlen = typentry->typlen;
typbyval = typentry->typbyval;
typalign = typentry->typalign;
if (my_extra->element_type != element_type)
{
Oid eqfuncid = equality_oper_funcid(element_type);
Oid ordfuncid = ordering_oper_funcid(element_type);
get_typlenbyvalalign(element_type,
&my_extra->typlen,
&my_extra->typbyval,
&my_extra->typalign);
fmgr_info_cxt(eqfuncid, &my_extra->eqproc,
ac_fmgr_info->fn_mcxt);
fmgr_info_cxt(ordfuncid, &my_extra->ordproc,
ac_fmgr_info->fn_mcxt);
my_extra->element_type = element_type;
}
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
/*
* apply the operator to each pair of array elements.
*/
MemSet(&locfcinfo, 0, sizeof(locfcinfo));
locfcinfo.flinfo = &typentry->cmp_proc_finfo;
locfcinfo.nargs = 2;
/* Loop over source data */
min_nitems = Min(nitems1, nitems2);
@@ -2581,7 +2565,7 @@ array_cmp(FunctionCallInfo fcinfo)
{
Datum elt1;
Datum elt2;
Datum opresult;
int32 cmpresult;
/* Get element pair */
elt1 = fetch_att(p1, typbyval, typlen);
@@ -2594,15 +2578,17 @@ array_cmp(FunctionCallInfo fcinfo)
p2 = (char *) att_align(p2, typalign);
/* Compare the pair of elements */
locfcinfo.arg[0] = elt1;
locfcinfo.arg[1] = elt2;
locfcinfo.argnull[0] = false;
locfcinfo.argnull[1] = false;
locfcinfo.isnull = false;
cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
/* are they equal */
opresult = FunctionCall2(&my_extra->eqproc, elt1, elt2);
if (DatumGetBool(opresult))
continue;
if (cmpresult == 0)
continue; /* equal */
/* nope, see if arg1 is less than arg2 */
opresult = FunctionCall2(&my_extra->ordproc, elt1, elt2);
if (DatumGetBool(opresult))
if (cmpresult < 0)
{
/* arg1 is less than arg2 */
result = -1;

View File

@@ -9,7 +9,7 @@
* workings can be found in the book "Software Solutions in C" by
* Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
*
* $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.59 2003/07/27 04:53:03 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.60 2003/08/17 19:58:05 tgl Exp $
*/
#include "postgres.h"
@@ -342,6 +342,9 @@ cash_send(PG_FUNCTION_ARGS)
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
* Comparison functions
*/
Datum
cash_eq(PG_FUNCTION_ARGS)
@@ -397,6 +400,20 @@ cash_ge(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(c1 >= c2);
}
Datum
cash_cmp(PG_FUNCTION_ARGS)
{
Cash c1 = PG_GETARG_CASH(0);
Cash c2 = PG_GETARG_CASH(1);
if (c1 > c2)
PG_RETURN_INT32(1);
else if (c1 == c2)
PG_RETURN_INT32(0);
else
PG_RETURN_INT32(-1);
}
/* cash_pl()
* Add two cash values.

View File

@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.113 2003/08/04 02:40:05 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.114 2003/08/17 19:58:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -505,11 +505,11 @@ abstime_finite(PG_FUNCTION_ARGS)
static int
abstime_cmp_internal(AbsoluteTime a, AbsoluteTime b)
{
/*
* We consider all INVALIDs to be equal and larger than any non-INVALID.
* This is somewhat arbitrary; the important thing is to have a
* consistent sort order.
*/
/*
* We consider all INVALIDs to be equal and larger than any non-INVALID.
* This is somewhat arbitrary; the important thing is to have a
* consistent sort order.
*/
if (a == INVALID_ABSTIME)
{
if (b == INVALID_ABSTIME)
@@ -904,7 +904,7 @@ tintervalout(PG_FUNCTION_ARGS)
char *i_str,
*p;
i_str = (char *) palloc(T_INTERVAL_LEN); /* ['...' '...'] */
i_str = (char *) palloc(T_INTERVAL_LEN); /* ["..." "..."] */
strcpy(i_str, "[\"");
if (interval->status == T_INTERVAL_INVAL)
strcat(i_str, INVALID_INTERVAL_STR);
@@ -920,7 +920,7 @@ tintervalout(PG_FUNCTION_ARGS)
strcat(i_str, p);
pfree(p);
}
strcat(i_str, "\"]\0");
strcat(i_str, "\"]");
PG_RETURN_CSTRING(i_str);
}
@@ -1190,22 +1190,42 @@ timenow(PG_FUNCTION_ARGS)
}
/*
* reltimeeq - returns true iff arguments are equal
* reltimene - returns true iff arguments are not equal
* reltimelt - returns true iff t1 less than t2
* reltimegt - returns true iff t1 greater than t2
* reltimele - returns true iff t1 less than or equal to t2
* reltimege - returns true iff t1 greater than or equal to t2
* reltime comparison routines
*/
static int
reltime_cmp_internal(RelativeTime a, RelativeTime b)
{
/*
* We consider all INVALIDs to be equal and larger than any non-INVALID.
* This is somewhat arbitrary; the important thing is to have a
* consistent sort order.
*/
if (a == INVALID_RELTIME)
{
if (b == INVALID_RELTIME)
return 0; /* INVALID = INVALID */
else
return 1; /* INVALID > non-INVALID */
}
if (b == INVALID_RELTIME)
return -1; /* non-INVALID < INVALID */
if (a > b)
return 1;
else if (a == b)
return 0;
else
return -1;
}
Datum
reltimeeq(PG_FUNCTION_ARGS)
{
RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
PG_RETURN_BOOL(false);
PG_RETURN_BOOL(t1 == t2);
PG_RETURN_BOOL(reltime_cmp_internal(t1, t2) == 0);
}
Datum
@@ -1214,9 +1234,7 @@ reltimene(PG_FUNCTION_ARGS)
RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
PG_RETURN_BOOL(false);
PG_RETURN_BOOL(t1 != t2);
PG_RETURN_BOOL(reltime_cmp_internal(t1, t2) != 0);
}
Datum
@@ -1225,9 +1243,7 @@ reltimelt(PG_FUNCTION_ARGS)
RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
PG_RETURN_BOOL(false);
PG_RETURN_BOOL(t1 < t2);
PG_RETURN_BOOL(reltime_cmp_internal(t1, t2) < 0);
}
Datum
@@ -1236,9 +1252,7 @@ reltimegt(PG_FUNCTION_ARGS)
RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
PG_RETURN_BOOL(false);
PG_RETURN_BOOL(t1 > t2);
PG_RETURN_BOOL(reltime_cmp_internal(t1, t2) > 0);
}
Datum
@@ -1247,9 +1261,7 @@ reltimele(PG_FUNCTION_ARGS)
RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
PG_RETURN_BOOL(false);
PG_RETURN_BOOL(t1 <= t2);
PG_RETURN_BOOL(reltime_cmp_internal(t1, t2) <= 0);
}
Datum
@@ -1258,9 +1270,16 @@ reltimege(PG_FUNCTION_ARGS)
RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
PG_RETURN_BOOL(false);
PG_RETURN_BOOL(t1 >= t2);
PG_RETURN_BOOL(reltime_cmp_internal(t1, t2) >= 0);
}
Datum
btreltimecmp(PG_FUNCTION_ARGS)
{
RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
PG_RETURN_INT32(reltime_cmp_internal(t1, t2));
}
@@ -1287,34 +1306,62 @@ tintervalsame(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(false);
}
/*
* tintervaleq - returns true iff interval i1 is equal to interval i2
* Check length of intervals.
* tinterval comparison routines
*
* Note: comparison is based on the lengths of the intervals, not on
* endpoint value. This is pretty bogus, but since it's only a legacy
* datatype I'm not going to propose changing it.
*/
static int
tinterval_cmp_internal(TimeInterval a, TimeInterval b)
{
bool a_invalid;
bool b_invalid;
AbsoluteTime a_len;
AbsoluteTime b_len;
/*
* We consider all INVALIDs to be equal and larger than any non-INVALID.
* This is somewhat arbitrary; the important thing is to have a
* consistent sort order.
*/
a_invalid = ((a->status == T_INTERVAL_INVAL) ||
(a->data[0] == INVALID_ABSTIME) ||
(a->data[1] == INVALID_ABSTIME));
b_invalid = ((b->status == T_INTERVAL_INVAL) ||
(b->data[0] == INVALID_ABSTIME) ||
(b->data[1] == INVALID_ABSTIME));
if (a_invalid)
{
if (b_invalid)
return 0; /* INVALID = INVALID */
else
return 1; /* INVALID > non-INVALID */
}
if (b_invalid)
return -1; /* non-INVALID < INVALID */
a_len = a->data[1] - a->data[0];
b_len = b->data[1] - b->data[0];
if (a_len > b_len)
return 1;
else if (a_len == b_len)
return 0;
else
return -1;
}
Datum
tintervaleq(PG_FUNCTION_ARGS)
{
TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
AbsoluteTime t10,
t11,
t20,
t21;
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
PG_RETURN_BOOL(false);
t10 = i1->data[0];
t11 = i1->data[1];
t20 = i2->data[0];
t21 = i2->data[1];
if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME)
|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
PG_RETURN_BOOL(false);
PG_RETURN_BOOL((t11 - t10) == (t21 - t20));
PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) == 0);
}
Datum
@@ -1322,24 +1369,8 @@ tintervalne(PG_FUNCTION_ARGS)
{
TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
AbsoluteTime t10,
t11,
t20,
t21;
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
PG_RETURN_BOOL(false);
t10 = i1->data[0];
t11 = i1->data[1];
t20 = i2->data[0];
t21 = i2->data[1];
if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME)
|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
PG_RETURN_BOOL(false);
PG_RETURN_BOOL((t11 - t10) != (t21 - t20));
PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) != 0);
}
Datum
@@ -1347,24 +1378,8 @@ tintervallt(PG_FUNCTION_ARGS)
{
TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
AbsoluteTime t10,
t11,
t20,
t21;
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
PG_RETURN_BOOL(false);
t10 = i1->data[0];
t11 = i1->data[1];
t20 = i2->data[0];
t21 = i2->data[1];
if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME)
|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
PG_RETURN_BOOL(false);
PG_RETURN_BOOL((t11 - t10) < (t21 - t20));
PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) < 0);
}
Datum
@@ -1372,24 +1387,8 @@ tintervalle(PG_FUNCTION_ARGS)
{
TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
AbsoluteTime t10,
t11,
t20,
t21;
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
PG_RETURN_BOOL(false);
t10 = i1->data[0];
t11 = i1->data[1];
t20 = i2->data[0];
t21 = i2->data[1];
if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME)
|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
PG_RETURN_BOOL(false);
PG_RETURN_BOOL((t11 - t10) <= (t21 - t20));
PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) <= 0);
}
Datum
@@ -1397,24 +1396,8 @@ tintervalgt(PG_FUNCTION_ARGS)
{
TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
AbsoluteTime t10,
t11,
t20,
t21;
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
PG_RETURN_BOOL(false);
t10 = i1->data[0];
t11 = i1->data[1];
t20 = i2->data[0];
t21 = i2->data[1];
if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME)
|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
PG_RETURN_BOOL(false);
PG_RETURN_BOOL((t11 - t10) > (t21 - t20));
PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) > 0);
}
Datum
@@ -1422,24 +1405,17 @@ tintervalge(PG_FUNCTION_ARGS)
{
TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
AbsoluteTime t10,
t11,
t20,
t21;
if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
PG_RETURN_BOOL(false);
PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) >= 0);
}
t10 = i1->data[0];
t11 = i1->data[1];
t20 = i2->data[0];
t21 = i2->data[1];
Datum
bttintervalcmp(PG_FUNCTION_ARGS)
{
TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME)
|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
PG_RETURN_BOOL(false);
PG_RETURN_BOOL((t11 - t10) >= (t21 - t20));
PG_RETURN_INT32(tinterval_cmp_internal(i1, i2));
}
@@ -1652,7 +1628,7 @@ istinterval(char *i_string,
break;
}
p++;
/* skip leading blanks up to "'" */
/* skip leading blanks up to '"' */
while ((c = *p) != '\0')
{
if (IsSpace(c))
@@ -1680,10 +1656,10 @@ istinterval(char *i_string,
/* get the first date */
*i_start = DatumGetAbsoluteTime(DirectFunctionCall1(abstimein,
CStringGetDatum(p)));
/* rechange NULL at the end of the first date to a "'" */
/* rechange NULL at the end of the first date to a '"' */
*p1 = '"';
p = ++p1;
/* skip blanks up to "'", beginning of second date */
/* skip blanks up to '"', beginning of second date */
while ((c = *p) != '\0')
{
if (IsSpace(c))
@@ -1708,7 +1684,7 @@ istinterval(char *i_string,
/* get the second date */
*i_end = DatumGetAbsoluteTime(DirectFunctionCall1(abstimein,
CStringGetDatum(p)));
/* rechange NULL at the end of the first date to a ''' */
/* rechange NULL at the end of the first date to a '"' */
*p1 = '"';
p = ++p1;
/* skip blanks up to ']' */

View File

@@ -17,7 +17,7 @@
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
*
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.54 2003/08/04 02:40:05 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.55 2003/08/17 19:58:05 tgl Exp $
*
* ----------
*/
@@ -39,6 +39,7 @@
#include "parser/parse_oper.h"
#include "rewrite/rewriteHandler.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
#include "miscadmin.h"
@@ -48,7 +49,6 @@
*/
#define RI_INIT_QUERYHASHSIZE 128
#define RI_INIT_OPREQHASHSIZE 128
#define RI_MATCH_TYPE_UNSPECIFIED 0
#define RI_MATCH_TYPE_FULL 1
@@ -109,20 +109,11 @@ typedef struct RI_QueryHashEntry
} RI_QueryHashEntry;
typedef struct RI_OpreqHashEntry
{
Oid typeid;
FmgrInfo oprfmgrinfo;
} RI_OpreqHashEntry;
/* ----------
* Local data
* ----------
*/
static HTAB *ri_query_cache = (HTAB *) NULL;
static HTAB *ri_opreq_cache = (HTAB *) NULL;
/* ----------
@@ -3197,8 +3188,8 @@ ri_NullCheck(Relation rel, HeapTuple tup, RI_QueryKey *key, int pairidx)
/* ----------
* ri_InitHashTables -
*
* Initialize our internal hash tables for prepared
* query plans and equal operators.
* Initialize our internal hash table for prepared
* query plans.
* ----------
*/
static void
@@ -3212,12 +3203,6 @@ ri_InitHashTables(void)
ctl.hash = tag_hash;
ri_query_cache = hash_create("RI query cache", RI_INIT_QUERYHASHSIZE,
&ctl, HASH_ELEM | HASH_FUNCTION);
ctl.keysize = sizeof(Oid);
ctl.entrysize = sizeof(RI_OpreqHashEntry);
ctl.hash = tag_hash;
ri_opreq_cache = hash_create("RI OpReq cache", RI_INIT_OPREQHASHSIZE,
&ctl, HASH_ELEM | HASH_FUNCTION);
}
@@ -3438,57 +3423,22 @@ ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup, HeapTuple newtup,
static bool
ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue)
{
RI_OpreqHashEntry *entry;
bool found;
TypeCacheEntry *typentry;
/*
* On the first call initialize the hashtable
* Find the data type in the typcache, and ask for eq_opr info.
*/
if (!ri_opreq_cache)
ri_InitHashTables();
typentry = lookup_type_cache(typeid, TYPECACHE_EQ_OPR_FINFO);
/*
* Try to find the '=' operator for this type in our cache
*/
entry = (RI_OpreqHashEntry *) hash_search(ri_opreq_cache,
(void *) &typeid,
HASH_FIND, NULL);
/*
* If not found, lookup the operator, then do the function manager
* lookup, and remember that info.
*/
if (!entry)
{
Oid opr_proc;
FmgrInfo finfo;
opr_proc = equality_oper_funcid(typeid);
/*
* Since fmgr_info could fail, call it *before* creating the
* hashtable entry --- otherwise we could ereport leaving an
* incomplete entry in the hashtable. Also, because this will be
* a permanent table entry, we must make sure any subsidiary
* structures of the fmgr record are kept in TopMemoryContext.
*/
fmgr_info_cxt(opr_proc, &finfo, TopMemoryContext);
entry = (RI_OpreqHashEntry *) hash_search(ri_opreq_cache,
(void *) &typeid,
HASH_ENTER, &found);
if (entry == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
entry->typeid = typeid;
memcpy(&(entry->oprfmgrinfo), &finfo, sizeof(FmgrInfo));
}
if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not identify an equality operator for type %s",
format_type_be(typeid))));
/*
* Call the type specific '=' function
*/
return DatumGetBool(FunctionCall2(&(entry->oprfmgrinfo),
return DatumGetBool(FunctionCall2(&(typentry->eq_opr_finfo),
oldvalue, newvalue));
}

View File

@@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.151 2003/08/11 23:04:49 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.152 2003/08/17 19:58:05 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -69,6 +69,7 @@
#include "utils/array.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
/* ----------
@@ -1815,21 +1816,24 @@ get_select_query_def(Query *query, deparse_context *context,
SortClause *srt = (SortClause *) lfirst(l);
Node *sortexpr;
Oid sortcoltype;
char *opname;
TypeCacheEntry *typentry;
appendStringInfo(buf, sep);
sortexpr = get_rule_sortgroupclause(srt, query->targetList,
force_colno, context);
sortcoltype = exprType(sortexpr);
opname = generate_operator_name(srt->sortop,
sortcoltype, sortcoltype);
if (strcmp(opname, "<") != 0)
{
if (strcmp(opname, ">") == 0)
appendStringInfo(buf, " DESC");
else
appendStringInfo(buf, " USING %s", opname);
}
/* See whether operator is default < or > for datatype */
typentry = lookup_type_cache(sortcoltype,
TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
if (srt->sortop == typentry->lt_opr)
/* ASC is default, so emit nothing */ ;
else if (srt->sortop == typentry->gt_opr)
appendStringInfo(buf, " DESC");
else
appendStringInfo(buf, " USING %s",
generate_operator_name(srt->sortop,
sortcoltype,
sortcoltype));
sep = ", ";
}
}
@@ -4032,6 +4036,15 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
if (!HeapTupleIsValid(ht_opc))
elog(ERROR, "cache lookup failed for opclass %u", opclass);
opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
/* Special case for ARRAY_OPS: pretend it is default for any array type */
if (OidIsValid(actual_datatype))
{
if (opcrec->opcintype == ANYARRAYOID &&
OidIsValid(get_element_type(actual_datatype)))
actual_datatype = opcrec->opcintype;
}
if (actual_datatype != opcrec->opcintype || !opcrec->opcdefault)
{
/* Okay, we need the opclass name. Do we need to qualify it? */

View File

@@ -4,7 +4,7 @@
# Makefile for utils/cache
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/utils/cache/Makefile,v 1.17 2002/12/13 19:45:56 tgl Exp $
# $Header: /cvsroot/pgsql/src/backend/utils/cache/Makefile,v 1.18 2003/08/17 19:58:06 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -12,7 +12,7 @@ subdir = src/backend/utils/cache
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = catcache.o inval.o relcache.o syscache.o lsyscache.o
OBJS = catcache.o inval.o relcache.o syscache.o lsyscache.o typcache.o
all: SUBSYS.o

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.106 2003/08/11 23:04:49 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.107 2003/08/17 19:58:06 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -122,7 +122,6 @@ get_op_hash_function(Oid opno)
{
CatCList *catlist;
int i;
HeapTuple tuple;
Oid opclass = InvalidOid;
/*
@@ -137,10 +136,8 @@ get_op_hash_function(Oid opno)
for (i = 0; i < catlist->n_members; i++)
{
Form_pg_amop aform;
tuple = &catlist->members[i]->tuple;
aform = (Form_pg_amop) GETSTRUCT(tuple);
HeapTuple tuple = &catlist->members[i]->tuple;
Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
if (aform->amopstrategy == HTEqualStrategyNumber &&
opclass_is_hash(aform->amopclaid))
@@ -155,20 +152,7 @@ get_op_hash_function(Oid opno)
if (OidIsValid(opclass))
{
/* Found a suitable opclass, get its hash support function */
tuple = SearchSysCache(AMPROCNUM,
ObjectIdGetDatum(opclass),
Int16GetDatum(HASHPROC),
0, 0);
if (HeapTupleIsValid(tuple))
{
Form_pg_amproc aform = (Form_pg_amproc) GETSTRUCT(tuple);
RegProcedure result;
result = aform->amproc;
ReleaseSysCache(tuple);
Assert(RegProcedureIsValid(result));
return result;
}
return get_opclass_proc(opclass, HASHPROC);
}
/* Didn't find a match... */
@@ -176,6 +160,35 @@ get_op_hash_function(Oid opno)
}
/* ---------- AMPROC CACHES ---------- */
/*
* get_opclass_proc
* Get the OID of the specified support function
* for the specified opclass.
*
* Returns InvalidOid if there is no pg_amproc entry for the given keys.
*/
Oid
get_opclass_proc(Oid opclass, int16 procnum)
{
HeapTuple tp;
Form_pg_amproc amproc_tup;
RegProcedure result;
tp = SearchSysCache(AMPROCNUM,
ObjectIdGetDatum(opclass),
Int16GetDatum(procnum),
0, 0);
if (!HeapTupleIsValid(tp))
return InvalidOid;
amproc_tup = (Form_pg_amproc) GETSTRUCT(tp);
result = amproc_tup->amproc;
ReleaseSysCache(tp);
return result;
}
/* ---------- ATTRIBUTE CACHES ---------- */
/*

292
src/backend/utils/cache/typcache.c vendored Normal file
View File

@@ -0,0 +1,292 @@
/*-------------------------------------------------------------------------
*
* typcache.c
* POSTGRES type cache code
*
* The type cache exists to speed lookup of certain information about data
* types that is not directly available from a type's pg_type row. In
* particular, we use a type's default btree opclass, or the default hash
* opclass if no btree opclass exists, to determine which operators should
* be used for grouping and sorting the type (GROUP BY, ORDER BY ASC/DESC).
*
* Several seemingly-odd choices have been made to support use of the type
* cache by the generic array comparison routines array_eq() and array_cmp().
* Because these routines are used as index support operations, they cannot
* leak memory. To allow them to execute efficiently, all information that
* either of them would like to re-use across calls is made available in the
* type cache.
*
* Once created, a type cache entry lives as long as the backend does, so
* there is no need for a call to release a cache entry. (For present uses,
* it would be okay to flush type cache entries at the ends of transactions,
* if we needed to reclaim space.)
*
* There is presently no provision for clearing out a cache entry if the
* stored data becomes obsolete. (The code will work if a type acquires
* opclasses it didn't have before while a backend runs --- but not if the
* definition of an existing opclass is altered.) However, the relcache
* doesn't cope with opclasses changing under it, either, so this seems
* a low-priority problem.
*
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/typcache.c,v 1.1 2003/08/17 19:58:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/hash.h"
#include "access/nbtree.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/pg_am.h"
#include "catalog/pg_opclass.h"
#include "parser/parse_coerce.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/fmgroids.h"
#include "utils/hsearch.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
static HTAB *TypeCacheHash = NULL;
static Oid lookup_default_opclass(Oid type_id, Oid am_id);
/*
* lookup_type_cache
*
* Fetch the type cache entry for the specified datatype, and make sure that
* all the fields requested by bits in 'flags' are valid.
*
* The result is never NULL --- we will elog() if the passed type OID is
* invalid. Note however that we may fail to find one or more of the
* requested opclass-dependent fields; the caller needs to check whether
* the fields are InvalidOid or not.
*/
TypeCacheEntry *
lookup_type_cache(Oid type_id, int flags)
{
TypeCacheEntry *typentry;
bool found;
if (TypeCacheHash == NULL)
{
/* First time through: initialize the hash table */
HASHCTL ctl;
if (!CacheMemoryContext)
CreateCacheMemoryContext();
MemSet(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(Oid);
ctl.entrysize = sizeof(TypeCacheEntry);
ctl.hash = tag_hash;
TypeCacheHash = hash_create("Type information cache", 64,
&ctl, HASH_ELEM | HASH_FUNCTION);
}
/* Try to look up an existing entry */
typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
(void *) &type_id,
HASH_FIND, NULL);
if (typentry == NULL)
{
/*
* If we didn't find one, we want to make one. But first get the
* required info from the pg_type row, just to make sure we don't
* make a cache entry for an invalid type OID.
*/
int16 typlen;
bool typbyval;
char typalign;
get_typlenbyvalalign(type_id, &typlen, &typbyval, &typalign);
typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
(void *) &type_id,
HASH_ENTER, &found);
if (typentry == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
Assert(!found); /* it wasn't there a moment ago */
MemSet(typentry, 0, sizeof(TypeCacheEntry));
typentry->type_id = type_id;
typentry->typlen = typlen;
typentry->typbyval = typbyval;
typentry->typalign = typalign;
}
/* If we haven't already found the opclass, try to do so */
if (flags != 0 && typentry->btree_opc == InvalidOid)
{
typentry->btree_opc = lookup_default_opclass(type_id,
BTREE_AM_OID);
/* Only care about hash opclass if no btree opclass... */
if (typentry->btree_opc == InvalidOid)
{
if (typentry->hash_opc == InvalidOid)
typentry->hash_opc = lookup_default_opclass(type_id,
HASH_AM_OID);
}
else
{
/*
* If we find a btree opclass where previously we only found
* a hash opclass, forget the hash equality operator so we
* can use the btree operator instead.
*/
typentry->eq_opr = InvalidOid;
typentry->eq_opr_finfo.fn_oid = InvalidOid;
}
}
/* Look for requested operators and functions */
if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
typentry->eq_opr == InvalidOid)
{
if (typentry->btree_opc != InvalidOid)
typentry->eq_opr = get_opclass_member(typentry->btree_opc,
BTEqualStrategyNumber);
if (typentry->eq_opr == InvalidOid &&
typentry->hash_opc != InvalidOid)
typentry->eq_opr = get_opclass_member(typentry->hash_opc,
HTEqualStrategyNumber);
}
if ((flags & TYPECACHE_LT_OPR) && typentry->lt_opr == InvalidOid)
{
if (typentry->btree_opc != InvalidOid)
typentry->lt_opr = get_opclass_member(typentry->btree_opc,
BTLessStrategyNumber);
}
if ((flags & TYPECACHE_GT_OPR) && typentry->gt_opr == InvalidOid)
{
if (typentry->btree_opc != InvalidOid)
typentry->gt_opr = get_opclass_member(typentry->btree_opc,
BTGreaterStrategyNumber);
}
if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) &&
typentry->cmp_proc == InvalidOid)
{
if (typentry->btree_opc != InvalidOid)
typentry->cmp_proc = get_opclass_proc(typentry->btree_opc,
BTORDER_PROC);
}
/*
* Set up fmgr lookup info as requested
*
* Note: we tell fmgr the finfo structures live in CacheMemoryContext,
* which is not quite right (they're really in DynaHashContext) but this
* will do for our purposes.
*/
if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
typentry->eq_opr_finfo.fn_oid == InvalidOid &&
typentry->eq_opr != InvalidOid)
{
Oid eq_opr_func;
eq_opr_func = get_opcode(typentry->eq_opr);
if (eq_opr_func != InvalidOid)
fmgr_info_cxt(eq_opr_func, &typentry->eq_opr_finfo,
CacheMemoryContext);
}
if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
typentry->cmp_proc != InvalidOid)
{
fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
CacheMemoryContext);
}
return typentry;
}
/*
* lookup_default_opclass
*
* Given the OIDs of a datatype and an access method, find the default
* operator class, if any. Returns InvalidOid if there is none.
*/
static Oid
lookup_default_opclass(Oid type_id, Oid am_id)
{
int nexact = 0;
int ncompatible = 0;
Oid exactOid = InvalidOid;
Oid compatibleOid = InvalidOid;
Relation rel;
ScanKeyData skey[1];
SysScanDesc scan;
HeapTuple tup;
/* If it's a domain, look at the base type instead */
type_id = getBaseType(type_id);
/*
* We scan through all the opclasses available for the access method,
* looking for one that is marked default and matches the target type
* (either exactly or binary-compatibly, but prefer an exact match).
*
* We could find more than one binary-compatible match, in which case we
* require the user to specify which one he wants. If we find more
* than one exact match, then someone put bogus entries in pg_opclass.
*
* This is the same logic as GetDefaultOpClass() in indexcmds.c, except
* that we consider all opclasses, regardless of the current search path.
*/
rel = heap_openr(OperatorClassRelationName, AccessShareLock);
ScanKeyEntryInitialize(&skey[0], 0x0,
Anum_pg_opclass_opcamid, F_OIDEQ,
ObjectIdGetDatum(am_id));
scan = systable_beginscan(rel, OpclassAmNameNspIndex, true,
SnapshotNow, 1, skey);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
if (opclass->opcdefault)
{
if (opclass->opcintype == type_id)
{
nexact++;
exactOid = HeapTupleGetOid(tup);
}
else if (IsBinaryCoercible(type_id, opclass->opcintype))
{
ncompatible++;
compatibleOid = HeapTupleGetOid(tup);
}
}
}
systable_endscan(scan);
heap_close(rel, AccessShareLock);
if (nexact == 1)
return exactOid;
if (nexact != 0)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("there are multiple default operator classes for data type %s",
format_type_be(type_id))));
if (ncompatible == 1)
return compatibleOid;
return InvalidOid;
}

View File

@@ -78,7 +78,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.36 2003/08/04 02:40:09 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.37 2003/08/17 19:58:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -88,7 +88,6 @@
#include "access/heapam.h"
#include "access/nbtree.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_operator.h"
#include "miscadmin.h"
#include "utils/catcache.h"
@@ -1754,26 +1753,17 @@ SelectSortFunction(Oid sortOperator,
if (OidIsValid(opclass))
{
/* Found a suitable opclass, get its comparator support function */
tuple = SearchSysCache(AMPROCNUM,
ObjectIdGetDatum(opclass),
Int16GetDatum(BTORDER_PROC),
0, 0);
if (HeapTupleIsValid(tuple))
{
Form_pg_amproc aform = (Form_pg_amproc) GETSTRUCT(tuple);
*sortFunction = aform->amproc;
ReleaseSysCache(tuple);
Assert(RegProcedureIsValid(*sortFunction));
return;
}
*sortFunction = get_opclass_proc(opclass, BTORDER_PROC);
Assert(RegProcedureIsValid(*sortFunction));
return;
}
/*
* Can't find a comparator, so use the operator as-is. Decide whether
* it is forward or reverse sort by looking at its name (grotty, but
* this only matters for deciding which end NULLs should get sorted
* to).
* to). XXX possibly better idea: see whether its selectivity function
* is scalargtcmp?
*/
tuple = SearchSysCache(OPEROID,
ObjectIdGetDatum(sortOperator),