Replace "amgetmulti" AM functions with "amgetbitmap", in which the whole
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 10 Apr 2008 22:25:26 +0000 (22:25 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 10 Apr 2008 22:25:26 +0000 (22:25 +0000)
indexscan always occurs in one call, and the results are returned in a
TIDBitmap instead of a limited-size array of TIDs.  This should improve
speed a little by reducing AM entry/exit overhead, and it is necessary
infrastructure if we are ever to support bitmap indexes.

In an only slightly related change, add support for TIDBitmaps to preserve
(somewhat lossily) the knowledge that particular TIDs reported by an index
need to have their quals rechecked when the heap is visited.  This facility
is not really used yet; we'll need to extend the forced-recheck feature to
plain indexscans before it's useful, and that hasn't been coded yet.
The intent is to use it to clean up 8.3's horrid @@@ kluge for text search
with weighted queries.  There might be other uses in future, but that one
alone is sufficient reason.

Heikki Linnakangas, with some adjustments by me.

30 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/indexam.sgml
src/backend/access/gin/ginget.c
src/backend/access/gist/gistget.c
src/backend/access/hash/hash.c
src/backend/access/index/genam.c
src/backend/access/index/indexam.c
src/backend/access/nbtree/nbtree.c
src/backend/executor/nodeBitmapHeapscan.c
src/backend/executor/nodeBitmapIndexscan.c
src/backend/nodes/tidbitmap.c
src/include/access/genam.h
src/include/access/gin.h
src/include/access/gist_private.h
src/include/access/hash.h
src/include/access/nbtree.h
src/include/access/relscan.h
src/include/catalog/catversion.h
src/include/catalog/pg_am.h
src/include/catalog/pg_proc.h
src/include/nodes/tidbitmap.h
src/include/utils/rel.h
src/test/regress/expected/bitmapops.out [new file with mode: 0644]
src/test/regress/expected/create_index.out
src/test/regress/expected/oidjoins.out
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/bitmapops.sql [new file with mode: 0644]
src/test/regress/sql/create_index.sql
src/test/regress/sql/oidjoins.sql

index c0a018a5091f8839c9de68612eaa95eba4f5b523..5fbb2c61855e100e614d253ccde69e56cc19d23d 100644 (file)
      </row>
 
      <row>
-      <entry><structfield>amgetmulti</structfield></entry>
+      <entry><structfield>amgetbitmap</structfield></entry>
       <entry><type>regproc</type></entry>
       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
-      <entry><quote>Fetch multiple tuples</quote> function</entry>
+      <entry><quote>Fetch all valid tuples</quote> function</entry>
      </row>
 
      <row>
index 0e52c0eb9186113c6c560e25acfe8bfed826f8b3..772ed119bec87b1bae42e7dacb07524bdbc27883 100644 (file)
@@ -320,23 +320,16 @@ amgettuple (IndexScanDesc scan,
 
   <para>
 <programlisting>
-boolean
-amgetmulti (IndexScanDesc scan,
-            ItemPointer tids,
-            int32 max_tids,
-            int32 *returned_tids);
+int64
+amgetbitmap (IndexScanDesc scan,
+             TIDBitmap *tbm);
 </programlisting>
-   Fetch multiple tuples in the given scan.  Returns TRUE if the scan should
-   continue, FALSE if no matching tuples remain.  <literal>tids</> points to
-   a caller-supplied array of <literal>max_tids</>
-   <structname>ItemPointerData</> records, which the call fills with TIDs of
-   matching tuples.  <literal>*returned_tids</> is set to the number of TIDs
-   actually returned.  This can be less than <literal>max_tids</>, or even
-   zero, even when the return value is TRUE.  (This provision allows the
-   access method to choose the most efficient stopping points in its scan,
-   for example index page boundaries.)  <function>amgetmulti</> and
+   Fetch all tuples in the given scan and add them to the caller-supplied
+   TIDBitmap (that is, OR the set of tuple IDs into whatever set is already
+   in the bitmap).  The number of tuples fetched is returned. 
+   <function>amgetbitmap</> and
    <function>amgettuple</> cannot be used in the same index scan; there
-   are other restrictions too when using <function>amgetmulti</>, as explained
+   are other restrictions too when using <function>amgetbitmap</>, as explained
    in <xref linkend="index-scanning">.
   </para>
 
@@ -491,20 +484,17 @@ amrestrpos (IndexScanDesc scan);
 
   <para>
    Instead of using <function>amgettuple</>, an index scan can be done with 
-   <function>amgetmulti</> to fetch multiple tuples per call.  This can be
+   <function>amgetbitmap</> to fetch all tuples in one call.  This can be
    noticeably more efficient than <function>amgettuple</> because it allows
    avoiding lock/unlock cycles within the access method.  In principle
-   <function>amgetmulti</> should have the same effects as repeated
+   <function>amgetbitmap</> should have the same effects as repeated
    <function>amgettuple</> calls, but we impose several restrictions to
-   simplify matters.  In the first place, <function>amgetmulti</> does not
-   take a <literal>direction</> argument, and therefore it does not support
-   backwards scan nor intrascan reversal of direction.  The access method
-   need not support marking or restoring scan positions during an
-   <function>amgetmulti</> scan, either.  (These restrictions cost little
-   since it would be difficult to use these features in an
-   <function>amgetmulti</> scan anyway: adjusting the caller's buffered
-   list of TIDs would be complex.)  Finally, <function>amgetmulti</> does
-   not guarantee any locking of the returned tuples, with implications
+   simplify matters.  First of all, <function>amgetbitmap</> returns all 
+   tuples at once and marking or restoring scan positions isn't 
+   supported. Secondly, the tuples are returned in a bitmap which doesn't
+   have any specific ordering, which is why <function>amgetbitmap</> doesn't
+   take a <literal>direction</> argument.  Finally, <function>amgetbitmap</>
+   does not guarantee any locking of the returned tuples, with implications
    spelled out in <xref linkend="index-locking">.
   </para>
 
@@ -605,9 +595,8 @@ amrestrpos (IndexScanDesc scan);
   </para>
 
   <para>
-   In an <function>amgetmulti</> index scan, the access method need not
-   guarantee to keep an index pin on any of the returned tuples.  (It would be
-   impractical to pin more than the last one anyway.)  Therefore
+   In an <function>amgetbitmap</> index scan, the access method does not
+   keep an index pin on any of the returned tuples.  Therefore
    it is only safe to use such scans with MVCC-compliant snapshots.
   </para>
 
index d2927043509a11172efeab3a22ed79070423e240..e2025f73b4264374a23d170376c767d1279376b2 100644 (file)
  */
 
 #include "postgres.h"
+
 #include "access/gin.h"
 #include "catalog/index.h"
+#include "miscadmin.h"
 #include "utils/memutils.h"
 
 static bool
@@ -476,34 +478,37 @@ scanGetItem(IndexScanDesc scan, ItemPointerData *item)
 #define GinIsVoidRes(s)                ( ((GinScanOpaque) scan->opaque)->isVoidRes == true )
 
 Datum
-gingetmulti(PG_FUNCTION_ARGS)
+gingetbitmap(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);
+       TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
+       int64           ntids;
 
        if (GinIsNewKey(scan))
                newScanKey(scan);
 
-       *returned_tids = 0;
-
        if (GinIsVoidRes(scan))
-               PG_RETURN_BOOL(false);
+               PG_RETURN_INT64(0);
 
        startScan(scan);
 
-       do
+       ntids = 0;
+       for (;;)
        {
-               if (scanGetItem(scan, tids + *returned_tids))
-                       (*returned_tids)++;
-               else
+               ItemPointerData iptr;
+
+               CHECK_FOR_INTERRUPTS();
+
+               if (!scanGetItem(scan, &iptr))
                        break;
-       } while (*returned_tids < max_tids);
+
+               tbm_add_tuples(tbm, &iptr, 1, false);
+               ntids++;
+       }
 
        stopScan(scan);
 
-       PG_RETURN_BOOL(*returned_tids == max_tids);
+       PG_RETURN_INT64(ntids);
 }
 
 Datum
