Re: bitmapscan changes patch review - Mailing list pgsql-patches

From Joshua D. Drake
Subject Re: bitmapscan changes patch review
Date
Msg-id 467BDC1F.6060600@commandprompt.com
Whole thread Raw
In response to bitmapscan changes patch review  (Alexey Klyukin <alexk@commandprompt.com>)
List pgsql-patches
Alexey Klyukin wrote:
> Hi,
>
> Here is a patch by Heikki Linnakangas with changes for amgetmulti index
> access method to amgetbitmap, which returns all matching tuples at once.
> The patch also introduces support for candidate matches. The original
> post is here:
> http://archives.postgresql.org/pgsql-patches/2007-03/msg00163.php
>
> I've made minor changes to the patch:
>
> - fixed all rejects when applying it to the current CVS head.
> - fixed counting ntids in gistnext if TIDBitmap is not null.
> - added missing expected/bitmapops.out
>
> It passes all regression tests on my system.

Any comments on the below?

Sincerely,

Joshua D. Drake




>
> Regards,
>
>
> ------------------------------------------------------------------------
>
> diff -c -N -r ./src/backend/access/gin/ginget.c ../pgsql.new/src/backend/access/gin/ginget.c
> *** ./src/backend/access/gin/ginget.c    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/backend/access/gin/ginget.c    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 476,509 ****
>   #define GinIsVoidRes(s)        ( ((GinScanOpaque) scan->opaque)->isVoidRes == true )
>
>   Datum
> ! gingetmulti(PG_FUNCTION_ARGS)
>   {
>       IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
> !     ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1);
> !     int32        max_tids = PG_GETARG_INT32(2);
> !     int32       *returned_tids = (int32 *) PG_GETARG_POINTER(3);
>
>       if (GinIsNewKey(scan))
>           newScanKey(scan);
>
> -     *returned_tids = 0;
> -
>       if (GinIsVoidRes(scan))
> !         PG_RETURN_BOOL(false);
>
>       startScan(scan);
>
> !     do
>       {
> !         if (scanGetItem(scan, tids + *returned_tids))
> !             (*returned_tids)++;
> !         else
>               break;
> !     } while (*returned_tids < max_tids);
>
>       stopScan(scan);
>
> !     PG_RETURN_BOOL(*returned_tids == max_tids);
>   }
>
>   Datum
> --- 476,510 ----
>   #define GinIsVoidRes(s)        ( ((GinScanOpaque) scan->opaque)->isVoidRes == true )
>
>   Datum
> ! gingetbitmap(PG_FUNCTION_ARGS)
>   {
>       IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
> !     TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
> !     int32 ntids;
>
>       if (GinIsNewKey(scan))
>           newScanKey(scan);
>
>       if (GinIsVoidRes(scan))
> !         PG_RETURN_INT32(0);
>
>       startScan(scan);
>
> !     ntids = 0;
> !     for(;;)
>       {
> !         ItemPointerData iptr;
> !
> !         if (!scanGetItem(scan, &iptr))
>               break;
> !
> !         ntids++;
> !         tbm_add_tuples(tbm, &iptr, 1, false);
> !     }
>
>       stopScan(scan);
>
> !     PG_RETURN_INT32(ntids);
>   }
>
>   Datum
> diff -c -N -r ./src/backend/access/gist/gistget.c ../pgsql.new/src/backend/access/gist/gistget.c
> *** ./src/backend/access/gist/gistget.c    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/backend/access/gist/gistget.c    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 22,28 ****
>
>   static OffsetNumber gistfindnext(IndexScanDesc scan, OffsetNumber n,
>                ScanDirection dir);
> ! static int    gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids, int maxtids, bool
ignore_killed_tuples);
>   static bool gistindex_keytest(IndexTuple tuple, IndexScanDesc scan,
>                     OffsetNumber offset);
>
> --- 22,30 ----
>
>   static OffsetNumber gistfindnext(IndexScanDesc scan, OffsetNumber n,
>                ScanDirection dir);
> ! static int    gistnext(IndexScanDesc scan, ScanDirection dir,
> !                      ItemPointer tid, TIDBitmap *tbm,
> !                      bool ignore_killed_tuples);
>   static bool gistindex_keytest(IndexTuple tuple, IndexScanDesc scan,
>                     OffsetNumber offset);
>
> ***************
> *** 114,145 ****
>        * tuples, continue looping until we find a non-killed tuple that matches
>        * the search key.
>        */
> !     res = (gistnext(scan, dir, &tid, 1, scan->ignore_killed_tuples)) ? true : false;
>
>       PG_RETURN_BOOL(res);
>   }
>
>   Datum
> ! gistgetmulti(PG_FUNCTION_ARGS)
>   {
>       IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
> !     ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1);
> !     int32        max_tids = PG_GETARG_INT32(2);
> !     int32       *returned_tids = (int32 *) PG_GETARG_POINTER(3);
>
> !     *returned_tids = gistnext(scan, ForwardScanDirection, tids, max_tids, false);
>
> !     PG_RETURN_BOOL(*returned_tids == max_tids);
>   }
>
>   /*
>    * Fetch a tuples that matchs the search key; this can be invoked
>    * either to fetch the first such tuple or subsequent matching
> !  * tuples. Returns true iff a matching tuple was found.
>    */
>   static int
> ! gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
> !          int maxtids, bool ignore_killed_tuples)
>   {
>       Page        p;
>       OffsetNumber n;
> --- 116,152 ----
>        * tuples, continue looping until we find a non-killed tuple that matches
>        * the search key.
>        */
> !     res = (gistnext(scan, dir, &tid, NULL, scan->ignore_killed_tuples)) ? true : false;
>
>       PG_RETURN_BOOL(res);
>   }
>
>   Datum
> ! gistgetbitmap(PG_FUNCTION_ARGS)
>   {
>       IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
> !     TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
> !     int32       ntids;
>
> !     ntids = gistnext(scan, ForwardScanDirection, NULL, tbm, false);
>
> !     PG_RETURN_INT32(ntids);
>   }
>
>   /*
>    * Fetch a tuples that matchs the search key; this can be invoked
>    * either to fetch the first such tuple or subsequent matching
> !  * tuples.
> !  *
> !  * This function is used by both gistgettuple and gistgetbitmap. When
> !  * invoked from gistgettuple, tbm is null and the next matching tuple
> !  * is returned in *tid. When invoked from getbitmap, tid is null and
> !  * all matching tuples are added to tbm and tid is null. In both cases,
> !  * the number of returned tuples is returned.
>    */
>   static int
> ! gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tid,
> !          TIDBitmap *tbm, bool ignore_killed_tuples)
>   {
>       Page        p;
>       OffsetNumber n;
> ***************
> *** 292,304 ****
>                   if (!(ignore_killed_tuples && ItemIdDeleted(PageGetItemId(p, n))))
>                   {
>                       it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
> -                     tids[ntids] = scan->xs_ctup.t_self = it->t_tid;
>                       ntids++;
> !
> !                     if (ntids == maxtids)
>                       {
>                           LockBuffer(so->curbuf, GIST_UNLOCK);
> !                         return ntids;
>                       }
>                   }
>               }
> --- 299,313 ----
>                   if (!(ignore_killed_tuples && ItemIdDeleted(PageGetItemId(p, n))))
>                   {
>                       it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
>                       ntids++;
> !                     if(tbm != NULL)
> !                         tbm_add_tuples(tbm, &it->t_tid, 1, false);
> !                     else
>                       {
> +                         *tid = scan->xs_ctup.t_self = it->t_tid;
> +
>                           LockBuffer(so->curbuf, GIST_UNLOCK);
> !                         return ntids; /* always 1 */
>                       }
>                   }
>               }
> diff -c -N -r ./src/backend/access/hash/hash.c ../pgsql.new/src/backend/access/hash/hash.c
> *** ./src/backend/access/hash/hash.c    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/backend/access/hash/hash.c    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 239,310 ****
>
>
>   /*
> !  *    hashgetmulti() -- get multiple tuples at once
> !  *
> !  * This is a somewhat generic implementation: it avoids lock reacquisition
> !  * overhead, but there's no smarts about picking especially good stopping
> !  * points such as index page boundaries.
>    */
>   Datum
> ! hashgetmulti(PG_FUNCTION_ARGS)
>   {
>       IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
> !     ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1);
> !     int32        max_tids = PG_GETARG_INT32(2);
> !     int32       *returned_tids = (int32 *) PG_GETARG_POINTER(3);
>       HashScanOpaque so = (HashScanOpaque) scan->opaque;
> -     Relation    rel = scan->indexRelation;
>       bool        res = true;
>       int32        ntids = 0;
>
> !     /*
> !      * We hold pin but not lock on current buffer while outside the hash AM.
> !      * Reacquire the read lock here.
> !      */
> !     if (BufferIsValid(so->hashso_curbuf))
> !         _hash_chgbufaccess(rel, so->hashso_curbuf, HASH_NOLOCK, HASH_READ);
>
> !     while (ntids < max_tids)
>       {
> !         /*
> !          * Start scan, or advance to next tuple.
> !          */
> !         if (ItemPointerIsValid(&(so->hashso_curpos)))
> !             res = _hash_next(scan, ForwardScanDirection);
> !         else
> !             res = _hash_first(scan, ForwardScanDirection);
> !
>           /*
>            * Skip killed tuples if asked to.
>            */
>           if (scan->ignore_killed_tuples)
>           {
> !             while (res)
> !             {
> !                 Page        page;
> !                 OffsetNumber offnum;
>
> !                 offnum = ItemPointerGetOffsetNumber(&(so->hashso_curpos));
> !                 page = BufferGetPage(so->hashso_curbuf);
> !                 if (!ItemIdDeleted(PageGetItemId(page, offnum)))
> !                     break;
> !                 res = _hash_next(scan, ForwardScanDirection);
> !             }
>           }
>
> -         if (!res)
> -             break;
>           /* Save tuple ID, and continue scanning */
> !         tids[ntids] = scan->xs_ctup.t_self;
> !         ntids++;
> !     }
>
> !     /* Release read lock on current buffer, but keep it pinned */
> !     if (BufferIsValid(so->hashso_curbuf))
> !         _hash_chgbufaccess(rel, so->hashso_curbuf, HASH_READ, HASH_NOLOCK);
>
> !     *returned_tids = ntids;
> !     PG_RETURN_BOOL(res);
>   }
>
>
> --- 239,286 ----
>
>
>   /*
> !  *    hashgetbitmap() -- get multiple tuples at once
>    */
>   Datum
> ! hashgetbitmap(PG_FUNCTION_ARGS)
>   {
>       IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
> !     TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
>       HashScanOpaque so = (HashScanOpaque) scan->opaque;
>       bool        res = true;
>       int32        ntids = 0;
>
> !     res = _hash_first(scan, ForwardScanDirection);
>
> !     while (res)
>       {
> !         bool add_tuple;
>           /*
>            * Skip killed tuples if asked to.
>            */
>           if (scan->ignore_killed_tuples)
>           {
> !             Page        page;
> !             OffsetNumber offnum;
>
> !             offnum = ItemPointerGetOffsetNumber(&(so->hashso_curpos));
> !             page = BufferGetPage(so->hashso_curbuf);
> !             add_tuple = !ItemIdDeleted(PageGetItemId(page, offnum));
>           }
> +         else
> +             add_tuple = true;
>
>           /* Save tuple ID, and continue scanning */
> !         if (add_tuple)
> !         {
> !             tbm_add_tuples(tbm, &scan->xs_ctup.t_self, 1, false);
> !             ntids++;
> !         }
>
> !         res = _hash_next(scan, ForwardScanDirection);
> !     }
>
> !     PG_RETURN_INT32(ntids);
>   }
>
>
> diff -c -N -r ./src/backend/access/index/indexam.c ../pgsql.new/src/backend/access/index/indexam.c
> *** ./src/backend/access/index/indexam.c    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/backend/access/index/indexam.c    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 21,27 ****
>    *        index_markpos    - mark a scan position
>    *        index_restrpos    - restore a scan position
>    *        index_getnext    - get the next tuple from a scan
> !  *        index_getmulti    - get multiple tuples from a scan
>    *        index_bulk_delete    - bulk deletion of index tuples
>    *        index_vacuum_cleanup    - post-deletion cleanup of an index
>    *        index_getprocid - get a support procedure OID
> --- 21,27 ----
>    *        index_markpos    - mark a scan position
>    *        index_restrpos    - restore a scan position
>    *        index_getnext    - get the next tuple from a scan
> !  *        index_getbitmap    - get all tuples from a scan
>    *        index_bulk_delete    - bulk deletion of index tuples
>    *        index_vacuum_cleanup    - post-deletion cleanup of an index
>    *        index_getprocid - get a support procedure OID
> ***************
> *** 66,71 ****
> --- 66,72 ----
>   #include "access/heapam.h"
>   #include "pgstat.h"
>   #include "utils/relcache.h"
> + #include "nodes/tidbitmap.h"
>
>
>   /* ----------------------------------------------------------------
> ***************
> *** 506,549 ****
>   }
>
>   /* ----------------
> !  *        index_getmulti - get multiple tuples from an index scan
>    *
> !  * Collects the TIDs of multiple heap tuples satisfying the scan keys.
>    * Since there's no interlock between the index scan and the eventual heap
>    * access, this is only safe to use with MVCC-based snapshots: the heap
>    * item slot could have been replaced by a newer tuple by the time we get
>    * to it.
>    *
> !  * A TRUE result indicates more calls should occur; a FALSE result says the
> !  * scan is done.  *returned_tids could be zero or nonzero in either case.
>    * ----------------
>    */
> ! bool
> ! index_getmulti(IndexScanDesc scan,
> !                ItemPointer tids, int32 max_tids,
> !                int32 *returned_tids)
>   {
>       FmgrInfo   *procedure;
> !     bool        found;
>
>       SCAN_CHECKS;
> !     GET_SCAN_PROCEDURE(amgetmulti);
>
>       /* just make sure this is false... */
>       scan->kill_prior_tuple = false;
>
>       /*
> !      * have the am's getmulti proc do all the work.
>        */
> !     found = DatumGetBool(FunctionCall4(procedure,
> !                                        PointerGetDatum(scan),
> !                                        PointerGetDatum(tids),
> !                                        Int32GetDatum(max_tids),
> !                                        PointerGetDatum(returned_tids)));
>
> !     pgstat_count_index_tuples(scan->indexRelation, *returned_tids);
>
> !     return found;
>   }
>
>   /* ----------------
> --- 507,545 ----
>   }
>
>   /* ----------------
> !  *        index_getbitmap - get all tuples at once from an index scan
>    *
> !  * Adds the TIDs of all heap tuples satisfying the scan keys to a bitmap.
>    * Since there's no interlock between the index scan and the eventual heap
>    * access, this is only safe to use with MVCC-based snapshots: the heap
>    * item slot could have been replaced by a newer tuple by the time we get
>    * to it.
>    *
> !  * Returns the number of matching tuples found.
>    * ----------------
>    */
> ! int32
> ! index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
>   {
>       FmgrInfo   *procedure;
> !     int32 ntids;
>
>       SCAN_CHECKS;
> !     GET_SCAN_PROCEDURE(amgetbitmap);
>
>       /* just make sure this is false... */
>       scan->kill_prior_tuple = false;
>
>       /*
> !      * have the am's getbitmap proc do all the work.
>        */
> !     ntids = DatumGetInt32(FunctionCall2(procedure,
> !                                            PointerGetDatum(scan),
> !                                           PointerGetDatum(bitmap)));
>
> !     pgstat_count_index_tuples(scan->indexRelation, ntids);
>
> !     return ntids;
>   }
>
>   /* ----------------
> diff -c -N -r ./src/backend/access/nbtree/nbtree.c ../pgsql.new/src/backend/access/nbtree/nbtree.c
> *** ./src/backend/access/nbtree/nbtree.c    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/backend/access/nbtree/nbtree.c    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 278,302 ****
>   }
>
>   /*
> !  * btgetmulti() -- get multiple tuples at once
> !  *
> !  * In the current implementation there seems no strong reason to stop at
> !  * index page boundaries; we just press on until we fill the caller's buffer
> !  * or run out of matches.
>    */
>   Datum
> ! btgetmulti(PG_FUNCTION_ARGS)
>   {
>       IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
> !     ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1);
> !     int32        max_tids = PG_GETARG_INT32(2);
> !     int32       *returned_tids = (int32 *) PG_GETARG_POINTER(3);
>       BTScanOpaque so = (BTScanOpaque) scan->opaque;
>       bool        res = true;
>       int32        ntids = 0;
> !
> !     if (max_tids <= 0)            /* behave correctly in boundary case */
> !         PG_RETURN_BOOL(true);
>
>       /* If we haven't started the scan yet, fetch the first page & tuple. */
>       if (!BTScanPosIsValid(so->currPos))
> --- 278,294 ----
>   }
>
>   /*
> !  * btgetbitmap() -- gets all matching tuples, and adds them to a bitmap
>    */
>   Datum
> ! btgetbitmap(PG_FUNCTION_ARGS)
>   {
>       IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
> !     TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
>       BTScanOpaque so = (BTScanOpaque) scan->opaque;
>       bool        res = true;
>       int32        ntids = 0;
> !     ItemPointer heapTid;
>
>       /* If we haven't started the scan yet, fetch the first page & tuple. */
>       if (!BTScanPosIsValid(so->currPos))
> ***************
> *** 305,319 ****
>           if (!res)
>           {
>               /* empty scan */
> !             *returned_tids = ntids;
> !             PG_RETURN_BOOL(res);
>           }
>           /* Save tuple ID, and continue scanning */
> !         tids[ntids] = scan->xs_ctup.t_self;
>           ntids++;
>       }
>
> !     while (ntids < max_tids)
>       {
>           /*
>            * Advance to next tuple within page.  This is the same as the easy
> --- 297,312 ----
>           if (!res)
>           {
>               /* empty scan */
> !             PG_RETURN_INT32(0);
>           }
>           /* Save tuple ID, and continue scanning */
> !         heapTid = &scan->xs_ctup.t_self;
> !         tbm_add_tuples(tbm, heapTid, 1, false);
> !
>           ntids++;
>       }
>
> !     while (true)
>       {
>           /*
>            * Advance to next tuple within page.  This is the same as the easy
> ***************
> *** 328,339 ****
>           }
>
>           /* Save tuple ID, and continue scanning */
> !         tids[ntids] = so->currPos.items[so->currPos.itemIndex].heapTid;
>           ntids++;
>       }
>
> !     *returned_tids = ntids;
> !     PG_RETURN_BOOL(res);
>   }
>
>   /*
> --- 321,333 ----
>           }
>
>           /* Save tuple ID, and continue scanning */
> !         heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
> !         tbm_add_tuples(tbm, heapTid, 1, false);
> !
>           ntids++;
>       }
>
> !     PG_RETURN_INT32(ntids);
>   }
>
>   /*
> diff -c -N -r ./src/backend/executor/nodeBitmapHeapscan.c ../pgsql.new/src/backend/executor/nodeBitmapHeapscan.c
> *** ./src/backend/executor/nodeBitmapHeapscan.c    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/backend/executor/nodeBitmapHeapscan.c    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 204,210 ****
>            * If we are using lossy info, we have to recheck the qual conditions
>            * at every tuple.
>            */
> !         if (tbmres->ntuples < 0)
>           {
>               econtext->ecxt_scantuple = slot;
>               ResetExprContext(econtext);
> --- 204,210 ----
>            * If we are using lossy info, we have to recheck the qual conditions
>            * at every tuple.
>            */
> !         if (tbmres->ntuples < 0 || tbmres->iscandidates)
>           {
>               econtext->ecxt_scantuple = slot;
>               ResetExprContext(econtext);
> diff -c -N -r ./src/backend/executor/nodeBitmapIndexscan.c ../pgsql.new/src/backend/executor/nodeBitmapIndexscan.c
> *** ./src/backend/executor/nodeBitmapIndexscan.c    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/backend/executor/nodeBitmapIndexscan.c    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 37,46 ****
>   Node *
>   MultiExecBitmapIndexScan(BitmapIndexScanState *node)
>   {
> - #define MAX_TIDS    1024
>       TIDBitmap  *tbm;
>       IndexScanDesc scandesc;
> -     ItemPointerData tids[MAX_TIDS];
>       int32        ntids;
>       double        nTuples = 0;
>       bool        doscan;
> --- 37,44 ----
> ***************
> *** 91,113 ****
>        */
>       while (doscan)
>       {
> !         bool        more = index_getmulti(scandesc, tids, MAX_TIDS, &ntids);
>
> !         if (ntids > 0)
> !         {
> !             tbm_add_tuples(tbm, tids, ntids);
> !             nTuples += ntids;
> !         }
>
>           CHECK_FOR_INTERRUPTS();
>
> !         if (!more)
> !         {
> !             doscan = ExecIndexAdvanceArrayKeys(node->biss_ArrayKeys,
>                                                  node->biss_NumArrayKeys);
> !             if (doscan)            /* reset index scan */
> !                 index_rescan(node->biss_ScanDesc, node->biss_ScanKeys);
> !         }
>       }
>
>       /* must provide our own instrumentation support */
> --- 89,104 ----
>        */
>       while (doscan)
>       {
> !         ntids = index_getbitmap(scandesc, tbm);
>
> !         nTuples += ntids;
>
>           CHECK_FOR_INTERRUPTS();
>
> !         doscan = ExecIndexAdvanceArrayKeys(node->biss_ArrayKeys,
>                                                  node->biss_NumArrayKeys);
> !         if (doscan)            /* reset index scan */
> !             index_rescan(node->biss_ScanDesc, node->biss_ScanKeys);
>       }
>
>       /* must provide our own instrumentation support */
> diff -c -N -r ./src/backend/nodes/tidbitmap.c ../pgsql.new/src/backend/nodes/tidbitmap.c
> *** ./src/backend/nodes/tidbitmap.c    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/backend/nodes/tidbitmap.c    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 10,16 ****
>    * Also, since we wish to be able to store very large tuple sets in
>    * memory with this data structure, we support "lossy" storage, in which
>    * we no longer remember individual tuple offsets on a page but only the
> !  * fact that a particular page needs to be visited.
>    *
>    * The "lossy" storage uses one bit per disk page, so at the standard 8K
>    * BLCKSZ, we can represent all pages in 64Gb of disk space in about 1Mb
> --- 10,21 ----
>    * Also, since we wish to be able to store very large tuple sets in
>    * memory with this data structure, we support "lossy" storage, in which
>    * we no longer remember individual tuple offsets on a page but only the
> !  * fact that a particular page needs to be visited. We also support the
> !  * notion of candidate matches, which are like non-lossy matches in that
> !  * the individual tuple offsets are remembered, but the offsets remembered
> !  * are a superset of the actual matches. Candidate matches need to be
> !  * rechecked in the executor to see which ones really match. They are
> !  * used when a lossy page is intersected with a non-lossy page.
>    *
>    * The "lossy" storage uses one bit per disk page, so at the standard 8K
>    * BLCKSZ, we can represent all pages in 64Gb of disk space in about 1Mb
> ***************
> *** 87,92 ****
> --- 92,98 ----
>   {
>       BlockNumber blockno;        /* page number (hashtable key) */
>       bool        ischunk;        /* T = lossy storage, F = exact */
> +     bool        iscandidate;    /* should the results be rechecked? */
>       bitmapword    words[Max(WORDS_PER_PAGE, WORDS_PER_CHUNK)];
>   } PagetableEntry;
>
> ***************
> *** 145,150 ****
> --- 151,160 ----
>   static void tbm_lossify(TIDBitmap *tbm);
>   static int    tbm_comparator(const void *left, const void *right);
>
> + #ifdef TIDBITMAP_DEBUG
> + static void dump_pte(const PagetableEntry *e);
> + static void tbm_dump(TIDBitmap *tbm);
> + #endif
>
>   /*
>    * tbm_create - create an initially-empty bitmap
> ***************
> *** 247,253 ****
>    * tbm_add_tuples - add some tuple IDs to a TIDBitmap
>    */
>   void
> ! tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids)
>   {
>       int            i;
>
> --- 257,263 ----
>    * tbm_add_tuples - add some tuple IDs to a TIDBitmap
>    */
>   void
> ! tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids, bool candidates)
>   {
>       int            i;
>
> ***************
> *** 281,286 ****
> --- 291,297 ----
>               bitnum = BITNUM(off - 1);
>           }
>           page->words[wordnum] |= ((bitmapword) 1 << bitnum);
> +         page->iscandidate = page->iscandidate || candidates;
>
>           if (tbm->nentries > tbm->maxentries)
>               tbm_lossify(tbm);
> ***************
> *** 361,366 ****
> --- 372,378 ----
>               /* Both pages are exact, merge at the bit level */
>               for (wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++)
>                   apage->words[wordnum] |= bpage->words[wordnum];
> +             apage->iscandidate = apage->iscandidate || bpage->iscandidate;
>           }
>       }
>
> ***************
> *** 472,493 ****
>       else if (tbm_page_is_lossy(b, apage->blockno))
>       {
>           /*
> !          * When the page is lossy in b, we have to mark it lossy in a too. We
> !          * know that no bits need be set in bitmap a, but we do not know which
> !          * ones should be cleared, and we have no API for "at most these
> !          * tuples need be checked".  (Perhaps it's worth adding that?)
>            */
> !         tbm_mark_page_lossy(a, apage->blockno);
>
> -         /*
> -          * Note: tbm_mark_page_lossy will have removed apage from a, and may
> -          * have inserted a new lossy chunk instead.  We can continue the same
> -          * seq_search scan at the caller level, because it does not matter
> -          * whether we visit such a new chunk or not: it will have only the bit
> -          * for apage->blockno set, which is correct.
> -          *
> -          * We must return false here since apage was already deleted.
> -          */
>           return false;
>       }
>       else
> --- 484,496 ----
>       else if (tbm_page_is_lossy(b, apage->blockno))
>       {
>           /*
> !          * Some of the tuples in 'a' might not satisfy the quals for 'b',
> !          * but because the page 'b' is lossy, we don't know which ones.
> !          * Therefore we mark 'a' as candidate to indicate that at most
> !          * those tuples set in 'a' are matches.
>            */
> !         apage->iscandidate = true;
>
>           return false;
>       }
>       else
> ***************
> *** 505,511 ****
> --- 508,516 ----
>                   if (apage->words[wordnum] != 0)
>                       candelete = false;
>               }
> +             apage->iscandidate = apage->iscandidate || bpage->iscandidate;
>           }
> +
>           return candelete;
>       }
>   }
> ***************
> *** 677,682 ****
> --- 682,688 ----
>           }
>           output->blockno = page->blockno;
>           output->ntuples = ntuples;
> +         output->iscandidates = page->iscandidate;
>           tbm->spageptr++;
>           return output;
>       }
> ***************
> *** 936,938 ****
> --- 942,994 ----
>           return 1;
>       return 0;
>   }
> +
> +
> + #ifdef TIDBITMAP_DEBUG
> + static void
> + dump_pte(const PagetableEntry *e)
> + {
> +     int i;
> +     int max;
> +     char str[Max(WORDS_PER_PAGE, WORDS_PER_CHUNK) * BITS_PER_BITMAPWORD + 1];
> +
> +     if(e->ischunk)
> +         max = WORDS_PER_CHUNK * BITS_PER_BITMAPWORD;
> +     else
> +         max = WORDS_PER_PAGE * BITS_PER_BITMAPWORD;
> +
> +     for(i=0; i < max; i++)
> +     {
> +         if(e->words[WORDNUM(i)] & (1<<(BITNUM(i))))
> +             str[i] = '1';
> +         else
> +             str[i] = '0';
> +     }
> +     str[max] = '\0';
> +
> +
> +     elog(LOG, "blockno %d%s%s: %s", e->blockno,
> +          e->ischunk ? " (lossy)" : "",
> +          e->iscandidate ? " (candidates)" : "",
> +          str);
> + }
> +
> +
> + static void
> + tbm_dump(TIDBitmap *tbm)
> + {
> +     int i;
> +
> +     elog(LOG, "Bitmap, %d lossy and %d non-lossy pages", tbm->nchunks, tbm->npages);
> +
> +     if(tbm->status == TBM_ONE_PAGE)
> +         dump_pte(&tbm->entry1);
> +     else
> +     {
> +         for(i = 0; i < tbm->nchunks; i++)
> +             dump_pte(tbm->schunks[i]);
> +         for(i = 0; i < tbm->npages; i++)
> +             dump_pte(tbm->spages[i]);
> +     }
> + }
> + #endif
> diff -c -N -r ./src/include/access/genam.h ../pgsql.new/src/include/access/genam.h
> *** ./src/include/access/genam.h    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/include/access/genam.h    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 17,22 ****
> --- 17,23 ----
>   #include "access/relscan.h"
>   #include "access/sdir.h"
>   #include "nodes/primnodes.h"
> + #include "nodes/tidbitmap.h"
>   #include "storage/lock.h"
>
>   /*
> ***************
> *** 109,117 ****
>   extern HeapTuple index_getnext(IndexScanDesc scan, ScanDirection direction);
>   extern bool index_getnext_indexitem(IndexScanDesc scan,
>                           ScanDirection direction);
> ! extern bool index_getmulti(IndexScanDesc scan,
> !                ItemPointer tids, int32 max_tids,
> !                int32 *returned_tids);
>
>   extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info,
>                     IndexBulkDeleteResult *stats,
> --- 110,116 ----
>   extern HeapTuple index_getnext(IndexScanDesc scan, ScanDirection direction);
>   extern bool index_getnext_indexitem(IndexScanDesc scan,
>                           ScanDirection direction);
> ! extern int index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap);
>
>   extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info,
>                     IndexBulkDeleteResult *stats,
> diff -c -N -r ./src/include/access/gin.h ../pgsql.new/src/include/access/gin.h
> *** ./src/include/access/gin.h    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/include/access/gin.h    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 421,427 ****
>   #define ItemPointerSetMin(p)    ItemPointerSet( (p), (BlockNumber)0, (OffsetNumber)0)
>   #define ItemPointerIsMin(p) ( ItemPointerGetBlockNumber(p) == (BlockNumber)0 && ItemPointerGetOffsetNumber(p) ==
(OffsetNumber)0) 
>
> ! extern Datum gingetmulti(PG_FUNCTION_ARGS);
>   extern Datum gingettuple(PG_FUNCTION_ARGS);
>
>   /* ginvacuum.c */
> --- 421,427 ----
>   #define ItemPointerSetMin(p)    ItemPointerSet( (p), (BlockNumber)0, (OffsetNumber)0)
>   #define ItemPointerIsMin(p) ( ItemPointerGetBlockNumber(p) == (BlockNumber)0 && ItemPointerGetOffsetNumber(p) ==
(OffsetNumber)0) 
>
> ! extern Datum gingetbitmap(PG_FUNCTION_ARGS);
>   extern Datum gingettuple(PG_FUNCTION_ARGS);
>
>   /* ginvacuum.c */
> diff -c -N -r ./src/include/access/gist_private.h ../pgsql.new/src/include/access/gist_private.h
> *** ./src/include/access/gist_private.h    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/include/access/gist_private.h    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 271,277 ****
>
>   /* gistget.c */
>   extern Datum gistgettuple(PG_FUNCTION_ARGS);
> ! extern Datum gistgetmulti(PG_FUNCTION_ARGS);
>
>   /* gistutil.c */
>
> --- 271,277 ----
>
>   /* gistget.c */
>   extern Datum gistgettuple(PG_FUNCTION_ARGS);
> ! extern Datum gistgetbitmap(PG_FUNCTION_ARGS);
>
>   /* gistutil.c */
>
> diff -c -N -r ./src/include/access/hash.h ../pgsql.new/src/include/access/hash.h
> *** ./src/include/access/hash.h    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/include/access/hash.h    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 233,239 ****
>   extern Datum hashinsert(PG_FUNCTION_ARGS);
>   extern Datum hashbeginscan(PG_FUNCTION_ARGS);
>   extern Datum hashgettuple(PG_FUNCTION_ARGS);
> ! extern Datum hashgetmulti(PG_FUNCTION_ARGS);
>   extern Datum hashrescan(PG_FUNCTION_ARGS);
>   extern Datum hashendscan(PG_FUNCTION_ARGS);
>   extern Datum hashmarkpos(PG_FUNCTION_ARGS);
> --- 233,239 ----
>   extern Datum hashinsert(PG_FUNCTION_ARGS);
>   extern Datum hashbeginscan(PG_FUNCTION_ARGS);
>   extern Datum hashgettuple(PG_FUNCTION_ARGS);
> ! extern Datum hashgetbitmap(PG_FUNCTION_ARGS);
>   extern Datum hashrescan(PG_FUNCTION_ARGS);
>   extern Datum hashendscan(PG_FUNCTION_ARGS);
>   extern Datum hashmarkpos(PG_FUNCTION_ARGS);
> diff -c -N -r ./src/include/access/nbtree.h ../pgsql.new/src/include/access/nbtree.h
> *** ./src/include/access/nbtree.h    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/include/access/nbtree.h    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 500,506 ****
>   extern Datum btinsert(PG_FUNCTION_ARGS);
>   extern Datum btbeginscan(PG_FUNCTION_ARGS);
>   extern Datum btgettuple(PG_FUNCTION_ARGS);
> ! extern Datum btgetmulti(PG_FUNCTION_ARGS);
>   extern Datum btrescan(PG_FUNCTION_ARGS);
>   extern Datum btendscan(PG_FUNCTION_ARGS);
>   extern Datum btmarkpos(PG_FUNCTION_ARGS);
> --- 500,506 ----
>   extern Datum btinsert(PG_FUNCTION_ARGS);
>   extern Datum btbeginscan(PG_FUNCTION_ARGS);
>   extern Datum btgettuple(PG_FUNCTION_ARGS);
> ! extern Datum btgetbitmap(PG_FUNCTION_ARGS);
>   extern Datum btrescan(PG_FUNCTION_ARGS);
>   extern Datum btendscan(PG_FUNCTION_ARGS);
>   extern Datum btmarkpos(PG_FUNCTION_ARGS);
> diff -c -N -r ./src/include/catalog/pg_am.h ../pgsql.new/src/include/catalog/pg_am.h
> *** ./src/include/catalog/pg_am.h    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/include/catalog/pg_am.h    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 56,62 ****
>       regproc        aminsert;        /* "insert this tuple" function */
>       regproc        ambeginscan;    /* "start new scan" function */
>       regproc        amgettuple;        /* "next valid tuple" function */
> !     regproc        amgetmulti;        /* "fetch multiple tuples" function */
>       regproc        amrescan;        /* "restart this scan" function */
>       regproc        amendscan;        /* "end this scan" function */
>       regproc        ammarkpos;        /* "mark current scan position" function */
> --- 56,62 ----
>       regproc        aminsert;        /* "insert this tuple" function */
>       regproc        ambeginscan;    /* "start new scan" function */
>       regproc        amgettuple;        /* "next valid tuple" function */
> !     regproc        amgetbitmap;    /* "fetch multiple tuples" function */
>       regproc        amrescan;        /* "restart this scan" function */
>       regproc        amendscan;        /* "end this scan" function */
>       regproc        ammarkpos;        /* "mark current scan position" function */
> ***************
> *** 94,100 ****
>   #define Anum_pg_am_aminsert                12
>   #define Anum_pg_am_ambeginscan            13
>   #define Anum_pg_am_amgettuple            14
> ! #define Anum_pg_am_amgetmulti            15
>   #define Anum_pg_am_amrescan                16
>   #define Anum_pg_am_amendscan            17
>   #define Anum_pg_am_ammarkpos            18
> --- 94,100 ----
>   #define Anum_pg_am_aminsert                12
>   #define Anum_pg_am_ambeginscan            13
>   #define Anum_pg_am_amgettuple            14
> ! #define Anum_pg_am_amgetbitmap            15
>   #define Anum_pg_am_amrescan                16
>   #define Anum_pg_am_amendscan            17
>   #define Anum_pg_am_ammarkpos            18
> ***************
> *** 110,125 ****
>    * ----------------
>    */
>
> ! DATA(insert OID = 403 (  btree    5 1 t t t t t t f t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan
btmarkposbtrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions )); 
>   DESCR("b-tree index access method");
>   #define BTREE_AM_OID 403
> ! DATA(insert OID = 405 (  hash    1 1 f f f f f f f f hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan
hashendscanhashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions )); 
>   DESCR("hash index access method");
>   #define HASH_AM_OID 405
> ! DATA(insert OID = 783 (  gist    0 7 f f t t t t t t gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan
gistendscangistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions )); 
>   DESCR("GiST index access method");
>   #define GIST_AM_OID 783
> ! DATA(insert OID = 2742 (  gin    0 4 f f f f f f t f gininsert ginbeginscan gingettuple gingetmulti ginrescan
ginendscanginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions )); 
>   DESCR("GIN index access method");
>   #define GIN_AM_OID 2742
>
> --- 110,125 ----
>    * ----------------
>    */
>
> ! DATA(insert OID = 403 (  btree    5 1 t t t t t t f t btinsert btbeginscan btgettuple btgetbitmap btrescan
btendscanbtmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions )); 
>   DESCR("b-tree index access method");
>   #define BTREE_AM_OID 403
> ! DATA(insert OID = 405 (  hash    1 1 f f f f f f f f hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan
hashendscanhashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions )); 
>   DESCR("hash index access method");
>   #define HASH_AM_OID 405
> ! DATA(insert OID = 783 (  gist    0 7 f f t t t t t t gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan
gistendscangistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions )); 
>   DESCR("GiST index access method");
>   #define GIST_AM_OID 783
> ! DATA(insert OID = 2742 (  gin    0 4 f f f f f f t f gininsert ginbeginscan gingettuple gingetbitmap ginrescan
ginendscanginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions )); 
>   DESCR("GIN index access method");
>   #define GIN_AM_OID 2742
>
> diff -c -N -r ./src/include/catalog/pg_proc.h ../pgsql.new/src/include/catalog/pg_proc.h
> *** ./src/include/catalog/pg_proc.h    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/include/catalog/pg_proc.h    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 658,664 ****
>
>   DATA(insert OID = 330 (  btgettuple           PGNSP PGUID 12 1 0 f f t f v 2 16 "2281 2281" _null_ _null_ _null_
btgettuple- _null_ )); 
>   DESCR("btree(internal)");
> ! DATA(insert OID = 636 (  btgetmulti           PGNSP PGUID 12 1 0 f f t f v 4 16 "2281 2281 2281 2281" _null_ _null_
_null_ btgetmulti - _null_ )); 
>   DESCR("btree(internal)");
>   DATA(insert OID = 331 (  btinsert           PGNSP PGUID 12 1 0 f f t f v 6 16 "2281 2281 2281 2281 2281 2281"
_null__null_ _null_    btinsert - _null_ )); 
>   DESCR("btree(internal)");
> --- 658,664 ----
>
>   DATA(insert OID = 330 (  btgettuple           PGNSP PGUID 12 1 0 f f t f v 2 16 "2281 2281" _null_ _null_ _null_
btgettuple- _null_ )); 
>   DESCR("btree(internal)");
> ! DATA(insert OID = 636 (  btgetbitmap       PGNSP PGUID 12 1 0 f f t f v 4 16 "2281 2281 2281 2281" _null_ _null_
_null_ btgetbitmap - _null_ )); 
>   DESCR("btree(internal)");
>   DATA(insert OID = 331 (  btinsert           PGNSP PGUID 12 1 0 f f t f v 6 16 "2281 2281 2281 2281 2281 2281"
_null__null_ _null_    btinsert - _null_ )); 
>   DESCR("btree(internal)");
> ***************
> *** 777,783 ****
>
>   DATA(insert OID = 440 (  hashgettuple       PGNSP PGUID 12 1 0 f f t f v 2 16 "2281 2281" _null_ _null_ _null_
hashgettuple- _null_ )); 
>   DESCR("hash(internal)");
> ! DATA(insert OID = 637 (  hashgetmulti       PGNSP PGUID 12 1 0 f f t f v 4 16 "2281 2281 2281 2281" _null_ _null_
_null_ hashgetmulti - _null_ )); 
>   DESCR("hash(internal)");
>   DATA(insert OID = 441 (  hashinsert           PGNSP PGUID 12 1 0 f f t f v 6 16 "2281 2281 2281 2281 2281 2281"
_null__null_ _null_    hashinsert - _null_ )); 
>   DESCR("hash(internal)");
> --- 777,783 ----
>
>   DATA(insert OID = 440 (  hashgettuple       PGNSP PGUID 12 1 0 f f t f v 2 16 "2281 2281" _null_ _null_ _null_
hashgettuple- _null_ )); 
>   DESCR("hash(internal)");
> ! DATA(insert OID = 637 (  hashgetbitmap       PGNSP PGUID 12 1 0 f f t f v 4 16 "2281 2281 2281 2281" _null_ _null_
_null_ hashgetbitmap - _null_ )); 
>   DESCR("hash(internal)");
>   DATA(insert OID = 441 (  hashinsert           PGNSP PGUID 12 1 0 f f t f v 6 16 "2281 2281 2281 2281 2281 2281"
_null__null_ _null_    hashinsert - _null_ )); 
>   DESCR("hash(internal)");
> ***************
> *** 1045,1051 ****
>
>   DATA(insert OID = 774 (  gistgettuple       PGNSP PGUID 12 1 0 f f t f v 2 16 "2281 2281" _null_ _null_ _null_
gistgettuple- _null_ )); 
>   DESCR("gist(internal)");
> ! DATA(insert OID = 638 (  gistgetmulti       PGNSP PGUID 12 1 0 f f t f v 4 16 "2281 2281 2281 2281" _null_ _null_
_null_ gistgetmulti - _null_ )); 
>   DESCR("gist(internal)");
>   DATA(insert OID = 775 (  gistinsert           PGNSP PGUID 12 1 0 f f t f v 6 16 "2281 2281 2281 2281 2281 2281"
_null__null_ _null_    gistinsert - _null_ )); 
>   DESCR("gist(internal)");
> --- 1045,1051 ----
>
>   DATA(insert OID = 774 (  gistgettuple       PGNSP PGUID 12 1 0 f f t f v 2 16 "2281 2281" _null_ _null_ _null_
gistgettuple- _null_ )); 
>   DESCR("gist(internal)");
> ! DATA(insert OID = 638 (  gistgetbitmap       PGNSP PGUID 12 1 0 f f t f v 4 16 "2281 2281 2281 2281" _null_ _null_
_null_ gistgetbitmap - _null_ )); 
>   DESCR("gist(internal)");
>   DATA(insert OID = 775 (  gistinsert           PGNSP PGUID 12 1 0 f f t f v 6 16 "2281 2281 2281 2281 2281 2281"
_null__null_ _null_    gistinsert - _null_ )); 
>   DESCR("gist(internal)");
> ***************
> *** 3941,3947 ****
>   /* GIN */
>   DATA(insert OID = 2730 (  gingettuple       PGNSP PGUID 12 1 0 f f t f v 2 16 "2281 2281" _null_ _null_ _null_
gingettuple- _null_ )); 
>   DESCR("gin(internal)");
> ! DATA(insert OID = 2731 (  gingetmulti       PGNSP PGUID 12 1 0 f f t f v 4 16 "2281 2281 2281 2281" _null_ _null_
_null_ gingetmulti - _null_ )); 
>   DESCR("gin(internal)");
>   DATA(insert OID = 2732 (  gininsert           PGNSP PGUID 12 1 0 f f t f v 6 16 "2281 2281 2281 2281 2281 2281"
_null__null_ _null_    gininsert - _null_ )); 
>   DESCR("gin(internal)");
> --- 3941,3947 ----
>   /* GIN */
>   DATA(insert OID = 2730 (  gingettuple       PGNSP PGUID 12 1 0 f f t f v 2 16 "2281 2281" _null_ _null_ _null_
gingettuple- _null_ )); 
>   DESCR("gin(internal)");
> ! DATA(insert OID = 2731 (  gingetbitmap       PGNSP PGUID 12 1 0 f f t f v 4 16 "2281 2281 2281 2281" _null_ _null_
_null_ gingetbitmap - _null_ )); 
>   DESCR("gin(internal)");
>   DATA(insert OID = 2732 (  gininsert           PGNSP PGUID 12 1 0 f f t f v 6 16 "2281 2281 2281 2281 2281 2281"
_null__null_ _null_    gininsert - _null_ )); 
>   DESCR("gin(internal)");
> diff -c -N -r ./src/include/nodes/tidbitmap.h ../pgsql.new/src/include/nodes/tidbitmap.h
> *** ./src/include/nodes/tidbitmap.h    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/include/nodes/tidbitmap.h    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 36,41 ****
> --- 36,42 ----
>   {
>       BlockNumber blockno;        /* page number containing tuples */
>       int            ntuples;        /* -1 indicates lossy result */
> +     bool        iscandidates;    /* do the results need to be rechecked */
>       OffsetNumber offsets[1];    /* VARIABLE LENGTH ARRAY */
>   } TBMIterateResult;                /* VARIABLE LENGTH STRUCT */
>
> ***************
> *** 44,50 ****
>   extern TIDBitmap *tbm_create(long maxbytes);
>   extern void tbm_free(TIDBitmap *tbm);
>
> ! extern void tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids);
>
>   extern void tbm_union(TIDBitmap *a, const TIDBitmap *b);
>   extern void tbm_intersect(TIDBitmap *a, const TIDBitmap *b);
> --- 45,51 ----
>   extern TIDBitmap *tbm_create(long maxbytes);
>   extern void tbm_free(TIDBitmap *tbm);
>
> ! extern void tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids, bool candidates);
>
>   extern void tbm_union(TIDBitmap *a, const TIDBitmap *b);
>   extern void tbm_intersect(TIDBitmap *a, const TIDBitmap *b);
> diff -c -N -r ./src/include/utils/rel.h ../pgsql.new/src/include/utils/rel.h
> *** ./src/include/utils/rel.h    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/include/utils/rel.h    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 98,104 ****
>       FmgrInfo    aminsert;
>       FmgrInfo    ambeginscan;
>       FmgrInfo    amgettuple;
> !     FmgrInfo    amgetmulti;
>       FmgrInfo    amrescan;
>       FmgrInfo    amendscan;
>       FmgrInfo    ammarkpos;
> --- 98,104 ----
>       FmgrInfo    aminsert;
>       FmgrInfo    ambeginscan;
>       FmgrInfo    amgettuple;
> !     FmgrInfo    amgetbitmap;
>       FmgrInfo    amrescan;
>       FmgrInfo    amendscan;
>       FmgrInfo    ammarkpos;
> diff -c -N -r ./src/test/regress/expected/bitmapops.out ../pgsql.new/src/test/regress/expected/bitmapops.out
> *** ./src/test/regress/expected/bitmapops.out    1970-01-01 03:00:00.000000000 +0300
> --- ../pgsql.new/src/test/regress/expected/bitmapops.out    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 0 ****
> --- 1,38 ----
> + -- Test bitmap AND and OR
> + -- Generate enough data that we can test the lossy bitmaps.
> + -- There's 55 tuples per page in the table. 53 is just
> + -- below 55, so that an index scan with qual a = constant
> + -- will return at least one hit per page. 59 is just above
> + -- 55, so that an index scan with qual b = constant will return
> + -- hits on most but not all pages. 53 and 59 are prime, so that
> + -- there's a maximum number of a,b combinations in the table.
> + -- That allows us to test all the different combinations of
> + -- lossy and non-lossy pages with the minimum amount of data
> + CREATE TABLE bmscantest (a int, b int, t text);
> + INSERT INTO bmscantest
> +   SELECT (r%53), (r%59),
'foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo'
> +   FROM generate_series(1,70000) r;
> + CREATE INDEX i_bmtest_a ON bmscantest(a);
> + CREATE INDEX i_bmtest_b ON bmscantest(b);
> + -- We want to use bitmapscans. With default settings, the planner currently
> + -- chooses a bitmap scan for the queries below anyway, but let's make sure.
> + set enable_indexscan=false;
> + set enable_seqscan=false;
> + -- Lower work_mem to trigger use of lossy bitmaps
> + set work_mem = 64;
> + -- Test bitmap-and.
> + SELECT count(*) FROM bmscantest WHERE a = 1 AND b = 1;
> +  count
> + -------
> +     23
> + (1 row)
> +
> + -- Test bitmap-or.
> + SELECT count(*) FROM bmscantest WHERE a = 1 OR b = 1;
> +  count
> + -------
> +   2485
> + (1 row)
> +
> + -- clean up
> + DROP TABLE bmscantest;
> diff -c -N -r ./src/test/regress/expected/create_index.out ../pgsql.new/src/test/regress/expected/create_index.out
> *** ./src/test/regress/expected/create_index.out    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/test/regress/expected/create_index.out    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 327,332 ****
> --- 327,420 ----
>       96 | {23,97,43} | {AAAAAAAAAA646,A87088}
>   (1 row)
>
> + -- Repeat some of the above tests but make sure we exercise bitmapscans
> + SET enable_indexscan = OFF;
> + SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
> +  seqno |                i                |                                                                 t
                                                          
