From ec646dbc65afc8c55118cb3fb75cb6fd18d78dd8 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 17 Aug 2003 19:58:06 +0000 Subject: [PATCH] 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. --- src/backend/commands/analyze.c | 15 +- src/backend/commands/indexcmds.c | 5 +- src/backend/commands/opclasscmds.c | 6 +- src/backend/nodes/copyfuncs.c | 13 +- src/backend/nodes/equalfuncs.c | 9 +- src/backend/optimizer/plan/createplan.c | 7 +- src/backend/optimizer/plan/planner.c | 6 +- src/backend/parser/gram.y | 45 ++-- src/backend/parser/parse_clause.c | 42 +++- src/backend/parser/parse_oper.c | 212 ++++++++++------ src/backend/utils/adt/acl.c | 19 +- src/backend/utils/adt/arrayfuncs.c | 132 +++++----- src/backend/utils/adt/cash.c | 19 +- src/backend/utils/adt/nabstime.c | 254 +++++++++----------- src/backend/utils/adt/ri_triggers.c | 76 +----- src/backend/utils/adt/ruleutils.c | 35 ++- src/backend/utils/cache/Makefile | 4 +- src/backend/utils/cache/lsyscache.c | 53 ++-- src/backend/utils/cache/typcache.c | 292 +++++++++++++++++++++++ src/backend/utils/sort/tuplesort.c | 22 +- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_amop.h | 34 ++- src/include/catalog/pg_amproc.h | 6 +- src/include/catalog/pg_opclass.h | 6 +- src/include/catalog/pg_operator.h | 5 +- src/include/catalog/pg_proc.h | 10 +- src/include/nodes/nodes.h | 4 +- src/include/nodes/parsenodes.h | 19 +- src/include/parser/parse_clause.h | 5 +- src/include/parser/parse_oper.h | 7 +- src/include/utils/acl.h | 3 +- src/include/utils/builtins.h | 4 +- src/include/utils/cash.h | 1 + src/include/utils/lsyscache.h | 3 +- src/include/utils/typcache.h | 66 +++++ src/test/regress/expected/circle.out | 2 +- src/test/regress/expected/geometry.out | 2 +- src/test/regress/expected/geometry_1.out | 2 +- src/test/regress/sql/circle.sql | 3 +- src/test/regress/sql/geometry.sql | 3 +- 40 files changed, 964 insertions(+), 491 deletions(-) create mode 100644 src/backend/utils/cache/typcache.c create mode 100644 src/include/utils/typcache.h diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 2272d7bf3f..aea5e45b23 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -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; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 36baf4690e..45f4c3b4f7 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.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; diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index 0677751ead..a93af3e604 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -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) { diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index fa1c567df0..55076ec48d 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.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); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 5ad5dab1d3..e3a59f4739 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 2f7fd02fb4..634cfb3a57 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -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); diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 9eea1c8d20..c29d127acf 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -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) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index d3060b76c7..ef6f8a80c5 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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 func_name handler_name qual_Op qual_all_Op OptUseOp +%type func_name handler_name qual_Op qual_all_Op opt_class opt_validator %type qualified_name OptConstrFromTable @@ -278,7 +278,7 @@ static void doNegateFloat(Value *v); %type NumericOnly FloatOnly IntegerOnly %type columnref %type alias_clause -%type sortby +%type sortby %type index_elem %type table_ref %type 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*/ } ; diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index ebc3ed23ee..b31e70205d 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -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); } diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index e5d2ab2d05..244cd76964 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.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 - { - /* - * 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); - } + 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), @@ -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) + Oid elem_type = get_element_type(argtype); + + if (OidIsValid(elem_type)) { - ReleaseSysCache(optup); - return SearchSysCache(OPEROID, - ObjectIdGetDatum(ARRAY_LT_OP), - 0, 0, 0); + optup = ordering_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)) { - /* - * 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; + 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; + } - 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 (!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; } diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 504e8f5565..8d5a675006 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -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 * diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 37504718ad..46f7881d64 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -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; - } - - 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; + 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; } - 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; diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c index e33aad28d6..c4fc14134f 100644 --- a/src/backend/utils/adt/cash.c +++ b/src/backend/utils/adt/cash.c @@ -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. diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c index a4f3b061e6..f694349db7 100644 --- a/src/backend/utils/adt/nabstime.c +++ b/src/backend/utils/adt/nabstime.c @@ -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,59 +1306,71 @@ 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. */ -Datum -tintervaleq(PG_FUNCTION_ARGS) +static int +tinterval_cmp_internal(TimeInterval a, TimeInterval b) { - TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0); - TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1); - AbsoluteTime t10, - t11, - t20, - t21; + bool a_invalid; + bool b_invalid; + AbsoluteTime a_len; + AbsoluteTime b_len; - if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) - PG_RETURN_BOOL(false); + /* + * 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 */ + } - t10 = i1->data[0]; - t11 = i1->data[1]; - t20 = i2->data[0]; - t21 = i2->data[1]; + if (b_invalid) + return -1; /* non-INVALID < INVALID */ - if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME) - || (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME)) - PG_RETURN_BOOL(false); + a_len = a->data[1] - a->data[0]; + b_len = b->data[1] - b->data[0]; - PG_RETURN_BOOL((t11 - t10) == (t21 - t20)); + if (a_len > b_len) + return 1; + else if (a_len == b_len) + return 0; + else + return -1; } Datum -tintervalne(PG_FUNCTION_ARGS) +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]; + PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) == 0); +} - if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME) - || (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME)) - PG_RETURN_BOOL(false); +Datum +tintervalne(PG_FUNCTION_ARGS) +{ + TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0); + TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1); - 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); - t10 = i1->data[0]; - t11 = i1->data[1]; - t20 = i2->data[0]; - t21 = i2->data[1]; + PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) >= 0); +} - if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME) - || (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME)) - PG_RETURN_BOOL(false); +Datum +bttintervalcmp(PG_FUNCTION_ARGS) +{ + TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0); + TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1); - 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 ']' */ diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 7476204d89..55a6944971 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -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)); } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 83989292d6..5504251bc4 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.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? */ diff --git a/src/backend/utils/cache/Makefile b/src/backend/utils/cache/Makefile index b13ecc38dd..6a1156aca5 100644 --- a/src/backend/utils/cache/Makefile +++ b/src/backend/utils/cache/Makefile @@ -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 diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 0faa097f34..3864a2fa52 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -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 ---------- */ /* diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c new file mode 100644 index 0000000000..3ae5a1d17b --- /dev/null +++ b/src/backend/utils/cache/typcache.c @@ -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; +} diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index 255ec71ff1..983296086b 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -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), diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 48723471a2..9d4f80b727 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.206 2003/08/04 02:40:10 momjian Exp $ + * $Id: catversion.h,v 1.207 2003/08/17 19:58:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200307301 +#define CATALOG_VERSION_NO 200308171 #endif diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index fcf162eb98..bff4b21700 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -16,7 +16,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_amop.h,v 1.54 2003/08/04 02:40:10 momjian Exp $ + * $Id: pg_amop.h,v 1.55 2003/08/17 19:58:06 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -418,6 +418,36 @@ DATA(insert ( 2098 3 f 2334 )); DATA(insert ( 2098 4 f 2335 )); DATA(insert ( 2098 5 f 2336 )); +/* + * btree money_ops + */ + +DATA(insert ( 2099 1 f 902 )); +DATA(insert ( 2099 2 f 904 )); +DATA(insert ( 2099 3 f 900 )); +DATA(insert ( 2099 4 f 905 )); +DATA(insert ( 2099 5 f 903 )); + +/* + * btree reltime_ops + */ + +DATA(insert ( 2233 1 f 568 )); +DATA(insert ( 2233 2 f 570 )); +DATA(insert ( 2233 3 f 566 )); +DATA(insert ( 2233 4 f 569 )); +DATA(insert ( 2233 5 f 571 )); + +/* + * btree tinterval_ops + */ + +DATA(insert ( 2234 1 f 813 )); +DATA(insert ( 2234 2 f 815 )); +DATA(insert ( 2234 3 f 811 )); +DATA(insert ( 2234 4 f 814 )); +DATA(insert ( 2234 5 f 816 )); + /* * btree array_ops */ @@ -496,5 +526,7 @@ DATA(insert ( 2230 1 f 2316 )); DATA(insert ( 2231 1 f 2328 )); /* name_pattern_ops */ DATA(insert ( 2232 1 f 2334 )); +/* aclitem_ops */ +DATA(insert ( 2235 1 f 974 )); #endif /* PG_AMOP_H */ diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index 24c1fa416f..a2607db5eb 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -14,7 +14,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_amproc.h,v 1.43 2003/08/04 02:40:11 momjian Exp $ + * $Id: pg_amproc.h,v 1.44 2003/08/17 19:58:06 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -110,6 +110,9 @@ DATA(insert ( 2095 1 2166 )); DATA(insert ( 2096 1 2166 )); DATA(insert ( 2097 1 2180 )); DATA(insert ( 2098 1 2187 )); +DATA(insert ( 2099 1 377 )); +DATA(insert ( 2233 1 380 )); +DATA(insert ( 2234 1 381 )); /* hash */ @@ -145,5 +148,6 @@ DATA(insert ( 2229 1 456 )); DATA(insert ( 2230 1 456 )); DATA(insert ( 2231 1 456 )); DATA(insert ( 2232 1 455 )); +DATA(insert ( 2235 1 329 )); #endif /* PG_AMPROC_H */ diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index bbd69dca5b..af277ad687 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -26,7 +26,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_opclass.h,v 1.56 2003/08/04 02:40:12 momjian Exp $ + * $Id: pg_opclass.h,v 1.57 2003/08/17 19:58:06 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -157,6 +157,7 @@ DATA(insert OID = 2097 ( 403 bpchar_pattern_ops PGNSP PGUID 1042 f 0 )); #define BPCHAR_PATTERN_BTREE_OPS_OID 2097 DATA(insert OID = 2098 ( 403 name_pattern_ops PGNSP PGUID 19 f 0 )); #define NAME_PATTERN_BTREE_OPS_OID 2098 +DATA(insert OID = 2099 ( 403 money_ops PGNSP PGUID 790 t 0 )); DATA(insert OID = 2222 ( 405 bool_ops PGNSP PGUID 16 t 0 )); DATA(insert OID = 2223 ( 405 bytea_ops PGNSP PGUID 17 t 0 )); DATA(insert OID = 2224 ( 405 int2vector_ops PGNSP PGUID 22 t 0 )); @@ -168,5 +169,8 @@ DATA(insert OID = 2229 ( 405 text_pattern_ops PGNSP PGUID 25 f 0 )); DATA(insert OID = 2230 ( 405 varchar_pattern_ops PGNSP PGUID 1043 f 0 )); DATA(insert OID = 2231 ( 405 bpchar_pattern_ops PGNSP PGUID 1042 f 0 )); DATA(insert OID = 2232 ( 405 name_pattern_ops PGNSP PGUID 19 f 0 )); +DATA(insert OID = 2233 ( 403 reltime_ops PGNSP PGUID 703 t 0 )); +DATA(insert OID = 2234 ( 403 tinterval_ops PGNSP PGUID 704 t 0 )); +DATA(insert OID = 2235 ( 405 aclitem_ops PGNSP PGUID 1033 t 0 )); #endif /* PG_OPCLASS_H */ diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index 176cd1eaaf..991e9429d3 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_operator.h,v 1.120 2003/08/04 02:40:12 momjian Exp $ + * $Id: pg_operator.h,v 1.121 2003/08/17 19:58:06 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -424,7 +424,7 @@ DATA(insert OID = 965 ( "^" PGNSP PGUID b f 701 701 701 0 0 0 0 0 0 dpow - DATA(insert OID = 966 ( "+" PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - )); DATA(insert OID = 967 ( "-" PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - )); DATA(insert OID = 968 ( "~" PGNSP PGUID b f 1034 1033 16 0 0 0 0 0 0 aclcontains - - )); -DATA(insert OID = 974 ( "=" PGNSP PGUID b f 1033 1033 16 974 0 0 0 0 0 aclitemeq eqsel eqjoinsel )); +DATA(insert OID = 974 ( "=" PGNSP PGUID b t 1033 1033 16 974 0 0 0 0 0 aclitemeq eqsel eqjoinsel )); /* additional geometric operators - thomas 1997-07-09 */ DATA(insert OID = 969 ( "@@" PGNSP PGUID l f 0 601 600 0 0 0 0 0 0 lseg_center - - )); @@ -448,6 +448,7 @@ DATA(insert OID = 1071 ( "<>" PGNSP PGUID b f 2277 2277 16 1071 1070 0 0 0 DATA(insert OID = 1072 ( "<" PGNSP PGUID b f 2277 2277 16 1073 1075 0 0 0 0 array_lt scalarltsel scalarltjoinsel )); #define ARRAY_LT_OP 1072 DATA(insert OID = 1073 ( ">" PGNSP PGUID b f 2277 2277 16 1072 1074 0 0 0 0 array_gt scalargtsel scalargtjoinsel )); +#define ARRAY_GT_OP 1073 DATA(insert OID = 1074 ( "<=" PGNSP PGUID b f 2277 2277 16 1075 1073 0 0 0 0 array_le scalarltsel scalarltjoinsel )); DATA(insert OID = 1075 ( ">=" PGNSP PGUID b f 2277 2277 16 1074 1072 0 0 0 0 array_ge scalargtsel scalargtjoinsel )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 1fe3812d70..59b957ddd0 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.312 2003/08/04 02:40:12 momjian Exp $ + * $Id: pg_proc.h,v 1.313 2003/08/17 19:58:06 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -758,6 +758,12 @@ DATA(insert OID = 359 ( btnamecmp PGNSP PGUID 12 f f t f i 2 23 "19 19" btn DESCR("btree less-equal-greater"); DATA(insert OID = 360 ( bttextcmp PGNSP PGUID 12 f f t f i 2 23 "25 25" bttextcmp - _null_ )); DESCR("btree less-equal-greater"); +DATA(insert OID = 377 ( cash_cmp PGNSP PGUID 12 f f t f i 2 23 "790 790" cash_cmp - _null_ )); +DESCR("btree less-equal-greater"); +DATA(insert OID = 380 ( btreltimecmp PGNSP PGUID 12 f f t f i 2 23 "703 703" btreltimecmp - _null_ )); +DESCR("btree less-equal-greater"); +DATA(insert OID = 381 ( bttintervalcmp PGNSP PGUID 12 f f t f i 2 23 "704 704" bttintervalcmp - _null_ )); +DESCR("btree less-equal-greater"); DATA(insert OID = 382 ( btarraycmp PGNSP PGUID 12 f f t f i 2 23 "2277 2277" btarraycmp - _null_ )); DESCR("btree less-equal-greater"); @@ -844,6 +850,8 @@ DATA(insert OID = 456 ( hashvarlena PGNSP PGUID 12 f f t f i 1 23 "2281" has DESCR("hash any varlena type"); DATA(insert OID = 457 ( hashoidvector PGNSP PGUID 12 f f t f i 1 23 "30" hashoidvector - _null_ )); DESCR("hash"); +DATA(insert OID = 329 ( hash_aclitem PGNSP PGUID 12 f f t f i 1 23 "1033" hash_aclitem - _null_ )); +DESCR("hash"); DATA(insert OID = 398 ( hashint2vector PGNSP PGUID 12 f f t f i 1 23 "22" hashint2vector - _null_ )); DESCR("hash"); DATA(insert OID = 399 ( hashmacaddr PGNSP PGUID 12 f f t f i 1 23 "829" hashmacaddr - _null_ )); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 9c80a9ebc7..6e3254dc67 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: nodes.h,v 1.146 2003/08/04 02:40:13 momjian Exp $ + * $Id: nodes.h,v 1.147 2003/08/17 19:58:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -265,7 +265,7 @@ typedef enum NodeTag T_ExprFieldSelect, T_ResTarget, T_TypeCast, - T_SortGroupBy, + T_SortBy, T_RangeSubselect, T_RangeFunction, T_TypeName, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 497ba3b624..cbaaee49cf 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.246 2003/08/08 21:42:48 momjian Exp $ + * $Id: parsenodes.h,v 1.247 2003/08/17 19:58:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -279,14 +279,19 @@ typedef struct ResTarget } ResTarget; /* - * SortGroupBy - for ORDER BY clause + * SortBy - for ORDER BY clause */ -typedef struct SortGroupBy +#define SORTBY_ASC 1 +#define SORTBY_DESC 2 +#define SORTBY_USING 3 + +typedef struct SortBy { NodeTag type; - List *useOp; /* operator to use */ - Node *node; /* Expression */ -} SortGroupBy; + int sortby_kind; /* see codes above */ + List *useOp; /* name of op to use, if SORTBY_USING */ + Node *node; /* expression to sort on */ +} SortBy; /* * RangeSubselect - subquery appearing in a FROM clause @@ -614,7 +619,7 @@ typedef struct SelectStmt * These fields are used in both "leaf" SelectStmts and upper-level * SelectStmts. */ - List *sortClause; /* sort clause (a list of SortGroupBy's) */ + List *sortClause; /* sort clause (a list of SortBy's) */ Node *limitOffset; /* # of result tuples to skip */ Node *limitCount; /* # of result tuples to return */ List *forUpdate; /* FOR UPDATE clause */ diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h index 670b72cfba..8439c19804 100644 --- a/src/include/parser/parse_clause.h +++ b/src/include/parser/parse_clause.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_clause.h,v 1.36 2003/08/07 19:20:23 tgl Exp $ + * $Id: parse_clause.h,v 1.37 2003/08/17 19:58:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,7 +37,8 @@ extern List *addAllTargetsToSortList(ParseState *pstate, bool resolveUnknown); extern List *addTargetToSortList(ParseState *pstate, TargetEntry *tle, List *sortlist, List *targetlist, - List *opname, bool resolveUnknown); + int sortby_kind, List *sortby_opname, + bool resolveUnknown); extern Index assignSortGroupRef(TargetEntry *tle, List *tlist); extern bool targetIsInSortList(TargetEntry *tle, List *sortList); diff --git a/src/include/parser/parse_oper.h b/src/include/parser/parse_oper.h index 1167997706..130f125eff 100644 --- a/src/include/parser/parse_oper.h +++ b/src/include/parser/parse_oper.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_oper.h,v 1.32 2003/08/04 02:40:14 momjian Exp $ + * $Id: parse_oper.h,v 1.33 2003/08/17 19:58:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,15 +38,16 @@ extern Operator compatible_oper(List *op, Oid arg1, Oid arg2, bool noError); /* currently no need for compatible_left_oper/compatible_right_oper */ -/* Routines for identifying "=" and "<" operators for a type */ +/* Routines for identifying "=", "<", ">" operators for a type */ extern Operator equality_oper(Oid argtype, bool noError); extern Operator ordering_oper(Oid argtype, bool noError); +extern Operator reverse_ordering_oper(Oid argtype, bool noError); /* Convenience routines for common calls on the above */ extern Oid compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError); extern Oid equality_oper_funcid(Oid argtype); extern Oid ordering_oper_opid(Oid argtype); -extern Oid ordering_oper_funcid(Oid argtype); +extern Oid reverse_ordering_oper_opid(Oid argtype); /* Extract operator OID or underlying-function OID from an Operator tuple */ extern Oid oprid(Operator op); diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 59b5d106fa..29a807744c 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: acl.h,v 1.61 2003/08/08 21:42:55 momjian Exp $ + * $Id: acl.h,v 1.62 2003/08/17 19:58:06 tgl Exp $ * * NOTES * For backward-compatibility purposes we have to allow there @@ -209,6 +209,7 @@ extern Datum aclremove(PG_FUNCTION_ARGS); extern Datum aclcontains(PG_FUNCTION_ARGS); extern Datum makeaclitem(PG_FUNCTION_ARGS); extern Datum aclitem_eq(PG_FUNCTION_ARGS); +extern Datum hash_aclitem(PG_FUNCTION_ARGS); /* * prototypes for functions in aclchk.c diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index a84f851225..71615b6610 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.227 2003/08/08 21:42:55 momjian Exp $ + * $Id: builtins.h,v 1.228 2003/08/17 19:58:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -222,6 +222,8 @@ extern Datum btfloat8cmp(PG_FUNCTION_ARGS); extern Datum btoidcmp(PG_FUNCTION_ARGS); extern Datum btoidvectorcmp(PG_FUNCTION_ARGS); extern Datum btabstimecmp(PG_FUNCTION_ARGS); +extern Datum btreltimecmp(PG_FUNCTION_ARGS); +extern Datum bttintervalcmp(PG_FUNCTION_ARGS); extern Datum btcharcmp(PG_FUNCTION_ARGS); extern Datum btnamecmp(PG_FUNCTION_ARGS); extern Datum bttextcmp(PG_FUNCTION_ARGS); diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h index 305304c20d..b78da25edd 100644 --- a/src/include/utils/cash.h +++ b/src/include/utils/cash.h @@ -23,6 +23,7 @@ extern Datum cash_lt(PG_FUNCTION_ARGS); extern Datum cash_le(PG_FUNCTION_ARGS); extern Datum cash_gt(PG_FUNCTION_ARGS); extern Datum cash_ge(PG_FUNCTION_ARGS); +extern Datum cash_cmp(PG_FUNCTION_ARGS); extern Datum cash_pl(PG_FUNCTION_ARGS); extern Datum cash_mi(PG_FUNCTION_ARGS); diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 4c9c073ade..927190e35d 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: lsyscache.h,v 1.80 2003/08/11 23:04:50 tgl Exp $ + * $Id: lsyscache.h,v 1.81 2003/08/17 19:58:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,7 @@ extern bool op_in_opclass(Oid opno, Oid opclass); extern bool op_requires_recheck(Oid opno, Oid opclass); extern Oid get_opclass_member(Oid opclass, int16 strategy); extern Oid get_op_hash_function(Oid opno); +extern Oid get_opclass_proc(Oid opclass, int16 procnum); extern char *get_attname(Oid relid, AttrNumber attnum); extern char *get_relid_attribute_name(Oid relid, AttrNumber attnum); extern AttrNumber get_attnum(Oid relid, const char *attname); diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h new file mode 100644 index 0000000000..ae080d3fee --- /dev/null +++ b/src/include/utils/typcache.h @@ -0,0 +1,66 @@ +/*------------------------------------------------------------------------- + * + * typcache.h + * Type cache definitions. + * + * 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. + * + * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $Id: typcache.h,v 1.1 2003/08/17 19:58:06 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef TYPCACHE_H +#define TYPCACHE_H + +#include "fmgr.h" + + +typedef struct TypeCacheEntry +{ + /* typeId is the hash lookup key and MUST BE FIRST */ + Oid type_id; /* OID of the data type */ + + /* some subsidiary information copied from the pg_type row */ + int16 typlen; + bool typbyval; + char typalign; + + /* + * Information obtained from opclass entries + * + * These will be InvalidOid if no match could be found, or if the + * information hasn't yet been requested. + */ + Oid btree_opc; /* OID of the default btree opclass */ + Oid hash_opc; /* OID of the default hash opclass */ + Oid eq_opr; /* OID of the equality operator */ + Oid lt_opr; /* OID of the less-than operator */ + Oid gt_opr; /* OID of the greater-than operator */ + Oid cmp_proc; /* OID of the btree comparison function */ + + /* + * Pre-set-up fmgr call info for the equality operator and the btree + * comparison function. These are kept in the type cache to avoid + * problems with memory leaks in repeated calls to array_eq and array_cmp. + * There is not currently a need to maintain call info for the lt_opr + * or gt_opr. + */ + FmgrInfo eq_opr_finfo; + FmgrInfo cmp_proc_finfo; +} TypeCacheEntry; + +/* Bit flags to indicate which fields a given caller needs to have set */ +#define TYPECACHE_EQ_OPR 0x0001 +#define TYPECACHE_LT_OPR 0x0002 +#define TYPECACHE_GT_OPR 0x0004 +#define TYPECACHE_CMP_PROC 0x0008 +#define TYPECACHE_EQ_OPR_FINFO 0x0010 +#define TYPECACHE_CMP_PROC_FINFO 0x0020 + +extern TypeCacheEntry *lookup_type_cache(Oid type_id, int flags); + +#endif /* TYPCACHE_H */ diff --git a/src/test/regress/expected/circle.out b/src/test/regress/expected/circle.out index 37a3591a64..d8713dc53d 100644 --- a/src/test/regress/expected/circle.out +++ b/src/test/regress/expected/circle.out @@ -81,7 +81,7 @@ SELECT '' AS four, f1 FROM CIRCLE_TBL WHERE diameter(f1) >= 10; SELECT '' as five, c1.f1 AS one, c2.f1 AS two, (c1.f1 <-> c2.f1) AS distance FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE (c1.f1 < c2.f1) AND ((c1.f1 <-> c2.f1) > 0) - ORDER BY distance, one, two; + ORDER BY distance, one USING < , two USING < ; five | one | two | distance ------+----------------+----------------+------------------ | <(100,200),10> | <(100,1),115> | 74 diff --git a/src/test/regress/expected/geometry.out b/src/test/regress/expected/geometry.out index 69118725f4..0889ea25e6 100644 --- a/src/test/regress/expected/geometry.out +++ b/src/test/regress/expected/geometry.out @@ -504,7 +504,7 @@ SELECT '' AS two, circle(f1) SELECT '' AS twentyfour, c1.f1 AS circle, p1.f1 AS point, (p1.f1 <-> c1.f1) AS distance FROM CIRCLE_TBL c1, POINT_TBL p1 WHERE (p1.f1 <-> c1.f1) > 0 - ORDER BY distance, circle, point using <<; + ORDER BY distance, circle using <, point using <<; twentyfour | circle | point | distance ------------+----------------+------------+--------------- | <(1,2),3> | (-3,4) | 1.472135955 diff --git a/src/test/regress/expected/geometry_1.out b/src/test/regress/expected/geometry_1.out index 76b621fede..5c205d8bcc 100644 --- a/src/test/regress/expected/geometry_1.out +++ b/src/test/regress/expected/geometry_1.out @@ -504,7 +504,7 @@ SELECT '' AS two, circle(f1) SELECT '' AS twentyfour, c1.f1 AS circle, p1.f1 AS point, (p1.f1 <-> c1.f1) AS distance FROM CIRCLE_TBL c1, POINT_TBL p1 WHERE (p1.f1 <-> c1.f1) > 0 - ORDER BY distance, circle, point using <<; + ORDER BY distance, circle using <, point using <<; twentyfour | circle | point | distance ------------+----------------+------------+--------------- | <(1,2),3> | (-3,4) | 1.472135955 diff --git a/src/test/regress/sql/circle.sql b/src/test/regress/sql/circle.sql index 9b384554d7..fe229b3b2c 100644 --- a/src/test/regress/sql/circle.sql +++ b/src/test/regress/sql/circle.sql @@ -42,5 +42,4 @@ SELECT '' AS four, f1 FROM CIRCLE_TBL WHERE diameter(f1) >= 10; SELECT '' as five, c1.f1 AS one, c2.f1 AS two, (c1.f1 <-> c2.f1) AS distance FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE (c1.f1 < c2.f1) AND ((c1.f1 <-> c2.f1) > 0) - ORDER BY distance, one, two; - + ORDER BY distance, one USING < , two USING < ; diff --git a/src/test/regress/sql/geometry.sql b/src/test/regress/sql/geometry.sql index fab791df0d..4abb679148 100644 --- a/src/test/regress/sql/geometry.sql +++ b/src/test/regress/sql/geometry.sql @@ -152,5 +152,4 @@ SELECT '' AS two, circle(f1) SELECT '' AS twentyfour, c1.f1 AS circle, p1.f1 AS point, (p1.f1 <-> c1.f1) AS distance FROM CIRCLE_TBL c1, POINT_TBL p1 WHERE (p1.f1 <-> c1.f1) > 0 - ORDER BY distance, circle, point using <<; - + ORDER BY distance, circle using <, point using <<; -- 2.40.0