index c9f4118a932f254619556caf9bad842798484ced..5e973da886da43be885870341db79008d07fc782 100644 (file)
 
 #include "access/gist_private.h"
 #include "executor/execdebug.h"
+#include "miscadmin.h"
 #include "pgstat.h"
 #include "utils/memutils.h"
 
 
 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 int64 gistnext(IndexScanDesc scan, ScanDirection dir,
+                                         ItemPointer tid, TIDBitmap *tbm,
+                                         bool ignore_killed_tuples);
 static bool gistindex_keytest(IndexTuple tuple, IndexScanDesc scan,
                                  OffsetNumber offset);
 
@@ -114,32 +117,37 @@ gistgettuple(PG_FUNCTION_ARGS)
         * 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;
+       res = (gistnext(scan, dir, &tid, NULL, scan->ignore_killed_tuples) > 0) ? true : false;
 
        PG_RETURN_BOOL(res);
 }
 
 Datum
-gistgetmulti(PG_FUNCTION_ARGS)
+gistgetbitmap(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);
+       TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
+       int64      ntids;
 
-       *returned_tids = gistnext(scan, ForwardScanDirection, tids, max_tids, false);
+       ntids = gistnext(scan, ForwardScanDirection, NULL, tbm, false);
 
-       PG_RETURN_BOOL(*returned_tids == max_tids);
+       PG_RETURN_INT64(ntids);
 }
 
 /*
- * 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.
+ * Fetch tuple(s) that match 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. In both cases, the function
+ * result is the number of returned tuples.
  */
