Re: Rethinking representation of sort/hash semantics in queries and plans
От | Tom Lane |
---|---|
Тема | Re: Rethinking representation of sort/hash semantics in queries and plans |
Дата | |
Msg-id | 28019.1290991343@sss.pgh.pa.us обсуждение исходный текст |
Ответ на | Re: Rethinking representation of sort/hash semantics in queries and plans (Tom Lane <tgl@sss.pgh.pa.us>) |
Список | pgsql-hackers |
I wrote: > Now, this loss of flexibility doesn't particularly bother me, because > I know of no existing or contemplated btree-substitute access methods. > If one did appear on the horizon, there are a couple of ways we could > fix the problem, the cleanest being to let a non-btree opfamily declare > that it sorts the same as btree opfamily so-and-so. Or we could fix > it locally in plancat.c by performing the lookup-the-operators-and- > then-the-btree-opfamilies dance on the fly when setting up IndexOptInfo > for a non-btree amcanorder index. But I'm disinclined to write such > code when there's no way to test it and no foreseeable possibility > that it'll ever be used. Maybe we should just make plancat.c throw > a not-implemented error if amcanorder is true but it's not btree. On further reflection the last seems like clearly the thing to do. > PS: the attached patch doesn't yet include removal of relcache > rd_operator arrays, since that would just make the patch bigger > without exposing any new issues. And here's the complete patch. regards, tom lane diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index d8fc12068fb6113c0baf8aa4e5941e0150ce3521..f73e0e6dc6007ce768828480f74621752aaf57d2 100644 *** a/src/backend/optimizer/path/indxpath.c --- b/src/backend/optimizer/path/indxpath.c *************** find_usable_indexes(PlannerInfo *root, R *** 380,386 **** * how many of them are actually useful for this query. This is not * relevant unless we are at top level. */ ! index_is_ordered = OidIsValid(index->fwdsortop[0]); if (index_is_ordered && possibly_useful_pathkeys && istoplevel && outer_rel == NULL) { --- 380,386 ---- * how many of them are actually useful for this query. This is not * relevant unless we are at top level. */ ! index_is_ordered = (index->sortopfamily != NULL); if (index_is_ordered && possibly_useful_pathkeys && istoplevel && outer_rel == NULL) { diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 8af0c6dc482372244b15a8a5f5a4a562e34ac91b..6325655eed84759168a51aed3f712582ec9c1f00 100644 *** a/src/backend/optimizer/path/pathkeys.c --- b/src/backend/optimizer/path/pathkeys.c *************** static PathKey *make_canonical_pathkey(P *** 36,47 **** EquivalenceClass *eclass, Oid opfamily, int strategy, bool nulls_first); static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys); - static PathKey *make_pathkey_from_sortinfo(PlannerInfo *root, - Expr *expr, Oid ordering_op, - bool nulls_first, - Index sortref, - bool create_it, - bool canonicalize); static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno); static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey); --- 36,41 ---- *************** canonicalize_pathkeys(PlannerInfo *root, *** 224,232 **** /* * make_pathkey_from_sortinfo ! * Given an expression, a sortop, and a nulls-first flag, create ! * a PathKey. If canonicalize = true, the result is a "canonical" ! * PathKey, otherwise not. (But note it might be redundant anyway.) * * If the PathKey is being generated from a SortGroupClause, sortref should be * the SortGroupClause's SortGroupRef; otherwise zero. --- 218,226 ---- /* * make_pathkey_from_sortinfo ! * Given an expression and sort-order information, create a PathKey. ! * If canonicalize = true, the result is a "canonical" PathKey, ! * otherwise not. (But note it might be redundant anyway.) * * If the PathKey is being generated from a SortGroupClause, sortref should be * the SortGroupClause's SortGroupRef; otherwise zero. *************** canonicalize_pathkeys(PlannerInfo *root, *** 240,285 **** */ static PathKey * make_pathkey_from_sortinfo(PlannerInfo *root, ! Expr *expr, Oid ordering_op, bool nulls_first, Index sortref, bool create_it, bool canonicalize) { - Oid opfamily, - opcintype; int16 strategy; Oid equality_op; List *opfamilies; EquivalenceClass *eclass; /* ! * An ordering operator fully determines the behavior of its opfamily, so ! * could only meaningfully appear in one family --- or perhaps two if one ! * builds a reverse-sort opfamily, but there's not much point in that ! * anymore. But EquivalenceClasses need to contain opfamily lists based ! * on the family membership of equality operators, which could easily be ! * bigger. So, look up the equality operator that goes with the ordering ! * operator (this should be unique) and get its membership. */ - - /* Find the operator in pg_amop --- failure shouldn't happen */ - if (!get_ordering_op_properties(ordering_op, - &opfamily, &opcintype, &strategy)) - elog(ERROR, "operator %u is not a valid ordering operator", - ordering_op); - /* Get matching equality operator */ equality_op = get_opfamily_member(opfamily, opcintype, opcintype, BTEqualStrategyNumber); if (!OidIsValid(equality_op)) /* shouldn't happen */ ! elog(ERROR, "could not find equality operator for ordering operator %u", ! ordering_op); opfamilies = get_mergejoin_opfamilies(equality_op); if (!opfamilies) /* certainly should find some */ ! elog(ERROR, "could not find opfamilies for ordering operator %u", ! ordering_op); /* * When dealing with binary-compatible opclasses, we have to ensure that --- 234,272 ---- */ static PathKey * make_pathkey_from_sortinfo(PlannerInfo *root, ! Expr *expr, ! Oid opfamily, ! Oid opcintype, ! bool reverse_sort, bool nulls_first, Index sortref, bool create_it, bool canonicalize) { int16 strategy; Oid equality_op; List *opfamilies; EquivalenceClass *eclass; + strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber; + /* ! * EquivalenceClasses need to contain opfamily lists based on the family ! * membership of mergejoinable equality operators, which could belong to ! * more than one opfamily. So we have to look up the opfamily's equality ! * operator and get its membership. */ equality_op = get_opfamily_member(opfamily, opcintype, opcintype, BTEqualStrategyNumber); if (!OidIsValid(equality_op)) /* shouldn't happen */ ! elog(ERROR, "could not find equality operator for opfamily %u", ! opfamily); opfamilies = get_mergejoin_opfamilies(equality_op); if (!opfamilies) /* certainly should find some */ ! elog(ERROR, "could not find opfamilies for equality operator %u", ! equality_op); /* * When dealing with binary-compatible opclasses, we have to ensure that *************** make_pathkey_from_sortinfo(PlannerInfo * *** 322,327 **** --- 309,350 ---- return makePathKey(eclass, opfamily, strategy, nulls_first); } + /* + * make_pathkey_from_sortop + * Like make_pathkey_from_sortinfo, but work from a sort operator. + * + * This should eventually go away, but we need to restructure SortGroupClause + * first. + */ + static PathKey * + make_pathkey_from_sortop(PlannerInfo *root, + Expr *expr, + Oid ordering_op, + bool nulls_first, + Index sortref, + bool create_it, + bool canonicalize) + { + Oid opfamily, + opcintype; + int16 strategy; + + /* Find the operator in pg_amop --- failure shouldn't happen */ + if (!get_ordering_op_properties(ordering_op, + &opfamily, &opcintype, &strategy)) + elog(ERROR, "operator %u is not a valid ordering operator", + ordering_op); + return make_pathkey_from_sortinfo(root, + expr, + opfamily, + opcintype, + (strategy == BTGreaterStrategyNumber), + nulls_first, + sortref, + create_it, + canonicalize); + } + /**************************************************************************** * PATHKEY COMPARISONS *************** get_cheapest_fractional_path_for_pathkey *** 479,489 **** * build_index_pathkeys * Build a pathkeys list that describes the ordering induced by an index * scan using the given index. (Note that an unordered index doesn't ! * induce any ordering; such an index will have no sortop OIDS in ! * its sortops arrays, and we will return NIL.) * ! * If 'scandir' is BackwardScanDirection, attempt to build pathkeys ! * representing a backwards scan of the index. Return NIL if can't do it. * * The result is canonical, meaning that redundant pathkeys are removed; * it may therefore have fewer entries than there are index columns. --- 502,511 ---- * build_index_pathkeys * Build a pathkeys list that describes the ordering induced by an index * scan using the given index. (Note that an unordered index doesn't ! * induce any ordering, so we return NIL.) * ! * If 'scandir' is BackwardScanDirection, build pathkeys representing a ! * backwards scan of the index. * * The result is canonical, meaning that redundant pathkeys are removed; * it may therefore have fewer entries than there are index columns. *************** build_index_pathkeys(PlannerInfo *root, *** 500,511 **** ScanDirection scandir) { List *retval = NIL; ! ListCell *indexprs_item = list_head(index->indexprs); int i; for (i = 0; i < index->ncolumns; i++) { ! Oid sortop; bool nulls_first; int ikey; Expr *indexkey; --- 522,537 ---- ScanDirection scandir) { List *retval = NIL; ! ListCell *indexprs_item; int i; + if (index->sortopfamily == NULL) + return NIL; /* non-orderable index */ + + indexprs_item = list_head(index->indexprs); for (i = 0; i < index->ncolumns; i++) { ! bool reverse_sort; bool nulls_first; int ikey; Expr *indexkey; *************** build_index_pathkeys(PlannerInfo *root, *** 513,530 **** if (ScanDirectionIsBackward(scandir)) { ! sortop = index->revsortop[i]; nulls_first = !index->nulls_first[i]; } else { ! sortop = index->fwdsortop[i]; nulls_first = index->nulls_first[i]; } - if (!OidIsValid(sortop)) - break; /* no more orderable columns */ - ikey = index->indexkeys[i]; if (ikey != 0) { --- 539,553 ---- if (ScanDirectionIsBackward(scandir)) { ! reverse_sort = !index->reverse_sort[i]; nulls_first = !index->nulls_first[i]; } else { ! reverse_sort = index->reverse_sort[i]; nulls_first = index->nulls_first[i]; } ikey = index->indexkeys[i]; if (ikey != 0) { *************** build_index_pathkeys(PlannerInfo *root, *** 543,549 **** /* OK, try to make a canonical pathkey for this sort key */ cpathkey = make_pathkey_from_sortinfo(root, indexkey, ! sortop, nulls_first, 0, false, --- 566,574 ---- /* OK, try to make a canonical pathkey for this sort key */ cpathkey = make_pathkey_from_sortinfo(root, indexkey, ! index->sortopfamily[i], ! index->opcintype[i], ! reverse_sort, nulls_first, 0, false, *************** make_pathkeys_for_sortclauses(PlannerInf *** 892,904 **** sortkey = (Expr *) get_sortgroupclause_expr(sortcl, tlist); Assert(OidIsValid(sortcl->sortop)); ! pathkey = make_pathkey_from_sortinfo(root, ! sortkey, ! sortcl->sortop, ! sortcl->nulls_first, ! sortcl->tleSortGroupRef, ! true, ! canonicalize); /* Canonical form eliminates redundant ordering keys */ if (canonicalize) --- 917,929 ---- sortkey = (Expr *) get_sortgroupclause_expr(sortcl, tlist); Assert(OidIsValid(sortcl->sortop)); ! pathkey = make_pathkey_from_sortop(root, ! sortkey, ! sortcl->sortop, ! sortcl->nulls_first, ! sortcl->tleSortGroupRef, ! true, ! canonicalize); /* Canonical form eliminates redundant ordering keys */ if (canonicalize) *************** make_pathkeys_for_aggregate(PlannerInfo *** 935,947 **** * We arbitrarily set nulls_first to false. Actually, a MIN/MAX agg can * use either nulls ordering option, but that is dealt with elsewhere. */ ! pathkey = make_pathkey_from_sortinfo(root, ! aggtarget, ! aggsortop, ! false, /* nulls_first */ ! 0, ! true, ! false); return list_make1(pathkey); } --- 960,972 ---- * We arbitrarily set nulls_first to false. Actually, a MIN/MAX agg can * use either nulls ordering option, but that is dealt with elsewhere. */ ! pathkey = make_pathkey_from_sortop(root, ! aggtarget, ! aggsortop, ! false, /* nulls_first */ ! 0, ! true, ! false); return list_make1(pathkey); } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 908b4f7205d86f0888007d3a05e65b13132520b9..cd26e906187167728042945f9e8ba29046500e2e 100644 *** a/src/backend/optimizer/util/plancat.c --- b/src/backend/optimizer/util/plancat.c *************** get_relation_info(PlannerInfo *root, Oid *** 189,207 **** RelationGetForm(indexRelation)->reltablespace; info->rel = rel; info->ncolumns = ncolumns = index->indnatts; - - /* - * Allocate per-column info arrays. To save a few palloc cycles - * we allocate all the Oid-type arrays in one request. We must - * pre-zero the sortop and nulls_first arrays in case the index is - * unordered. - */ info->indexkeys = (int *) palloc(sizeof(int) * ncolumns); ! info->opfamily = (Oid *) palloc0(sizeof(Oid) * (4 * ncolumns)); ! info->opcintype = info->opfamily + ncolumns; ! info->fwdsortop = info->opcintype + ncolumns; ! info->revsortop = info->fwdsortop + ncolumns; ! info->nulls_first = (bool *) palloc0(sizeof(bool) * ncolumns); for (i = 0; i < ncolumns; i++) { --- 189,197 ---- RelationGetForm(indexRelation)->reltablespace; info->rel = rel; info->ncolumns = ncolumns = index->indnatts; info->indexkeys = (int *) palloc(sizeof(int) * ncolumns); ! info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns); ! info->opcintype = (Oid *) palloc(sizeof(Oid) * ncolumns); for (i = 0; i < ncolumns; i++) { *************** get_relation_info(PlannerInfo *root, Oid *** 219,267 **** info->amhasgetbitmap = OidIsValid(indexRelation->rd_am->amgetbitmap); /* ! * Fetch the ordering operators associated with the index, if any. ! * We expect that all ordering-capable indexes use btree's ! * strategy numbers for the ordering operators. */ ! if (indexRelation->rd_am->amcanorder) { ! int nstrat = indexRelation->rd_am->amstrategies; for (i = 0; i < ncolumns; i++) { int16 opt = indexRelation->rd_indoption[i]; - int fwdstrat; - int revstrat; ! if (opt & INDOPTION_DESC) ! { ! fwdstrat = BTGreaterStrategyNumber; ! revstrat = BTLessStrategyNumber; ! } ! else ! { ! fwdstrat = BTLessStrategyNumber; ! revstrat = BTGreaterStrategyNumber; ! } ! ! /* ! * Index AM must have a fixed set of strategies for it to ! * make sense to specify amcanorder, so we need not allow ! * the case amstrategies == 0. ! */ ! if (fwdstrat > 0) ! { ! Assert(fwdstrat <= nstrat); ! info->fwdsortop[i] = indexRelation->rd_operator[i * nstrat + fwdstrat - 1]; ! } ! if (revstrat > 0) ! { ! Assert(revstrat <= nstrat); ! info->revsortop[i] = indexRelation->rd_operator[i * nstrat + revstrat - 1]; ! } info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0; } } /* * Fetch the index expressions and predicate, if any. We must --- 209,250 ---- info->amhasgetbitmap = OidIsValid(indexRelation->rd_am->amgetbitmap); /* ! * Fetch the ordering information for the index, if any. ! * ! * If it's a btree index, we can use its opfamily OIDs directly ! * as the sort ordering opfamily OIDs. */ ! if (info->relam == BTREE_AM_OID) { ! info->sortopfamily = info->opfamily; ! info->reverse_sort = (bool *) palloc(sizeof(bool) * ncolumns); ! info->nulls_first = (bool *) palloc(sizeof(bool) * ncolumns); for (i = 0; i < ncolumns; i++) { int16 opt = indexRelation->rd_indoption[i]; ! info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0; info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0; } } + else if (indexRelation->rd_am->amcanorder) + { + /* + * To support other orderable index types, we'd need a way to + * map from their opfamilies to btree opfamilies. This could + * reasonably be done as an additional property in + * pg_opfamily, but for the foreseeable future there's no need + * to bother implementing that. + */ + elog(ERROR, "only btree is currently allowed to set amcanorder"); + } + else + { + info->sortopfamily = NULL; + info->reverse_sort = NULL; + info->nulls_first = NULL; + } /* * Fetch the index expressions and predicate, if any. We must diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index c7442218a84a12972658ef320c20c7aa783b80a7..95397aa7cee3132db72138443bd7181ff5c32889 100644 *** a/src/backend/utils/adt/selfuncs.c --- b/src/backend/utils/adt/selfuncs.c *************** get_actual_variable_range(PlannerInfo *r *** 4567,4580 **** * The first index column must match the desired variable and sort * operator --- but we can use a descending-order index. */ - if (sortop == index->fwdsortop[0]) - indexscandir = ForwardScanDirection; - else if (sortop == index->revsortop[0]) - indexscandir = BackwardScanDirection; - else - continue; if (!match_index_to_operand(vardata->var, 0, index)) continue; /* * Found a suitable index to extract data from. We'll need an EState --- 4567,4592 ---- * The first index column must match the desired variable and sort * operator --- but we can use a descending-order index. */ if (!match_index_to_operand(vardata->var, 0, index)) continue; + switch (get_op_opfamily_strategy(sortop, index->sortopfamily[0])) + { + case BTLessStrategyNumber: + if (index->reverse_sort[0]) + indexscandir = BackwardScanDirection; + else + indexscandir = ForwardScanDirection; + break; + case BTGreaterStrategyNumber: + if (index->reverse_sort[0]) + indexscandir = ForwardScanDirection; + else + indexscandir = BackwardScanDirection; + break; + default: + /* index doesn't match the sortop */ + continue; + } /* * Found a suitable index to extract data from. We'll need an EState *************** btcostestimate(PG_FUNCTION_ARGS) *** 6150,6161 **** if (HeapTupleIsValid(vardata.statsTuple)) { float4 *numbers; int nnumbers; ! if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0, STATISTIC_KIND_CORRELATION, ! index->fwdsortop[0], NULL, NULL, NULL, &numbers, &nnumbers)) --- 6162,6179 ---- if (HeapTupleIsValid(vardata.statsTuple)) { + Oid sortop; float4 *numbers; int nnumbers; ! sortop = get_opfamily_member(index->opfamily[0], ! index->opcintype[0], ! index->opcintype[0], ! BTLessStrategyNumber); ! if (OidIsValid(sortop) && ! get_attstatsslot(vardata.statsTuple, InvalidOid, 0, STATISTIC_KIND_CORRELATION, ! sortop, NULL, NULL, NULL, &numbers, &nnumbers)) *************** btcostestimate(PG_FUNCTION_ARGS) *** 6165,6170 **** --- 6183,6191 ---- Assert(nnumbers == 1); varCorrelation = numbers[0]; + if (index->reverse_sort[0]) + varCorrelation = -varCorrelation; + if (index->ncolumns > 1) *indexCorrelation = varCorrelation * 0.75; else *************** btcostestimate(PG_FUNCTION_ARGS) *** 6172,6196 **** free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers); } - else if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0, - STATISTIC_KIND_CORRELATION, - index->revsortop[0], - NULL, - NULL, NULL, - &numbers, &nnumbers)) - { - double varCorrelation; - - Assert(nnumbers == 1); - varCorrelation = numbers[0]; - - if (index->ncolumns > 1) - *indexCorrelation = -varCorrelation * 0.75; - else - *indexCorrelation = -varCorrelation; - - free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers); - } } ReleaseVariableStats(vardata); --- 6193,6198 ---- diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 9353a347bcb3692a4c82bc4ea3c275610e42fd82..8df12a1424322501c2a55a59723a4b21880dfb35 100644 *** a/src/backend/utils/cache/relcache.c --- b/src/backend/utils/cache/relcache.c *************** *** 39,45 **** #include "catalog/index.h" #include "catalog/indexing.h" #include "catalog/namespace.h" - #include "catalog/pg_amop.h" #include "catalog/pg_amproc.h" #include "catalog/pg_attrdef.h" #include "catalog/pg_authid.h" --- 39,44 ---- *************** *** 48,54 **** #include "catalog/pg_database.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" - #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" #include "catalog/pg_rewrite.h" #include "catalog/pg_tablespace.h" --- 47,52 ---- *************** *** 84,93 **** */ #define RELCACHE_INIT_FILENAME "pg_internal.init" ! #define RELCACHE_INIT_FILEMAGIC 0x573265 /* version ID value */ /* ! * hardcoded tuple descriptors, generated by genbki.pl */ static const FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class}; static const FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute}; --- 82,91 ---- */ #define RELCACHE_INIT_FILENAME "pg_internal.init" ! #define RELCACHE_INIT_FILEMAGIC 0x573266 /* version ID value */ /* ! * hardcoded tuple descriptors, contents generated by genbki.pl */ static const FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class}; static const FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute}; *************** do { \ *** 185,203 **** /* * Special cache for opclass-related information * ! * Note: only default operators and support procs get cached, ie, those with * lefttype = righttype = opcintype. */ typedef struct opclasscacheent { Oid opclassoid; /* lookup key: OID of opclass */ bool valid; /* set TRUE after successful fill-in */ - StrategyNumber numStrats; /* max # of strategies (from pg_am) */ StrategyNumber numSupport; /* max # of support procs (from pg_am) */ Oid opcfamily; /* OID of opclass's family */ Oid opcintype; /* OID of opclass's declared input type */ ! Oid *operatorOids; /* strategy operators' OIDs */ ! RegProcedure *supportProcs; /* support procs */ } OpClassCacheEnt; static HTAB *OpClassCache = NULL; --- 183,199 ---- /* * Special cache for opclass-related information * ! * Note: only default support procs get cached, ie, those with * lefttype = righttype = opcintype. */ typedef struct opclasscacheent { Oid opclassoid; /* lookup key: OID of opclass */ bool valid; /* set TRUE after successful fill-in */ StrategyNumber numSupport; /* max # of support procs (from pg_am) */ Oid opcfamily; /* OID of opclass's family */ Oid opcintype; /* OID of opclass's declared input type */ ! RegProcedure *supportProcs; /* OIDs of support procedures */ } OpClassCacheEnt; static HTAB *OpClassCache = NULL; *************** static void AttrDefaultFetch(Relation re *** 231,245 **** static void CheckConstraintFetch(Relation relation); static List *insert_ordered_oid(List *list, Oid datum); static void IndexSupportInitialize(oidvector *indclass, - Oid *indexOperator, RegProcedure *indexSupport, Oid *opFamily, Oid *opcInType, - StrategyNumber maxStrategyNumber, StrategyNumber maxSupportNumber, AttrNumber maxAttributeNumber); static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid, - StrategyNumber numStrats, StrategyNumber numSupport); static void RelationCacheInitFileRemoveInDir(const char *tblspcpath); static void unlink_initfile(const char *initfilename); --- 227,238 ---- *************** RelationInitIndexAccessInfo(Relation rel *** 980,986 **** MemoryContext indexcxt; MemoryContext oldcontext; int natts; - uint16 amstrategies; uint16 amsupport; /* --- 973,978 ---- *************** RelationInitIndexAccessInfo(Relation rel *** 1015,1021 **** if (natts != relation->rd_index->indnatts) elog(ERROR, "relnatts disagrees with indnatts for index %u", RelationGetRelid(relation)); - amstrategies = aform->amstrategies; amsupport = aform->amsupport; /* --- 1007,1012 ---- *************** RelationInitIndexAccessInfo(Relation rel *** 1044,1056 **** relation->rd_opcintype = (Oid *) MemoryContextAllocZero(indexcxt, natts * sizeof(Oid)); - if (amstrategies > 0) - relation->rd_operator = (Oid *) - MemoryContextAllocZero(indexcxt, - natts * amstrategies * sizeof(Oid)); - else - relation->rd_operator = NULL; - if (amsupport > 0) { int nsupport = natts * amsupport; --- 1035,1040 ---- *************** RelationInitIndexAccessInfo(Relation rel *** 1082,1095 **** indclass = (oidvector *) DatumGetPointer(indclassDatum); /* ! * Fill the operator and support procedure OID arrays, as well as the info ! * about opfamilies and opclass input types. (aminfo and supportinfo are ! * left as zeroes, and are filled on-the-fly when used) */ ! IndexSupportInitialize(indclass, ! relation->rd_operator, relation->rd_support, relation->rd_opfamily, relation->rd_opcintype, ! amstrategies, amsupport, natts); /* * Similarly extract indoption and copy it to the cache entry --- 1066,1078 ---- indclass = (oidvector *) DatumGetPointer(indclassDatum); /* ! * Fill the support procedure OID array, as well as the info about ! * opfamilies and opclass input types. (aminfo and supportinfo are left ! * as zeroes, and are filled on-the-fly when used) */ ! IndexSupportInitialize(indclass, relation->rd_support, relation->rd_opfamily, relation->rd_opcintype, ! amsupport, natts); /* * Similarly extract indoption and copy it to the cache entry *************** RelationInitIndexAccessInfo(Relation rel *** 1118,1139 **** * Initializes an index's cached opclass information, * given the index's pg_index.indclass entry. * ! * Data is returned into *indexOperator, *indexSupport, *opFamily, and ! * *opcInType, which are arrays allocated by the caller. * ! * The caller also passes maxStrategyNumber, maxSupportNumber, and ! * maxAttributeNumber, since these indicate the size of the arrays ! * it has allocated --- but in practice these numbers must always match ! * those obtainable from the system catalog entries for the index and ! * access method. */ static void IndexSupportInitialize(oidvector *indclass, - Oid *indexOperator, RegProcedure *indexSupport, Oid *opFamily, Oid *opcInType, - StrategyNumber maxStrategyNumber, StrategyNumber maxSupportNumber, AttrNumber maxAttributeNumber) { --- 1101,1119 ---- * Initializes an index's cached opclass information, * given the index's pg_index.indclass entry. * ! * Data is returned into *indexSupport, *opFamily, and *opcInType, ! * which are arrays allocated by the caller. * ! * The caller also passes maxSupportNumber and maxAttributeNumber, since these ! * indicate the size of the arrays it has allocated --- but in practice these ! * numbers must always match those obtainable from the system catalog entries ! * for the index and access method. */ static void IndexSupportInitialize(oidvector *indclass, RegProcedure *indexSupport, Oid *opFamily, Oid *opcInType, StrategyNumber maxSupportNumber, AttrNumber maxAttributeNumber) { *************** IndexSupportInitialize(oidvector *indcla *** 1148,1163 **** /* look up the info for this opclass, using a cache */ opcentry = LookupOpclassInfo(indclass->values[attIndex], - maxStrategyNumber, maxSupportNumber); /* copy cached data into relcache entry */ opFamily[attIndex] = opcentry->opcfamily; opcInType[attIndex] = opcentry->opcintype; - if (maxStrategyNumber > 0) - memcpy(&indexOperator[attIndex * maxStrategyNumber], - opcentry->operatorOids, - maxStrategyNumber * sizeof(Oid)); if (maxSupportNumber > 0) memcpy(&indexSupport[attIndex * maxSupportNumber], opcentry->supportProcs, --- 1128,1138 ---- *************** IndexSupportInitialize(oidvector *indcla *** 1171,1179 **** * This routine maintains a per-opclass cache of the information needed * by IndexSupportInitialize(). This is more efficient than relying on * the catalog cache, because we can load all the info about a particular ! * opclass in a single indexscan of pg_amproc or pg_amop. * ! * The information from pg_am about expected range of strategy and support * numbers is passed in, rather than being looked up, mainly because the * caller will have it already. * --- 1146,1154 ---- * This routine maintains a per-opclass cache of the information needed * by IndexSupportInitialize(). This is more efficient than relying on * the catalog cache, because we can load all the info about a particular ! * opclass in a single indexscan of pg_amproc. * ! * The information from pg_am about expected range of support function * numbers is passed in, rather than being looked up, mainly because the * caller will have it already. * *************** IndexSupportInitialize(oidvector *indcla *** 1187,1193 **** */ static OpClassCacheEnt * LookupOpclassInfo(Oid operatorClassOid, - StrategyNumber numStrats, StrategyNumber numSupport) { OpClassCacheEnt *opcentry; --- 1162,1167 ---- *************** LookupOpclassInfo(Oid operatorClassOid, *** 1223,1238 **** { /* Need to allocate memory for new entry */ opcentry->valid = false; /* until known OK */ - opcentry->numStrats = numStrats; opcentry->numSupport = numSupport; - if (numStrats > 0) - opcentry->operatorOids = (Oid *) - MemoryContextAllocZero(CacheMemoryContext, - numStrats * sizeof(Oid)); - else - opcentry->operatorOids = NULL; - if (numSupport > 0) opcentry->supportProcs = (RegProcedure *) MemoryContextAllocZero(CacheMemoryContext, --- 1197,1204 ---- *************** LookupOpclassInfo(Oid operatorClassOid, *** 1242,1248 **** } else { - Assert(numStrats == opcentry->numStrats); Assert(numSupport == opcentry->numSupport); } --- 1208,1213 ---- *************** LookupOpclassInfo(Oid operatorClassOid, *** 1273,1279 **** /* * We have to fetch the pg_opclass row to determine its opfamily and ! * opcintype, which are needed to look up the operators and functions. * It'd be convenient to use the syscache here, but that probably doesn't * work while bootstrapping. */ --- 1238,1244 ---- /* * We have to fetch the pg_opclass row to determine its opfamily and ! * opcintype, which are needed to look up related operators and functions. * It'd be convenient to use the syscache here, but that probably doesn't * work while bootstrapping. */ *************** LookupOpclassInfo(Oid operatorClassOid, *** 1298,1342 **** systable_endscan(scan); heap_close(rel, AccessShareLock); - - /* - * Scan pg_amop to obtain operators for the opclass. We only fetch the - * default ones (those with lefttype = righttype = opcintype). - */ - if (numStrats > 0) - { - ScanKeyInit(&skey[0], - Anum_pg_amop_amopfamily, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(opcentry->opcfamily)); - ScanKeyInit(&skey[1], - Anum_pg_amop_amoplefttype, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(opcentry->opcintype)); - ScanKeyInit(&skey[2], - Anum_pg_amop_amoprighttype, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(opcentry->opcintype)); - rel = heap_open(AccessMethodOperatorRelationId, AccessShareLock); - scan = systable_beginscan(rel, AccessMethodStrategyIndexId, indexOK, - SnapshotNow, 3, skey); - - while (HeapTupleIsValid(htup = systable_getnext(scan))) - { - Form_pg_amop amopform = (Form_pg_amop) GETSTRUCT(htup); - - if (amopform->amopstrategy <= 0 || - (StrategyNumber) amopform->amopstrategy > numStrats) - elog(ERROR, "invalid amopstrategy number %d for opclass %u", - amopform->amopstrategy, operatorClassOid); - opcentry->operatorOids[amopform->amopstrategy - 1] = - amopform->amopopr; - } - - systable_endscan(scan); - heap_close(rel, AccessShareLock); - } - /* * Scan pg_amproc to obtain support procs for the opclass. We only fetch * the default ones (those with lefttype = righttype = opcintype). --- 1263,1268 ---- *************** RelationCacheInitializePhase3(void) *** 2907,2924 **** IndexRelationId); load_critical_index(OpclassOidIndexId, OperatorClassRelationId); - load_critical_index(AccessMethodStrategyIndexId, - AccessMethodOperatorRelationId); load_critical_index(AccessMethodProcedureIndexId, AccessMethodProcedureRelationId); - load_critical_index(OperatorOidIndexId, - OperatorRelationId); load_critical_index(RewriteRelRulenameIndexId, RewriteRelationId); load_critical_index(TriggerRelidNameIndexId, TriggerRelationId); ! #define NUM_CRITICAL_LOCAL_INDEXES 9 /* fix if you change list above */ criticalRelcachesBuilt = true; } --- 2833,2846 ---- IndexRelationId); load_critical_index(OpclassOidIndexId, OperatorClassRelationId); load_critical_index(AccessMethodProcedureIndexId, AccessMethodProcedureRelationId); load_critical_index(RewriteRelRulenameIndexId, RewriteRelationId); load_critical_index(TriggerRelidNameIndexId, TriggerRelationId); ! #define NUM_CRITICAL_LOCAL_INDEXES 7 /* fix if you change list above */ criticalRelcachesBuilt = true; } *************** load_relcache_init_file(bool shared) *** 4044,4050 **** MemoryContext indexcxt; Oid *opfamily; Oid *opcintype; - Oid *operator; RegProcedure *support; int nsupport; int16 *indoption; --- 3966,3971 ---- *************** load_relcache_init_file(bool shared) *** 4105,4121 **** rel->rd_opcintype = opcintype; ! /* next, read the vector of operator OIDs */ ! if (fread(&len, 1, sizeof(len), fp) != sizeof(len)) ! goto read_failed; ! ! operator = (Oid *) MemoryContextAlloc(indexcxt, len); ! if (fread(operator, 1, len, fp) != len) ! goto read_failed; ! ! rel->rd_operator = operator; ! ! /* next, read the vector of support procedures */ if (fread(&len, 1, sizeof(len), fp) != sizeof(len)) goto read_failed; support = (RegProcedure *) MemoryContextAlloc(indexcxt, len); --- 4026,4032 ---- rel->rd_opcintype = opcintype; ! /* next, read the vector of support procedure OIDs */ if (fread(&len, 1, sizeof(len), fp) != sizeof(len)) goto read_failed; support = (RegProcedure *) MemoryContextAlloc(indexcxt, len); *************** load_relcache_init_file(bool shared) *** 4154,4160 **** Assert(rel->rd_aminfo == NULL); Assert(rel->rd_opfamily == NULL); Assert(rel->rd_opcintype == NULL); - Assert(rel->rd_operator == NULL); Assert(rel->rd_support == NULL); Assert(rel->rd_supportinfo == NULL); Assert(rel->rd_indoption == NULL); --- 4065,4070 ---- *************** write_relcache_init_file(bool shared) *** 4371,4382 **** relform->relnatts * sizeof(Oid), fp); ! /* next, write the vector of operator OIDs */ ! write_item(rel->rd_operator, ! relform->relnatts * (am->amstrategies * sizeof(Oid)), ! fp); ! ! /* next, write the vector of support procedures */ write_item(rel->rd_support, relform->relnatts * (am->amsupport * sizeof(RegProcedure)), fp); --- 4281,4287 ---- relform->relnatts * sizeof(Oid), fp); ! /* next, write the vector of support procedure OIDs */ write_item(rel->rd_support, relform->relnatts * (am->amsupport * sizeof(RegProcedure)), fp); diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 785acc955ad652254c547d015715ec6ac1841925..d084338f356206c354a0a85179ea358862eab5bb 100644 *** a/src/include/nodes/relation.h --- b/src/include/nodes/relation.h *************** typedef struct RelOptInfo *** 421,440 **** * IndexOptInfo * Per-index information for planning/optimization * ! * Prior to Postgres 7.0, RelOptInfo was used to describe both relations ! * and indexes, but that created confusion without actually doing anything ! * useful. So now we have a separate IndexOptInfo struct for indexes. ! * ! * opfamily[], indexkeys[], opcintype[], fwdsortop[], revsortop[], ! * and nulls_first[] each have ncolumns entries. * * Zeroes in the indexkeys[] array indicate index columns that are * expressions; there is one element in indexprs for each such column. * ! * For an unordered index, the sortop arrays contains zeroes. Note that ! * fwdsortop[] and nulls_first[] describe the sort ordering of a forward ! * indexscan; we can also consider a backward indexscan, which will ! * generate sort order described by revsortop/!nulls_first. * * The indexprs and indpred expressions have been run through * prepqual.c and eval_const_expressions() for ease of matching to --- 421,437 ---- * IndexOptInfo * Per-index information for planning/optimization * ! * opfamily[], indexkeys[], and opcintype[] each have ncolumns entries. ! * sortopfamily[], reverse_sort[], and nulls_first[] likewise have ! * ncolumns entries, if the index is ordered; but if it is unordered, ! * those pointers are NULL. * * Zeroes in the indexkeys[] array indicate index columns that are * expressions; there is one element in indexprs for each such column. * ! * For an ordered index, reverse_sort[] and nulls_first[] describe the ! * sort ordering of a forward indexscan; we can also consider a backward ! * indexscan, which will generate the reverse ordering. * * The indexprs and indpred expressions have been run through * prepqual.c and eval_const_expressions() for ease of matching to *************** typedef struct IndexOptInfo *** 457,464 **** Oid *opfamily; /* OIDs of operator families for columns */ int *indexkeys; /* column numbers of index's keys, or 0 */ Oid *opcintype; /* OIDs of opclass declared input data types */ ! Oid *fwdsortop; /* OIDs of sort operators for each column */ ! Oid *revsortop; /* OIDs of sort operators for backward scan */ bool *nulls_first; /* do NULLs come first in the sort order? */ Oid relam; /* OID of the access method (in pg_am) */ --- 454,461 ---- Oid *opfamily; /* OIDs of operator families for columns */ int *indexkeys; /* column numbers of index's keys, or 0 */ Oid *opcintype; /* OIDs of opclass declared input data types */ ! Oid *sortopfamily; /* OIDs of btree opfamilies, if orderable */ ! bool *reverse_sort; /* is sort order descending? */ bool *nulls_first; /* do NULLs come first in the sort order? */ Oid relam; /* OID of the access method (in pg_am) */ diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 9ad92c299e8355ce9874478c76d71330b940e382..39e0365c0b124022f2ec487d8be6a4fa2668760d 100644 *** a/src/include/utils/rel.h --- b/src/include/utils/rel.h *************** typedef struct RelationData *** 178,187 **** /* * index access support info (used only for an index relation) * ! * Note: only default operators and support procs for each opclass are ! * cached, namely those with lefttype and righttype equal to the opclass's ! * opcintype. The arrays are indexed by strategy or support number, which ! * is a sufficient identifier given that restriction. * * Note: rd_amcache is available for index AMs to cache private data about * an index. This must be just a cache since it may get reset at any time --- 178,187 ---- /* * index access support info (used only for an index relation) * ! * Note: only default support procs for each opclass are cached, namely ! * those with lefttype and righttype equal to the opclass's opcintype. ! * The arrays are indexed by support function number, which is a ! * sufficient identifier given that restriction. * * Note: rd_amcache is available for index AMs to cache private data about * an index. This must be just a cache since it may get reset at any time *************** typedef struct RelationData *** 194,200 **** RelationAmInfo *rd_aminfo; /* lookup info for funcs found in pg_am */ Oid *rd_opfamily; /* OIDs of op families for each index col */ Oid *rd_opcintype; /* OIDs of opclass declared input data types */ - Oid *rd_operator; /* OIDs of index operators */ RegProcedure *rd_support; /* OIDs of support procedures */ FmgrInfo *rd_supportinfo; /* lookup info for support procedures */ int16 *rd_indoption; /* per-column AM-specific flags */ --- 194,199 ----
В списке pgsql-hackers по дате отправления:
Предыдущее
От: Tom LaneДата:
Сообщение: Re: [PATCH] Return command tag 'REPLACE X' for CREATE OR REPLACE statements.