> +
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
> +      6 | {39,35,5,94,17,92,60,32}        | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
> +     74 | {32}                            |
{AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
> +     77 | {97,15,32,17,55,59,18,37,50,39} |
{AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
> +     89 | {40,32,17,6,30,88}              |
{AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
> +     98 | {38,34,32,89}                   |
{AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
> +    100 | {85,32,57,39,49,84,32,3,30}     |
{AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
> + (6 rows)
> +
> + SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
> +  seqno |                i                |                                                                 t
                                                          
> +
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
> +      6 | {39,35,5,94,17,92,60,32}        | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
> +     74 | {32}                            |
{AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
> +     77 | {97,15,32,17,55,59,18,37,50,39} |
{AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
> +     89 | {40,32,17,6,30,88}              |
{AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
> +     98 | {38,34,32,89}                   |
{AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
> +    100 | {85,32,57,39,49,84,32,3,30}     |
{AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
> + (6 rows)
> +
> + SELECT * FROM array_index_op_test WHERE i @> '{17}' ORDER BY seqno;
> +  seqno |                i                |                                                                 t
                                                          
> +
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
> +      6 | {39,35,5,94,17,92,60,32}        | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
> +     12 | {17,99,18,52,91,72,0,43,96,23}  | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
> +     15 | {17,14,16,63,67}                | {AA6416,AAAAAAAAAA646,AAAAA95309}
> +     19 | {52,82,17,74,23,46,69,51,75}    |
{AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
> +     53 | {38,17}                         | {AAAAAAAAAAA21658}
> +     65 | {61,5,76,59,17}                 | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
> +     77 | {97,15,32,17,55,59,18,37,50,39} |
{AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
> +     89 | {40,32,17,6,30,88}              |
{AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
> + (8 rows)
> +
> + SELECT * FROM array_index_op_test WHERE i && '{17}' ORDER BY seqno;
> +  seqno |                i                |                                                                 t
                                                          
> +
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
> +      6 | {39,35,5,94,17,92,60,32}        | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
> +     12 | {17,99,18,52,91,72,0,43,96,23}  | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
> +     15 | {17,14,16,63,67}                | {AA6416,AAAAAAAAAA646,AAAAA95309}
> +     19 | {52,82,17,74,23,46,69,51,75}    |
{AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
> +     53 | {38,17}                         | {AAAAAAAAAAA21658}
> +     65 | {61,5,76,59,17}                 | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
> +     77 | {97,15,32,17,55,59,18,37,50,39} |
{AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
> +     89 | {40,32,17,6,30,88}              |
{AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
> + (8 rows)
> +
> + SELECT * FROM array_index_op_test WHERE i @> '{32,17}' ORDER BY seqno;
> +  seqno |                i                |                                                                 t
                                                          
> +
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
> +      6 | {39,35,5,94,17,92,60,32}        | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
> +     77 | {97,15,32,17,55,59,18,37,50,39} |
{AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
> +     89 | {40,32,17,6,30,88}              |
{AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
> + (3 rows)
> +
> + SELECT * FROM array_index_op_test WHERE i && '{32,17}' ORDER BY seqno;
> +  seqno |                i                |                                                                 t
                                                          
> +
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
> +      6 | {39,35,5,94,17,92,60,32}        | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
> +     12 | {17,99,18,52,91,72,0,43,96,23}  | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
> +     15 | {17,14,16,63,67}                | {AA6416,AAAAAAAAAA646,AAAAA95309}
> +     19 | {52,82,17,74,23,46,69,51,75}    |
{AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
> +     53 | {38,17}                         | {AAAAAAAAAAA21658}
> +     65 | {61,5,76,59,17}                 | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
> +     74 | {32}                            |
{AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
> +     77 | {97,15,32,17,55,59,18,37,50,39} |
{AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
> +     89 | {40,32,17,6,30,88}              |
{AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
> +     98 | {38,34,32,89}                   |
{AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
> +    100 | {85,32,57,39,49,84,32,3,30}     |
{AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
> + (11 rows)
> +
> + SELECT * FROM array_index_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno;
> +  seqno |       i       |                                                             t
                                
> +
-------+---------------+----------------------------------------------------------------------------------------------------------------------------
> +     40 | {34}          |
{AAAAAAAAAAAAAA10611,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAA31334,AAAAA70466,AAAAAAAA81587,AAAAAAA74623}
> +     74 | {32}          |
{AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
> +     98 | {38,34,32,89} |
{AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
> + (3 rows)
> +
> + SELECT * FROM array_index_op_test WHERE i = '{47,77}' ORDER BY seqno;
> +  seqno |    i    |                                                        t
               
> +
-------+---------+-----------------------------------------------------------------------------------------------------------------
> +     95 | {47,77} |
{AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA74076,AAAAAAAAAA18107,AAAAA40681,AAAAAAAAAAAAAAA35875,AAAAA60038,AAAAAAA56483}
> + (1 row)
> +
>   RESET enable_seqscan;
>   RESET enable_indexscan;
>   RESET enable_bitmapscan;
> diff -c -N -r ./src/test/regress/expected/oidjoins.out ../pgsql.new/src/test/regress/expected/oidjoins.out
> *** ./src/test/regress/expected/oidjoins.out    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/test/regress/expected/oidjoins.out    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 65,76 ****
>   ------+------------
>   (0 rows)
>
> ! SELECT    ctid, amgetmulti
>   FROM    pg_catalog.pg_am fk
> ! WHERE    amgetmulti != 0 AND
> !     NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgetmulti);
> !  ctid | amgetmulti
> ! ------+------------
>   (0 rows)
>
>   SELECT    ctid, amrescan
> --- 65,76 ----
>   ------+------------
>   (0 rows)
>
> ! SELECT    ctid, amgetbitmap
>   FROM    pg_catalog.pg_am fk
> ! WHERE    amgetbitmap != 0 AND
> !     NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgetbitmap);
> !  ctid | amgetbitmap
> ! ------+-------------
>   (0 rows)
>
>   SELECT    ctid, amrescan
> diff -c -N -r ./src/test/regress/parallel_schedule ../pgsql.new/src/test/regress/parallel_schedule
> *** ./src/test/regress/parallel_schedule    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/test/regress/parallel_schedule    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 61,67 ****
>   # ----------
>   # The fourth group of parallel test
>   # ----------
> ! test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join
aggregatestransactions random portals arrays btree_index hash_index update namespace prepared_xacts delete 
>
>   test: privileges
>   test: misc
> --- 61,67 ----
>   # ----------
>   # The fourth group of parallel test
>   # ----------
> ! test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join
aggregatesbitmapops transactions random portals arrays btree_index hash_index update namespace prepared_xacts delete 
>
>   test: privileges
>   test: misc
> diff -c -N -r ./src/test/regress/serial_schedule ../pgsql.new/src/test/regress/serial_schedule
> *** ./src/test/regress/serial_schedule    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/test/regress/serial_schedule    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 69,74 ****
> --- 69,75 ----
>   test: case
>   test: join
>   test: aggregates
> + test: bitmapops
>   test: transactions
>   ignore: random
>   test: random
> diff -c -N -r ./src/test/regress/sql/bitmapops.sql ../pgsql.new/src/test/regress/sql/bitmapops.sql
> *** ./src/test/regress/sql/bitmapops.sql    1970-01-01 03:00:00.000000000 +0300
> --- ../pgsql.new/src/test/regress/sql/bitmapops.sql    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 0 ****
> --- 1,41 ----
> + -- Test bitmap AND and OR
> +
> +
> + -- Generate enough data that we can test the lossy bitmaps.
> +
> + -- There's 55 tuples per page in the table. 53 is just
> + -- below 55, so that an index scan with qual a = constant
> + -- will return at least one hit per page. 59 is just above
> + -- 55, so that an index scan with qual b = constant will return
> + -- hits on most but not all pages. 53 and 59 are prime, so that
> + -- there's a maximum number of a,b combinations in the table.
> + -- That allows us to test all the different combinations of
> + -- lossy and non-lossy pages with the minimum amount of data
> +
> + CREATE TABLE bmscantest (a int, b int, t text);
> +
> + INSERT INTO bmscantest
> +   SELECT (r%53), (r%59),
'foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo'
> +   FROM generate_series(1,70000) r;
> +
> + CREATE INDEX i_bmtest_a ON bmscantest(a);
> + CREATE INDEX i_bmtest_b ON bmscantest(b);
> +
> + -- We want to use bitmapscans. With default settings, the planner currently
> + -- chooses a bitmap scan for the queries below anyway, but let's make sure.
> + set enable_indexscan=false;
> + set enable_seqscan=false;
> +
> + -- Lower work_mem to trigger use of lossy bitmaps
> + set work_mem = 64;
> +
> +
> + -- Test bitmap-and.
> + SELECT count(*) FROM bmscantest WHERE a = 1 AND b = 1;
> +
> + -- Test bitmap-or.
> + SELECT count(*) FROM bmscantest WHERE a = 1 OR b = 1;
> +
> +
> + -- clean up
> + DROP TABLE bmscantest;
> diff -c -N -r ./src/test/regress/sql/create_index.sql ../pgsql.new/src/test/regress/sql/create_index.sql
> *** ./src/test/regress/sql/create_index.sql    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/test/regress/sql/create_index.sql    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 167,172 ****
> --- 167,183 ----
>   SELECT * FROM array_index_op_test WHERE t <@
'{AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}'ORDER BY
seqno;
>   SELECT * FROM array_index_op_test WHERE t = '{AAAAAAAAAA646,A87088}' ORDER BY seqno;
>
> + -- Repeat some of the above tests but make sure we exercise bitmapscans
> + SET enable_indexscan = OFF;
> +
> + SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
> + SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
> + SELECT * FROM array_index_op_test WHERE i @> '{17}' ORDER BY seqno;
> + SELECT * FROM array_index_op_test WHERE i && '{17}' ORDER BY seqno;
> + SELECT * FROM array_index_op_test WHERE i @> '{32,17}' ORDER BY seqno;
> + SELECT * FROM array_index_op_test WHERE i && '{32,17}' ORDER BY seqno;
> + SELECT * FROM array_index_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno;
> + SELECT * FROM array_index_op_test WHERE i = '{47,77}' ORDER BY seqno;
>
>   RESET enable_seqscan;
>   RESET enable_indexscan;
> diff -c -N -r ./src/test/regress/sql/oidjoins.sql ../pgsql.new/src/test/regress/sql/oidjoins.sql
> *** ./src/test/regress/sql/oidjoins.sql    2007-06-19 14:37:49.000000000 +0300
> --- ../pgsql.new/src/test/regress/sql/oidjoins.sql    2007-06-19 14:41:46.000000000 +0300
> ***************
> *** 33,42 ****
>   FROM    pg_catalog.pg_am fk
>   WHERE    amgettuple != 0 AND
>       NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgettuple);
> ! SELECT    ctid, amgetmulti
>   FROM    pg_catalog.pg_am fk
> ! WHERE    amgetmulti != 0 AND
> !     NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgetmulti);
>   SELECT    ctid, amrescan
>   FROM    pg_catalog.pg_am fk
>   WHERE    amrescan != 0 AND
> --- 33,42 ----
>   FROM    pg_catalog.pg_am fk
>   WHERE    amgettuple != 0 AND
>       NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgettuple);
> ! SELECT    ctid, amgetbitmap
>   FROM    pg_catalog.pg_am fk
> ! WHERE    amgetbitmap != 0 AND
> !     NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgetbitmap);
>   SELECT    ctid, amrescan
>   FROM    pg_catalog.pg_am fk
>   WHERE    amrescan != 0 AND
>
>
> ------------------------------------------------------------------------
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 9: In versions below 8.0, the planner will ignore your desire to
>        choose an index scan if your joining column's datatypes do not
>        match


--

       === The PostgreSQL Company: Command Prompt, Inc. ===
Sales/Support: +1.503.667.4564 || 24x7/Emergency: +1.800.492.2240
Providing the most comprehensive  PostgreSQL solutions since 1997
              http://www.commandprompt.com/

Donate to the PostgreSQL Project: http://www.postgresql.org/about/donate
PostgreSQL Replication: http://www.commandprompt.com/products/


pgsql-patches by date:

Previous
From: Tom Lane
Date:
Subject: Re: Transaction Guarantee, updated version
Next
From: "Simon Riggs"
Date:
Subject: Re: Transaction Guarantee, updated version