-static int
-gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
-                int maxtids, bool ignore_killed_tuples)
+static int64
+gistnext(IndexScanDesc scan, ScanDirection dir,
+                ItemPointer tid, TIDBitmap *tbm,
+                bool ignore_killed_tuples)
 {
        Page            p;
        OffsetNumber n;
@@ -148,7 +156,7 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
        IndexTuple      it;
        GISTPageOpaque opaque;
        bool            resetoffset = false;
-       int                     ntids = 0;
+       int64           ntids = 0;
 
        so = (GISTScanOpaque) scan->opaque;
 
@@ -174,6 +182,8 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
 
        for (;;)
        {
+               CHECK_FOR_INTERRUPTS();
+
                /* First of all, we need lock buffer */
                Assert(so->curbuf != InvalidBuffer);
                LockBuffer(so->curbuf, GIST_SHARE);
@@ -285,20 +295,21 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
                                 * return success. Note that we keep "curbuf" pinned so that
                                 * we can efficiently resume the index scan later.
                                 */
-
                                ItemPointerSet(&(so->curpos),
                                                           BufferGetBlockNumber(so->curbuf), n);
 
                                if (!(ignore_killed_tuples && ItemIdIsDead(PageGetItemId(p, n))))
                                {
                                        it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
-                                       tids[ntids] = scan->xs_ctup.t_self = it->t_tid;
                                        ntids++;
-
-                                       if (ntids == maxtids)
+                                       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;
+                                               return ntids; /* always 1 */
                                        }
                                }
                        }
@@ -308,7 +319,6 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
                                 * We've found an entry in an internal node whose key is
                                 * consistent with the search key, so push it to stack
                                 */
-
                                stk = (GISTSearchStack *) palloc(sizeof(GISTSearchStack));
 
                                it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
@@ -318,7 +328,6 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
 
                                stk->next = so->stack->next;
                                so->stack->next = stk;
-
                        }
 
                        if (ScanDirectionIsBackward(dir))
index 05f04ffa0ec25e61c61973cdaf3356dd5b7f6bdd..1ea0ce49af6721e01ba26fd755bc6ba5dc141138 100644 (file)
@@ -22,6 +22,7 @@
 #include "access/hash.h"
 #include "catalog/index.h"
 #include "commands/vacuum.h"
+#include "miscadmin.h"
 #include "optimizer/cost.h"
 #include "optimizer/plancat.h"
 
@@ -275,72 +276,51 @@ hashgettuple(PG_FUNCTION_ARGS)
 
 
 /*
- *     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.
+ *     hashgetbitmap() -- get all tuples at once
  */
 Datum
-hashgetmulti(PG_FUNCTION_ARGS)
+hashgetbitmap(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);
+       TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
        HashScanOpaque so = (HashScanOpaque) scan->opaque;
-       Relation        rel = scan->indexRelation;
-       bool            res = true;
-       int32           ntids = 0;
+       bool            res;
+       int64           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);
+       res = _hash_first(scan, ForwardScanDirection);
 
-       while (ntids < max_tids)
+       while (res)
        {
-               /*
-                * Start scan, or advance to next tuple.
-                */
-               if (ItemPointerIsValid(&(so->hashso_curpos)))
-                       res = _hash_next(scan, ForwardScanDirection);
-               else
-                       res = _hash_first(scan, ForwardScanDirection);
+               bool    add_tuple;
+
+               CHECK_FOR_INTERRUPTS();
 
                /*
                 * 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 (!ItemIdIsDead(PageGetItemId(page, offnum)))
-                                       break;
-                               res = _hash_next(scan, ForwardScanDirection);
-                       }
+                       Page            page;
+                       OffsetNumber offnum;
+
+                       offnum = ItemPointerGetOffsetNumber(&(so->hashso_curpos));
+                       page = BufferGetPage(so->hashso_curbuf);
+                       add_tuple = !ItemIdIsDead(PageGetItemId(page, offnum));
                }
+               else
+                       add_tuple = true;
 
-               if (!res)
-                       break;
                /* Save tuple ID, and continue scanning */
-               tids[ntids] = scan->xs_ctup.t_self;
-               ntids++;
-       }
+               if (add_tuple) 
+               {
+                       tbm_add_tuples(tbm, &scan->xs_ctup.t_self, 1, false);
+                       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);
+               res = _hash_next(scan, ForwardScanDirection);
+       }
 
-       *returned_tids = ntids;
-       PG_RETURN_BOOL(res);
+       PG_RETURN_INT64(ntids);
 }
 
 
