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 23027.1290981800@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Rethinking representation of sort/hash semantics in queries and plans  (Tom Lane <tgl@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:
> (For some extra amusement, trace through where
> build_index_pathkeys' data comes from...)

While I don't propose to implement right away the whole SortGroupClause
and plan tree modification sketched above, I did look into fixing
build_index_pathkeys so that it doesn't uselessly convert from
opfamilies to sort operators and back again.  The main reason this is
relevant at the moment is that we could get rid of relcache.c's caching
of operators related to indexes, which seems possibly useful in
connection with the current discussion of backend startup time.

What becomes apparent when you look closely at what that code is doing
is that it's catering to the possibility of an amcanorder index access
method that isn't btree.  As things stand in HEAD, an add-on index
access method would be recognized as supporting ordering so long as it
registers the regular comparison operators (<, >, etc) with the same
index strategy numbers as btree.  The reason that it would work is that
those operators would be found as the fwdsortop/revsortop entries for
the index, and then looking up those operator OIDs in btree opfamilies
would locate the corresponding btree opfamily OIDs, which is what you
have to have to match to a pathkey's opfamily.

In the attached draft patch that would no longer work, because the new
access method would have to have the exact same opfamily OIDs as btree
in order to match to btree-derived pathkeys --- and of course it can't
have that, since access method is a property of an opfamily.

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.

Thoughts?  Anyone particularly opposed to pursuing an optimization
of this kind at all?

            regards, tom lane

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.

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..a29401ba48c7776f91fdb38bbc1100801661327b 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,244 ----
              info->amhasgetbitmap = OidIsValid(indexRelation->rd_am->amgetbitmap);

              /*
!              * Fetch the ordering information for the index, if any.
!              *
!              * Currently, we assume that any orderable index uses btree's
!              * opfamily numbers, which really is equivalent to supposing that
!              * btree is the only amcanorder index type.  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.)
               */
              if (indexRelation->rd_am->amcanorder)
              {
!                 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
+             {
+                 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/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) */


В списке pgsql-hackers по дате отправления:

Предыдущее
От: "Kevin Grittner"
Дата:
Сообщение: SSI using rw-conflict lists
Следующее
От: Kenneth Marshall
Дата:
Сообщение: Re: Report: Linux huge pages with Postgres