index c9410642ce7fc587b77ed23be9839dcf26f7f534..3cccd1e783b91f6b63c98bd47a922bca78aaf2fb 100644 (file)
@@ -88,7 +88,6 @@ RelationGetIndexScan(Relation indexRelation,
        else
                scan->keyData = NULL;
 
-       scan->is_multiscan = false; /* caller may change this */
        scan->kill_prior_tuple = false;
        scan->ignore_killed_tuples = true;      /* default setting */
 
index 0929bb1ff341d750a4f6818cac359901f71b800b..754f8eaabbb46928820f3a1f3f82c91be89d0a6a 100644 (file)
  *             index_open              - open an index relation by relation OID
  *             index_close             - close an index relation
  *             index_beginscan - start a scan of an index with amgettuple
- *             index_beginscan_multi - start a scan of an index with amgetmulti
+ *             index_beginscan_bitmap - start a scan of an index with amgetbitmap
  *             index_rescan    - restart a scan of an index
  *             index_endscan   - end a scan
  *             index_insert    - insert an index tuple into a relation
  *             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_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
@@ -227,7 +227,6 @@ index_beginscan(Relation heapRelation,
         * Save additional parameters into the scandesc.  Everything else was set
         * up by RelationGetIndexScan.
         */
-       scan->is_multiscan = false;
        scan->heapRelation = heapRelation;
        scan->xs_snapshot = snapshot;
 
@@ -235,15 +234,15 @@ index_beginscan(Relation heapRelation,
 }
 
 /*
- * index_beginscan_multi - start a scan of an index with amgetmulti
+ * index_beginscan_bitmap - start a scan of an index with amgetbitmap
  *
  * As above, caller had better be holding some lock on the parent heap
  * relation, even though it's not explicitly mentioned here.
  */
 IndexScanDesc
-index_beginscan_multi(Relation indexRelation,
-                                         Snapshot snapshot,
-                                         int nkeys, ScanKey key)
+index_beginscan_bitmap(Relation indexRelation,
+                                          Snapshot snapshot,
+                                          int nkeys, ScanKey key)
 {
        IndexScanDesc scan;
 
@@ -253,7 +252,6 @@ index_beginscan_multi(Relation indexRelation,
         * Save additional parameters into the scandesc.  Everything else was set
         * up by RelationGetIndexScan.
         */
-       scan->is_multiscan = true;
        scan->xs_snapshot = snapshot;
 
        return scan;
@@ -676,44 +674,39 @@ index_getnext_indexitem(IndexScanDesc scan,
 }
 
 /* ----------------
- *             index_getmulti - get multiple tuples from an index scan
+ *             index_getbitmap - get all tuples at once from an index scan
  *
- * Collects the TIDs of multiple heap tuples satisfying the scan keys.
+ * 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.
  *
- * 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.
+ * Returns the number of matching tuples found.
  * ----------------
  */
-bool
-index_getmulti(IndexScanDesc scan,
-                          ItemPointer tids, int32 max_tids,
-                          int32 *returned_tids)
+int64
+index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
 {
        FmgrInfo   *procedure;
-       bool            found;
+       int64           ntids;
 
        SCAN_CHECKS;
-       GET_SCAN_PROCEDURE(amgetmulti);
+       GET_SCAN_PROCEDURE(amgetbitmap);
 
        /* just make sure this is false... */
        scan->kill_prior_tuple = false;
 
        /*
-        * have the am's getmulti proc do all the work.
+        * have the am's getbitmap proc do all the work.
         */
-       found = DatumGetBool(FunctionCall4(procedure,
-                                                                          PointerGetDatum(scan),
-                                                                          PointerGetDatum(tids),
-                                                                          Int32GetDatum(max_tids),
-                                                                          PointerGetDatum(returned_tids)));
+       ntids = DatumGetInt64(FunctionCall2(procedure,
+                                                                               PointerGetDatum(scan),
+                                                                               PointerGetDatum(bitmap)));
 
-       pgstat_count_index_tuples(scan->indexRelation, *returned_tids);
+       pgstat_count_index_tuples(scan->indexRelation, ntids);
 
-       return found;
+       return ntids;
 }
 
 /* ----------------
index 5fd2b0f771e8244c1c4d349fb25b50b940941572..17a6185c91cb54b1430df13cab798d181ed79247 100644 (file)
@@ -22,6 +22,7 @@
 #include "access/nbtree.h"
 #include "catalog/index.h"
 #include "commands/vacuum.h"
+#include "miscadmin.h"
 #include "storage/freespace.h"
 #include "storage/lmgr.h"
 #include "utils/memutils.h"
@@ -278,42 +279,29 @@ btgettuple(PG_FUNCTION_ARGS)
 }
 
 /*
- * 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.
+ * btgetbitmap() -- gets all matching tuples, and adds them to a bitmap
  */
 Datum
-btgetmulti(PG_FUNCTION_ARGS)
+btgetbitmap(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);
+       TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
        BTScanOpaque so = (BTScanOpaque) scan->opaque;
-       bool            res = true;
-       int32           ntids = 0;
-
-       if (max_tids <= 0)                      /* behave correctly in boundary case */
-               PG_RETURN_BOOL(true);
+       int64           ntids = 0;
+       ItemPointer heapTid;
 
-       /* If we haven't started the scan yet, fetch the first page & tuple. */
-       if (!BTScanPosIsValid(so->currPos))
+       /* Fetch the first page & tuple. */
+       if (!_bt_first(scan, ForwardScanDirection))
        {
-               res = _bt_first(scan, ForwardScanDirection);
-               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++;
+               /* empty scan */
+               PG_RETURN_INT64(0);
        }
+       /* Save tuple ID, and continue scanning */
+       heapTid = &scan->xs_ctup.t_self;
+       tbm_add_tuples(tbm, heapTid, 1, false);
+       ntids++;
 
-       while (ntids < max_tids)
+       for (;;)
        {
                /*
                 * Advance to next tuple within page.  This is the same as the easy
@@ -321,19 +309,20 @@ btgetmulti(PG_FUNCTION_ARGS)
                 */
                if (++so->currPos.itemIndex > so->currPos.lastItem)
                {
+                       CHECK_FOR_INTERRUPTS();
+
                        /* let _bt_next do the heavy lifting */
-                       res = _bt_next(scan, ForwardScanDirection);
-                       if (!res)
+                       if (!_bt_next(scan, ForwardScanDirection))
                                break;
                }
 
                /* Save tuple ID, and continue scanning */
-               tids[ntids] = so->currPos.items[so->currPos.itemIndex].heapTid;
+               heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
+               tbm_add_tuples(tbm, heapTid, 1, false);
                ntids++;
        }
 
-       *returned_tids = ntids;
-       PG_RETURN_BOOL(res);
+       PG_RETURN_INT64(ntids);
 }
 
 /*
index 672dd65bc2e5c83bd258f7348bea04302e3b26c8..4fe71adb71d63596bf1039a514b43146d77067e1 100644 (file)
@@ -206,7 +206,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
                 * If we are using lossy info, we have to recheck the qual conditions
                 * at every tuple.
                 */
-               if (tbmres->ntuples < 0)
+               if (tbmres->recheck)
                {
                        econtext->ecxt_scantuple = slot;
                        ResetExprContext(econtext);
index 88842ab319ac42b456f8765940acaabf8a854469..1297fedd2a3936e43b5dabbd95344ac79704b59b 100644 (file)
 Node *
 MultiExecBitmapIndexScan(BitmapIndexScanState *node)
 {
-#define MAX_TIDS       1024
        TIDBitmap  *tbm;
        IndexScanDesc scandesc;
-       ItemPointerData tids[MAX_TIDS];
-       int32           ntids;
        double          nTuples = 0;
        bool            doscan;
 
@@ -91,23 +88,14 @@ MultiExecBitmapIndexScan(BitmapIndexScanState *node)
         */
        while (doscan)
        {
-               bool            more = index_getmulti(scandesc, tids, MAX_TIDS, &ntids);
-
-               if (ntids > 0)
-               {
-                       tbm_add_tuples(tbm, tids, ntids);
-                       nTuples += ntids;
-               }
+               nTuples += (double) index_getbitmap(scandesc, tbm);
 
                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);
-               }
+               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 */
@@ -321,10 +309,10 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
         * Initialize scan descriptor.
         */
        indexstate->biss_ScanDesc =
-               index_beginscan_multi(indexstate->biss_RelationDesc,
-                                                         estate->es_snapshot,
-                                                         indexstate->biss_NumScanKeys,
-                                                         indexstate->biss_ScanKeys);
+               index_beginscan_bitmap(indexstate->biss_RelationDesc,
+                                                          estate->es_snapshot,
+                                                          indexstate->biss_NumScanKeys,
+                                                          indexstate->biss_ScanKeys);
 
        /*
         * all done.
index a1ac57f10bf39871d121fc7a997794e83a9cec9f..ffc882f008476c2cdb1f8934fd72f757632798e5 100644 (file)
  * of lossiness.  In theory we could fall back to page ranges at some
  * point, but for now that seems useless complexity.
  *
+ * We also support the notion of candidate matches, or rechecking.  This
+ * means we know that a search need visit only some tuples on a page,
+ * but we are not certain that all of those tuples are real matches.
+ * So the eventual heap scan must recheck the quals for these tuples only,
+ * rather than rechecking the quals for all tuples on the page as in the
+ * lossy-bitmap case.  Rechecking can be specified when TIDs are inserted
+ * into a bitmap, and it can also happen internally when we AND a lossy
+ * and a non-lossy page.
+ *
  *
  * Copyright (c) 2003-2008, PostgreSQL Global Development Group
  *
  * have exact storage for the first page of a chunk if we are using
  * lossy storage for any page in the chunk's range, since the same
  * hashtable entry has to serve both purposes.
+ *
+ * recheck is used only on exact pages --- it indicates that although
+ * only the stated tuples need be checked, the full index qual condition
+ * must be checked for each (ie, these are candidate matches).
  */
 typedef struct PagetableEntry
 {
        BlockNumber blockno;            /* page number (hashtable key) */
        bool            ischunk;                /* T = lossy storage, F = exact */
+       bool            recheck;                /* should the tuples be rechecked? */
        bitmapword      words[Max(WORDS_PER_PAGE, WORDS_PER_CHUNK)];
 } PagetableEntry;
 
@@ -244,9 +258,13 @@ tbm_free(TIDBitmap *tbm)
 
 /*
  * tbm_add_tuples - add some tuple IDs to a TIDBitmap
+ *
+ * If recheck is true, then the recheck flag will be set in the
+ * TBMIterateResult when any of these tuples are reported out.
  */
 void
-tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids)
+tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids,
+                          bool recheck)
 {
        int                     i;
 
@@ -280,6 +298,7 @@ tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids)
                        bitnum = BITNUM(off - 1);
                }
                page->words[wordnum] |= ((bitmapword) 1 << bitnum);
+               page->recheck |= recheck;
 
                if (tbm->nentries > tbm->maxentries)
                        tbm_lossify(tbm);
@@ -360,6 +379,7 @@ tbm_union_page(TIDBitmap *a, const PagetableEntry *bpage)
                        /* Both pages are exact, merge at the bit level */
                        for (wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++)
                                apage->words[wordnum] |= bpage->words[wordnum];
+                       apage->recheck |= bpage->recheck;
                }
        }
 
@@ -471,22 +491,12 @@ tbm_intersect_page(TIDBitmap *a, PagetableEntry *apage, const TIDBitmap *b)
        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.
+                * 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 requiring rechecks, to indicate that
+                * at most those tuples set in 'a' are matches.
                 */
+               apage->recheck = true;
                return false;
        }
        else
@@ -504,7 +514,9 @@ tbm_intersect_page(TIDBitmap *a, PagetableEntry *apage, const TIDBitmap *b)
                                if (apage->words[wordnum] != 0)
                                        candelete = false;
                        }
+                       apage->recheck |= bpage->recheck;
                }
+               /* If there is no matching b page, we can just delete the a page */
                return candelete;
        }
 }
@@ -585,7 +597,9 @@ tbm_begin_iterate(TIDBitmap *tbm)
  * order.  If result->ntuples < 0, then the bitmap is "lossy" and failed to
  * remember the exact tuples to look at on this page --- the caller must
  * examine all tuples on the page and check if they meet the intended
- * condition.
+ * condition.  If result->recheck is true, only the indicated tuples need
+ * be examined, but the condition must be rechecked anyway.  (For ease of
+ * testing, recheck is always set true when ntuples < 0.)
  */
 TBMIterateResult *
 tbm_iterate(TIDBitmap *tbm)
@@ -638,6 +652,7 @@ tbm_iterate(TIDBitmap *tbm)
                        /* Return a lossy page indicator from the chunk */
                        output->blockno = chunk_blockno;
                        output->ntuples = -1;
+                       output->recheck = true;
                        tbm->schunkbit++;
                        return output;
                }
@@ -676,6 +691,7 @@ tbm_iterate(TIDBitmap *tbm)
                }
                output->blockno = page->blockno;
                output->ntuples = ntuples;
+               output->recheck = page->recheck;
                tbm->spageptr++;
                return output;
        }
index 40e11a26fbb598674bba0a167f737566af1b898e..bbf4ab5809446a4cff8cbd82bb918b3dcb9872e3 100644 (file)
@@ -17,6 +17,7 @@
 #include "access/relscan.h"
 #include "access/sdir.h"
 #include "nodes/primnodes.h"
+#include "nodes/tidbitmap.h"
 #include "storage/lock.h"
 
 /*
@@ -99,7 +100,7 @@ extern IndexScanDesc index_beginscan(Relation heapRelation,
                                Relation indexRelation,
                                Snapshot snapshot,
                                int nkeys, ScanKey key);
-extern IndexScanDesc index_beginscan_multi(Relation indexRelation,
+extern IndexScanDesc index_beginscan_bitmap(Relation indexRelation,
                                          Snapshot snapshot,
                                          int nkeys, ScanKey key);
 extern void index_rescan(IndexScanDesc scan, ScanKey key);
@@ -109,9 +110,7 @@ extern void index_restrpos(IndexScanDesc scan);
 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 int64 index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap);
 
 extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info,
                                  IndexBulkDeleteResult *stats,
index 7684ec614e69751ca08a09c495b976d18a6740e3..20126bfe7c9f1adedbc96bcc21c314159b19277d 100644 (file)
@@ -422,7 +422,7 @@ extern PGDLLIMPORT int GinFuzzySearchLimit;
 #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 gingetbitmap(PG_FUNCTION_ARGS);
 extern Datum gingettuple(PG_FUNCTION_ARGS);
 
 /* ginvacuum.c */
index 4051546f49c0281373380ee629a7e9ca1e467aeb..1f83b54109cf42744955a19a816bd6cd3df97b67 100644 (file)
@@ -269,7 +269,7 @@ extern XLogRecPtr gistxlogInsertCompletion(RelFileNode node, ItemPointerData *ke
 
 /* gistget.c */
 extern Datum gistgettuple(PG_FUNCTION_ARGS);
-extern Datum gistgetmulti(PG_FUNCTION_ARGS);
+extern Datum gistgetbitmap(PG_FUNCTION_ARGS);
 
 /* gistutil.c */
 
index d41f7d0272b500557c5dea2212265fb7fabe8518..c1169cba30ad314695854ba30d6711170abab010 100644 (file)
@@ -233,7 +233,7 @@ extern Datum hashbuild(PG_FUNCTION_ARGS);
 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 hashgetbitmap(PG_FUNCTION_ARGS);
 extern Datum hashrescan(PG_FUNCTION_ARGS);
 extern Datum hashendscan(PG_FUNCTION_ARGS);
 extern Datum hashmarkpos(PG_FUNCTION_ARGS);
index 26d254f36a1fb77f8f7675935393c729685776d5..fb07512ea3fc94b2086a4ce98210f3628efec6f6 100644 (file)
@@ -507,7 +507,7 @@ extern Datum btbuild(PG_FUNCTION_ARGS);
 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 btgetbitmap(PG_FUNCTION_ARGS);
 extern Datum btrescan(PG_FUNCTION_ARGS);
 extern Datum btendscan(PG_FUNCTION_ARGS);
 extern Datum btmarkpos(PG_FUNCTION_ARGS);
index fba6dac60446a44d94b5bb28850949044a201a77..b516adf21f031c3f426cd80628a6d7148ea10904 100644 (file)
@@ -57,7 +57,7 @@ typedef HeapScanDescData *HeapScanDesc;
 
 /*
  * We use the same IndexScanDescData structure for both amgettuple-based
- * and amgetmulti-based index scans.  Some fields are only relevant in
+ * and amgetbitmap-based index scans.  Some fields are only relevant in
  * amgettuple-based scans.
  */
 typedef struct IndexScanDescData
@@ -68,7 +68,6 @@ typedef struct IndexScanDescData
        Snapshot        xs_snapshot;    /* snapshot to see */
        int                     numberOfKeys;   /* number of scan keys */
        ScanKey         keyData;                /* array of scan key descriptors */
-       bool            is_multiscan;   /* TRUE = using amgetmulti */
 
        /* signaling to index AM about killing index tuples */
        bool            kill_prior_tuple;               /* last-returned tuple is dead */
index 798ec85e1cf9767a8f7fb57a9104c693ac503dd6..5f44d0e85d91ca956b43d26de98df9c38f863174 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200804051
+#define CATALOG_VERSION_NO     200804101
 
 #endif
index affc937f1deacd5a53b6550004c449023ab3a989..7bf25ee3cee5b894bc72d02987d15820d99b663b 100644 (file)
@@ -51,7 +51,7 @@ CATALOG(pg_am,2601)
        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         amgetbitmap;    /* "fetch all valid tuples" function */
        regproc         amrescan;               /* "restart this scan" function */
        regproc         amendscan;              /* "end this scan" function */
        regproc         ammarkpos;              /* "mark current scan position" function */
@@ -89,7 +89,7 @@ typedef FormData_pg_am *Form_pg_am;
 #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_amgetbitmap                 15
 #define Anum_pg_am_amrescan                            16
 #define Anum_pg_am_amendscan                   17
 #define Anum_pg_am_ammarkpos                   18
@@ -105,16 +105,16 @@ typedef FormData_pg_am *Form_pg_am;
  * ----------------
  */
 
-DATA(insert OID = 403 (  btree 5 1 t t t t t t f t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions ));
+DATA(insert OID = 403 (  btree 5 1 t t t t t t f t btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos 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 hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
+DATA(insert OID = 405 (  hash  1 1 f f f f f f f f hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos 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 gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
+DATA(insert OID = 783 (  gist  0 7 f f t t t t t t gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos 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 ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
+DATA(insert OID = 2742 (  gin  0 4 f f f f f f t f gininsert ginbeginscan gingettuple gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
 DESCR("GIN index access method");
 #define GIN_AM_OID 2742
 
index 622b05ce32dc8d294ea28e43a2438170f589e813..3a08c2cd4343d44a72925d1a283c54c246248fdf 100644 (file)
@@ -655,7 +655,7 @@ DESCR("convert float4 to int4");
 
 DATA(insert OID = 330 (  btgettuple               PGNSP PGUID 12 1 0 f f t f v 2 16 "2281 2281" _null_ _null_ _null_  btgettuple - _null_ _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_ _null_ ));
+DATA(insert OID = 636 (  btgetbitmap      PGNSP PGUID 12 1 0 f f t f v 2 20 "2281 2281" _null_ _null_ _null_  btgetbitmap - _null_ _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_ _null_ ));
 DESCR("btree(internal)");
@@ -774,7 +774,7 @@ DESCR("convert char(n) to name");
 
 DATA(insert OID = 440 (  hashgettuple     PGNSP PGUID 12 1 0 f f t f v 2 16 "2281 2281" _null_ _null_ _null_  hashgettuple - _null_ _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_ _null_ ));
+DATA(insert OID = 637 (  hashgetbitmap    PGNSP PGUID 12 1 0 f f t f v 2 20 "2281 2281" _null_ _null_ _null_  hashgetbitmap - _null_ _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_ _null_ ));
 DESCR("hash(internal)");
@@ -1040,7 +1040,7 @@ DESCR("smaller of two");
 
 DATA(insert OID = 774 (  gistgettuple     PGNSP PGUID 12 1 0 f f t f v 2 16 "2281 2281" _null_ _null_ _null_  gistgettuple - _null_ _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_ _null_ ));
+DATA(insert OID = 638 (  gistgetbitmap    PGNSP PGUID 12 1 0 f f t f v 2 20 "2281 2281" _null_ _null_ _null_  gistgetbitmap - _null_ _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_ _null_ ));
 DESCR("gist(internal)");
@@ -3967,7 +3967,7 @@ DESCR("GiST support");
 /* 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_ _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_ _null_ ));
+DATA(insert OID = 2731 (  gingetbitmap    PGNSP PGUID 12 1 0 f f t f v 2 20 "2281 2281" _null_ _null_ _null_  gingetbitmap - _null_ _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_ _null_ ));
 DESCR("gin(internal)");
index 467cfdbfa3cd46e46ae62fcf51fc04554d23b15d..56d6a0d8bcdf71c4d527d222d3bb55300e933cf4 100644 (file)
@@ -36,6 +36,8 @@ typedef struct
 {
        BlockNumber blockno;            /* page number containing tuples */
        int                     ntuples;                /* -1 indicates lossy result */
+       bool            recheck;                /* should the tuples be rechecked? */
+       /* Note: recheck is always true if ntuples < 0 */
        OffsetNumber offsets[1];        /* VARIABLE LENGTH ARRAY */
 } TBMIterateResult;                            /* VARIABLE LENGTH STRUCT */
 
@@ -44,7 +46,9 @@ typedef struct
 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_add_tuples(TIDBitmap *tbm,
+                                                  const ItemPointer tids, int ntids,
+                                                  bool recheck);
 
 extern void tbm_union(TIDBitmap *a, const TIDBitmap *b);
 extern void tbm_intersect(TIDBitmap *a, const TIDBitmap *b);
index e0e7e054f728c7248b125ad0f1f3152c6bdc818a..607ac654d2725667034916174572970b0fb2b8b5 100644 (file)
@@ -100,7 +100,7 @@ typedef struct RelationAmInfo
        FmgrInfo        aminsert;
        FmgrInfo        ambeginscan;
        FmgrInfo        amgettuple;
-       FmgrInfo        amgetmulti;
+       FmgrInfo        amgetbitmap;
        FmgrInfo        amrescan;
        FmgrInfo        amendscan;
        FmgrInfo        ammarkpos;
diff --git a/src/test/regress/expected/bitmapops.out b/src/test/regress/expected/bitmapops.out
new file mode 100644 (file)
index 0000000..d88a76f
--- /dev/null
@@ -0,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;
index b56078edf235cbbb01f009663430afcb00cefd1d..52ba630a560d9050574694d129890c4146231088 100644 (file)
@@ -174,7 +174,7 @@ RESET enable_bitmapscan;
 --
 SET enable_seqscan = OFF;
 SET enable_indexscan = ON;
-SET enable_bitmapscan = ON;
+SET enable_bitmapscan = OFF;
 CREATE INDEX intarrayidx ON array_index_op_test USING gin (i);
 SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
  seqno |                i                |                                                                 t                                                                  
@@ -327,6 +327,95 @@ SELECT * FROM array_index_op_test WHERE t = '{AAAAAAAAAA646,A87088}' ORDER BY se
     96 | {23,97,43} | {AAAAAAAAAA646,A87088}
 (1 row)
 
+-- Repeat some of the above tests but exercising bitmapscans instead
+SET enable_indexscan = OFF;
+SET enable_bitmapscan = ON;
+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;
index bd55dc24ae3fcaca207913c80259e19d4ed0f00f..c57721958ff58425c03d53aaec7c93d73fcc75f7 100644 (file)
@@ -65,12 +65,12 @@ WHERE       amgettuple != 0 AND
 ------+------------
 (0 rows)
 
-SELECT ctid, amgetmulti 
+SELECT ctid, amgetbitmap 
 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 
-------+------------
+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 
index 0aba92cddc62ca118958049889a4989eceb4a117..389058f6643e17befb272cacb9fca8d9eb9fb732 100644 (file)
@@ -77,7 +77,7 @@ test: misc
 # ----------
 # Another group of parallel tests
 # ----------
-test: select_views portals_p2 rules foreign_key cluster dependency guc combocid tsearch tsdicts
+test: select_views portals_p2 rules foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts
 
 # ----------
 # Another group of parallel tests
index 15f179a0c19e089f737fae3b5b87fdc5e6e084cf..1263073fd0fc4ab92a05e6a6f7e6874d3de979fe 100644 (file)
@@ -92,6 +92,7 @@ test: foreign_key
 test: cluster
 test: dependency
 test: guc
+test: bitmapops
 test: combocid
 test: tsearch
 test: plancache
diff --git a/src/test/regress/sql/bitmapops.sql b/src/test/regress/sql/bitmapops.sql
new file mode 100644 (file)
index 0000000..0b5477e
--- /dev/null
@@ -0,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;
index 14f2f281ff8456aa84d4e1e1232f379e101f2e1f..447c5df37acfaa5ec8a747933df70c98f6de24a9 100644 (file)
@@ -143,7 +143,7 @@ RESET enable_bitmapscan;
 
 SET enable_seqscan = OFF;
 SET enable_indexscan = ON;
-SET enable_bitmapscan = ON;
+SET enable_bitmapscan = OFF;
 
 CREATE INDEX intarrayidx ON array_index_op_test USING gin (i);
 
@@ -167,6 +167,18 @@ SELECT * FROM array_index_op_test WHERE t && '{AAAAAAAA72908,AAAAAAAAAA646}' ORD
 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 exercising bitmapscans instead
+SET enable_indexscan = OFF;
+SET enable_bitmapscan = ON;
+
+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;
index 773c2afe7faf69992ba27c36934237ec46653864..7a072d4fd3dcd13efc22e09b1b8cea39af69fd0a 100644 (file)
@@ -33,10 +33,10 @@ SELECT      ctid, amgettuple
 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 
+SELECT ctid, amgetbitmap 
 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);
+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