Phantom Command IDs - Mailing list pgsql-patches

From Heikki Linnakangas
Subject Phantom Command IDs
Date
Msg-id 451BA5D0.8080509@enterprisedb.com
Whole thread Raw
List pgsql-patches
Here's a patch that implements the Phantom Command ID idea that has been
discussed.

I didn't do anything about the system columns. We need to before
applying the patch, because the HeapTupleHeaderGetCmin/max functions
don't work properly if called outside the inserting/deleting
transaction. In fact, SELECT cmin,cmax FROM foo will cause assertion
failures if there's rows with phantom cids in the table.

I used the last free bit in t_infomask. At first I thought it wouldn't
be necessary, because you could detect that a row has a phantom command
id if both xmin and xmax are part of the current top-level transaction
(TransactionIdIsCurrentTransactionId). But that doesn't work because we
don't consider aborted subtransactions as current. Using the infomask
bit seems more robust anyway.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com

Index: src/backend/access/heap/heapam.c
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/backend/access/heap/heapam.c,v
retrieving revision 1.219
diff -c -r1.219 heapam.c
*** src/backend/access/heap/heapam.c    18 Aug 2006 16:09:08 -0000    1.219
--- src/backend/access/heap/heapam.c    26 Sep 2006 11:44:22 -0000
***************
*** 1405,1412 ****
      tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
      HeapTupleHeaderSetXmin(tup->t_data, xid);
      HeapTupleHeaderSetCmin(tup->t_data, cid);
!     HeapTupleHeaderSetXmax(tup->t_data, 0);        /* zero out Datum fields */
!     HeapTupleHeaderSetCmax(tup->t_data, 0);        /* for cleanliness */
      tup->t_tableOid = RelationGetRelid(relation);

      /*
--- 1405,1411 ----
      tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
      HeapTupleHeaderSetXmin(tup->t_data, xid);
      HeapTupleHeaderSetCmin(tup->t_data, cid);
!     HeapTupleHeaderSetXmax(tup->t_data, 0);
      tup->t_tableOid = RelationGetRelid(relation);

      /*
***************
*** 2045,2052 ****
      newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
      HeapTupleHeaderSetXmin(newtup->t_data, xid);
      HeapTupleHeaderSetCmin(newtup->t_data, cid);
!     HeapTupleHeaderSetXmax(newtup->t_data, 0);    /* zero out Datum fields */
!     HeapTupleHeaderSetCmax(newtup->t_data, 0);    /* for cleanliness */

      /*
       * If the toaster needs to be activated, OR if the new tuple will not fit
--- 2044,2050 ----
      newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
      HeapTupleHeaderSetXmin(newtup->t_data, xid);
      HeapTupleHeaderSetCmin(newtup->t_data, cid);
!     HeapTupleHeaderSetXmax(newtup->t_data, 0);

      /*
       * If the toaster needs to be activated, OR if the new tuple will not fit
Index: src/backend/access/transam/xact.c
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/backend/access/transam/xact.c,v
retrieving revision 1.226
diff -c -r1.226 xact.c
*** src/backend/access/transam/xact.c    27 Aug 2006 19:11:46 -0000    1.226
--- src/backend/access/transam/xact.c    27 Sep 2006 11:03:25 -0000
***************
*** 43,48 ****
--- 43,49 ----
  #include "utils/memutils.h"
  #include "utils/relcache.h"
  #include "utils/guc.h"
+ #include "utils/phantomcid.h"


  /*
***************
*** 1595,1600 ****
--- 1596,1602 ----
      AtEOXact_Namespace(true);
      /* smgrcommit already done */
      AtEOXact_Files();
+     AtEOXact_PhantomCid();
      pgstat_count_xact_commit();

      CurrentResourceOwner = NULL;
***************
*** 1810,1815 ****
--- 1812,1818 ----
      AtEOXact_Namespace(true);
      /* smgrcommit already done */
      AtEOXact_Files();
+     AtEOXact_PhantomCid();

      CurrentResourceOwner = NULL;
      ResourceOwnerDelete(TopTransactionResourceOwner);
***************
*** 1961,1966 ****
--- 1964,1970 ----
      AtEOXact_Namespace(false);
      smgrabort();
      AtEOXact_Files();
+     AtEOXact_PhantomCid();
      pgstat_count_xact_rollback();

      /*
Index: src/backend/utils/time/Makefile
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/backend/utils/time/Makefile,v
retrieving revision 1.10
diff -c -r1.10 Makefile
*** src/backend/utils/time/Makefile    29 Nov 2003 19:52:04 -0000    1.10
--- src/backend/utils/time/Makefile    20 Sep 2006 10:05:19 -0000
***************
*** 12,18 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global

! OBJS = tqual.o

  all: SUBSYS.o

--- 12,18 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global

! OBJS = tqual.o phantomcid.o

  all: SUBSYS.o

Index: src/backend/utils/time/phantomcid.c
===================================================================
RCS file: src/backend/utils/time/phantomcid.c
diff -N src/backend/utils/time/phantomcid.c
*** /dev/null    1 Jan 1970 00:00:00 -0000
--- src/backend/utils/time/phantomcid.c    28 Sep 2006 09:43:13 -0000
***************
*** 0 ****
--- 1,274 ----
+ /*-------------------------------------------------------------------------
+  *
+  * phantomcid.c
+  *      Phantom command id support routines
+  *
+  * Before version 8.3, HeapTupleHeaderData had separate fields for cmin
+  * and cmax. To reduce the header size, cmin and cmax are now overlayed
+  * in the same field in the header. That usually works because you rarely
+  * insert and delete a tuple in the transaction. To make it work when you
+  * do, we create a phantom command id and store that in the tuple header
+  * instead of cmin and cmax. The phantom command id maps to the real cmin
+  * and cmax in a backend-private array. Other backends don't need them,
+  * because cmin and cmax are only interesting to the inserting/deleting
+  * transaction.
+  *
+  * To allow reusing existing phantom cids, we also keep a hash table that
+  * maps cmin,cmax pairs to phantom cids.
+  *
+  * The array and hash table are kept in TopTransactionContext, and are
+  * destroyed at the end of transaction.
+  *
+  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *      $PostgreSQL$
+  *
+  *-------------------------------------------------------------------------
+  */
+
+ #include "postgres.h"
+
+ #include "access/htup.h"
+ #include "access/xact.h"
+ #include "utils/memutils.h"
+ #include "utils/hsearch.h"
+
+ #define PHANTOMCID_DEBUG
+
+
+ /* Hash table to lookup phantom cids by cmin and cmax */
+ static HTAB *phantomHash = NULL;
+
+ /* Key and entry structures for the hash table */
+ typedef struct {
+     CommandId cmin;
+     CommandId cmax;
+ } PhantomCidKeyData;
+
+ typedef struct {
+     PhantomCidKeyData key;
+     CommandId phantomcid;
+ } PhantomCidEntryData;
+
+ typedef PhantomCidKeyData *PhantomCidKey;
+ typedef PhantomCidEntryData *PhantomCidEntry;
+
+ #define PCID_HASH_SIZE                100
+
+
+ /* An array of cmin,cmax pairs, indexed by phantom command id.
+  * To convert a phantom cid to cmin and cmax, you do a simple array
+  * lookup. */
+ static PhantomCidKey phantomCids = NULL;
+ static int usedPhantomCids = 0; /* number of elements in phantomCids */
+ static int sizePhantomCids = 0; /* size of phantomCids array */
+
+ /* Initial size of the array. It will be grown if it fills up */
+ #define PCID_ARRAY_INITIAL_SIZE        100
+
+
+ /* prototypes for internal functions */
+ static CommandId GetPhantomCommandId(CommandId cmin, CommandId cmax);
+ static CommandId GetRealCmin(CommandId phantomcid);
+ static CommandId GetRealCmax(CommandId phantomcid);
+
+ /**** External API ****/
+
+ /* All these functions rely on the caller to not call functions that don't make
+  * sense. You should only call cmin related functions in the inserting
+  * transaction and cmax related functions in the deleting transaction.
+  */
+
+ CommandId
+ HeapTupleHeaderGetCmin(HeapTupleHeader tup)
+ {
+     CommandId cid = tup->t_choice.t_heap.t_field4.t_commandid;
+
+     Assert(!(tup->t_infomask & HEAP_MOVED));
+
+     if (tup->t_infomask & HEAP_PHANTOMCID)
+         return GetRealCmin(cid);
+     else
+         return cid;
+ }
+
+ void
+ HeapTupleHeaderSetCmin(HeapTupleHeader tup, CommandId cmin)
+ {
+     /* We never need to create a phantom cid for a new tuple. */
+     tup->t_choice.t_heap.t_field4.t_commandid = cmin;
+ }
+
+ CommandId
+ HeapTupleHeaderGetCmax(HeapTupleHeader tup)
+ {
+     CommandId cid = tup->t_choice.t_heap.t_field4.t_commandid;
+
+     Assert(!(tup->t_infomask & HEAP_MOVED));
+
+     if (tup->t_infomask & HEAP_PHANTOMCID)
+         return GetRealCmax(cid);
+     else
+         return cid;
+ }
+
+ void
+ HeapTupleHeaderSetCmax(HeapTupleHeader tup, CommandId cmax)
+ {
+     HeapTupleFields *t_heap = &tup->t_choice.t_heap;
+
+     if (!(tup->t_infomask & HEAP_XMIN_COMMITTED)
+         && TransactionIdIsCurrentTransactionId(t_heap->t_xmin))
+     {
+         /* This row was inserted by our transaction, and now we're
+          * deleting it. Need to use phantom cid. */
+         CommandId cmin = t_heap->t_field4.t_commandid;
+         t_heap->t_field4.t_commandid = GetPhantomCommandId(cmin, cmax);
+         tup->t_infomask |= HEAP_PHANTOMCID;
+     }
+     else {
+         t_heap->t_field4.t_commandid = cmax;
+         tup->t_infomask &= ~HEAP_PHANTOMCID;
+     }
+ }
+ /*
+  * Phantom command IDs are only interesting to the inserting and deleting
+  * transaction, so we can forget about them at the end of transaction.
+  */
+ void
+ AtEOXact_PhantomCid()
+ {
+     usedPhantomCids = 0;
+     sizePhantomCids = 0;
+     /* Don't bother to pfree. These are allocated in TopTransactionContext,
+      * so they're going to be freed at the end of transaction anyway.
+      */
+     phantomCids = NULL;
+     phantomHash = NULL;
+ }
+
+
+ /**** Internal routines ****/
+
+ /*
+  * Get a phantom command id that maps to cmin and cmax.
+  *
+  * We try to reuse old phantom command ids when possible.
+  */
+ static
+ CommandId GetPhantomCommandId(CommandId cmin, CommandId cmax)
+ {
+     CommandId phantomcid;
+     PhantomCidKeyData key;
+     PhantomCidEntry entry = NULL;
+     bool found;
+
+ #ifdef PHANTOMCID_DEBUG
+     elog(LOG, "GetPhantomCommandId cmin: %d cmax: %d", cmin, cmax);
+ #endif
+
+     /* Create the array and hash table the first time we need to use
+      * phantom cids in the transaction.
+      */
+     if(phantomCids == NULL)
+     {
+         HASHCTL hash_ctl;
+         MemoryContext oldcontext;
+
+         oldcontext = MemoryContextSwitchTo(TopTransactionContext);
+
+         phantomCids =
+             palloc(sizeof(PhantomCidKeyData) * PCID_ARRAY_INITIAL_SIZE);
+         sizePhantomCids = PCID_ARRAY_INITIAL_SIZE;
+
+         memset(&hash_ctl, 0, sizeof(hash_ctl));
+         hash_ctl.keysize = sizeof(PhantomCidKeyData);
+         hash_ctl.entrysize = sizeof(PhantomCidEntryData);
+         hash_ctl.hash = tag_hash;
+         hash_ctl.hcxt = TopTransactionContext;
+
+         phantomHash =
+             hash_create("Phantom command id hash table",
+                         PCID_HASH_SIZE, &hash_ctl,
+                         HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+
+         MemoryContextSwitchTo(TopTransactionContext);
+     }
+
+     /* Try to find an old phantom cid with the same cmin and cmax for reuse */
+
+     key.cmin = cmin;
+     key.cmax = cmax;
+     entry = (PhantomCidEntry)
+         hash_search(phantomHash, (void *) &key, HASH_ENTER, &found);
+
+     if (found)
+     {
+ #ifdef PHANTOMCID_DEBUG
+         elog(LOG, "result: %d (reused)", entry->phantomcid);
+ #endif
+         return entry->phantomcid;
+     }
+     else
+     {
+         /* We have to create a new phantom cid. Check that there's room
+          * for it in the array, and grow it if there isn't */
+         if (usedPhantomCids >= sizePhantomCids)
+         {
+             /* We need to grow the array */
+
+             MemoryContext oldcontext;
+             oldcontext = MemoryContextSwitchTo(TopTransactionContext);
+
+             /* XXX: Should we create a bigger hash table too? */
+             sizePhantomCids *= 2;
+             phantomCids =
+                 repalloc(phantomCids,
+                          sizeof(PhantomCidKeyData) * sizePhantomCids);
+
+             MemoryContextSwitchTo(oldcontext);
+         }
+
+         phantomcid = usedPhantomCids;
+         entry->phantomcid = phantomcid;
+
+         phantomCids[phantomcid].cmin = cmin;
+         phantomCids[phantomcid].cmax = cmax;
+
+         usedPhantomCids++;
+
+ #ifdef PHANTOMCID_DEBUG
+         elog(LOG, "result: %d", phantomcid);
+ #endif
+     }
+
+     return phantomcid;
+ }
+
+ static CommandId
+ GetRealCmin(CommandId phantomcid)
+ {
+     Assert(phantomcid < usedPhantomCids);
+
+ #ifdef PHANTOMCID_DEBUG
+     elog(LOG, "GetRealCmin phantomcid: %d -> %d", phantomcid, phantomCids[phantomcid].cmin);
+ #endif
+
+
+     return phantomCids[phantomcid].cmin;
+ }
+
+ static CommandId
+ GetRealCmax(CommandId phantomcid)
+ {
+     Assert(phantomcid < usedPhantomCids);
+
+ #ifdef PHANTOMCID_DEBUG
+     elog(LOG, "GetRealCmax phantomcid: %d -> %d", phantomcid, phantomCids[phantomcid].cmax);
+ #endif
+
+     return phantomCids[phantomcid].cmax;
+ }
+
Index: src/include/access/htup.h
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/include/access/htup.h,v
retrieving revision 1.85
diff -c -r1.85 htup.h
*** src/include/access/htup.h    13 Jul 2006 17:47:01 -0000    1.85
--- src/include/access/htup.h    28 Sep 2006 09:23:03 -0000
***************
*** 65,77 ****
   *            object ID (if HEAP_HASOID is set in t_infomask)
   *            user data fields
   *
!  * We store five "virtual" fields Xmin, Cmin, Xmax, Cmax, and Xvac in four
!  * physical fields.  Xmin, Cmin and Xmax are always really stored, but
!  * Cmax and Xvac share a field.  This works because we know that there are
!  * only a limited number of states that a tuple can be in, and that Cmax
!  * is only interesting for the lifetime of the deleting transaction.
!  * This assumes that VACUUM FULL never tries to move a tuple whose Cmax
!  * is still interesting (ie, delete-in-progress).
   *
   * Note that in 7.3 and 7.4 a similar idea was applied to Xmax and Cmin.
   * However, with the advent of subtransactions, a tuple may need both Xmax
--- 65,81 ----
   *            object ID (if HEAP_HASOID is set in t_infomask)
   *            user data fields
   *
!  * We store five "virtual" fields Xmin, Cmin, Xmax, Cmax, and Xvac in three
!  * physical fields.  Xmin and Xmax are always really stored, but Cmin, Cmax
!  * and Xvac share a field.  This works because we know that there are only
!  * a limited number of states that a tuple can be in, and that Cmax and Cmin
!  * are only interesting for the lifetime of the deleting or inserting
!  * transaction. If a tuple is inserted and deleted in the same transaction,
!  * we use a phantom command id that maps to the real cmin and cmax. The
!  * mapping is local to the backend. See phantomcid.c for more details.
!  *
!  * This assumes that VACUUM FULL never tries to move a tuple whose Cmax or
!  * Cmin is still interesting (ie, delete- or insert-in-progress).
   *
   * Note that in 7.3 and 7.4 a similar idea was applied to Xmax and Cmin.
   * However, with the advent of subtransactions, a tuple may need both Xmax
***************
*** 103,114 ****
  typedef struct HeapTupleFields
  {
      TransactionId t_xmin;        /* inserting xact ID */
-     CommandId    t_cmin;            /* inserting command ID */
      TransactionId t_xmax;        /* deleting or locking xact ID */

      union
      {
!         CommandId    t_cmax;        /* deleting or locking command ID */
          TransactionId t_xvac;    /* VACUUM FULL xact ID */
      }            t_field4;
  } HeapTupleFields;
--- 107,121 ----
  typedef struct HeapTupleFields
  {
      TransactionId t_xmin;        /* inserting xact ID */
      TransactionId t_xmax;        /* deleting or locking xact ID */

      union
      {
!         /* t_commandid is the inserting command ID (cmin), the deleting
!          * command ID (cmax), or a phantom cid that maps to cmin and cmax if
!          * the tuple was inserted and deleted in the same transaction
!          */
!         CommandId    t_commandid;
          TransactionId t_xvac;    /* VACUUM FULL xact ID */
      }            t_field4;
  } HeapTupleFields;
***************
*** 163,169 ****
  #define HEAP_HASCOMPRESSED        0x0008    /* has compressed stored attribute(s) */
  #define HEAP_HASEXTENDED        0x000C    /* the two above combined */
  #define HEAP_HASOID                0x0010    /* has an object-id field */
! /* 0x0020 is presently unused */
  #define HEAP_XMAX_EXCL_LOCK        0x0040    /* xmax is exclusive locker */
  #define HEAP_XMAX_SHARED_LOCK    0x0080    /* xmax is shared locker */
  /* if either LOCK bit is set, xmax hasn't deleted the tuple, only locked it */
--- 170,176 ----
  #define HEAP_HASCOMPRESSED        0x0008    /* has compressed stored attribute(s) */
  #define HEAP_HASEXTENDED        0x000C    /* the two above combined */
  #define HEAP_HASOID                0x0010    /* has an object-id field */
! #define HEAP_PHANTOMCID            0x0020    /* t_commandid is a phantom cid */
  #define HEAP_XMAX_EXCL_LOCK        0x0040    /* xmax is exclusive locker */
  #define HEAP_XMAX_SHARED_LOCK    0x0080    /* xmax is shared locker */
  /* if either LOCK bit is set, xmax hasn't deleted the tuple, only locked it */
***************
*** 210,243 ****
      TransactionIdStore((xid), &(tup)->t_choice.t_heap.t_xmax) \
  )

! #define HeapTupleHeaderGetCmin(tup) \
! ( \
!     (tup)->t_choice.t_heap.t_cmin \
! )
!
! #define HeapTupleHeaderSetCmin(tup, cid) \
! ( \
!     (tup)->t_choice.t_heap.t_cmin = (cid) \
! )
!
! /*
!  * Note: GetCmax will produce wrong answers after SetXvac has been executed
!  * by a transaction other than the inserting one.  We could check
!  * HEAP_XMAX_INVALID and return FirstCommandId if it's clear, but since that
!  * bit will be set again if the deleting transaction aborts, there'd be no
!  * real gain in safety from the extra test.  So, just rely on the caller not
!  * to trust the value unless it's meaningful.
   */
- #define HeapTupleHeaderGetCmax(tup) \
- ( \
-     (tup)->t_choice.t_heap.t_field4.t_cmax \
- )
-
- #define HeapTupleHeaderSetCmax(tup, cid) \
- do { \
-     Assert(!((tup)->t_infomask & HEAP_MOVED)); \
-     (tup)->t_choice.t_heap.t_field4.t_cmax = (cid); \
- } while (0)

  #define HeapTupleHeaderGetXvac(tup) \
  ( \
--- 217,225 ----
      TransactionIdStore((xid), &(tup)->t_choice.t_heap.t_xmax) \
  )

! /* HeapTupleHeaderGetCmin, HeapTupleHeaderGetCmax, and ..SetCmin and ..SetCmax
!  * are defined in phantomcid.h
   */

  #define HeapTupleHeaderGetXvac(tup) \
  ( \
***************
*** 613,616 ****
--- 595,605 ----

  #define SizeOfHeapInplace    (offsetof(xl_heap_inplace, target) + SizeOfHeapTid)

+ /* prototypes of HeapTupleHeader* functions implemented in
+  * utils/time/phantomcid.c */
+ extern CommandId HeapTupleHeaderGetCmin(HeapTupleHeader tup);
+ extern void HeapTupleHeaderSetCmin(HeapTupleHeader tup, CommandId cmin);
+ extern CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup);
+ extern void HeapTupleHeaderSetCmax(HeapTupleHeader tup, CommandId cmax);
+
  #endif   /* HTUP_H */
Index: src/include/utils/phantomcid.h
===================================================================
RCS file: src/include/utils/phantomcid.h
diff -N src/include/utils/phantomcid.h
*** /dev/null    1 Jan 1970 00:00:00 -0000
--- src/include/utils/phantomcid.h    28 Sep 2006 09:12:11 -0000
***************
*** 0 ****
--- 1,25 ----
+ /*-------------------------------------------------------------------------
+  *
+  * phantomcid.h
+  *      Phantom cid function definitions
+  *
+  *
+  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * $PostgreSQL$
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef PHANTOMCID_H
+ #define PHANTOMCID_H
+
+ /* HeapTupleHeaderGetCmin, *SetCmin, *GetCmax and *SetCmax
+  * function prototypes are in access/htup.h, because that's
+  * where the macro definitions that the functions replaced
+  * used to be.
+  */
+
+ extern void AtEOXact_PhantomCid(void);
+
+ #endif   /* PHANTOMCID_H */
Index: src/test/regress/parallel_schedule
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/test/regress/parallel_schedule,v
retrieving revision 1.35
diff -c -r1.35 parallel_schedule
*** src/test/regress/parallel_schedule    30 Aug 2006 23:34:22 -0000    1.35
--- src/test/regress/parallel_schedule    27 Sep 2006 12:53:04 -0000
***************
*** 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
aggregatestransactions random portals arrays btree_index hash_index update namespace prepared_xacts delete phantomcid 

  test: privileges
  test: misc
Index: src/test/regress/serial_schedule
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/test/regress/serial_schedule,v
retrieving revision 1.33
diff -c -r1.33 serial_schedule
*** src/test/regress/serial_schedule    30 Aug 2006 23:34:22 -0000    1.33
--- src/test/regress/serial_schedule    27 Sep 2006 12:52:34 -0000
***************
*** 104,106 ****
--- 104,107 ----
  test: returning
  test: stats
  test: tablespace
+ test: phantomcid
Index: src/test/regress/expected/phantomcid.out
===================================================================
RCS file: src/test/regress/expected/phantomcid.out
diff -N src/test/regress/expected/phantomcid.out
*** /dev/null    1 Jan 1970 00:00:00 -0000
--- src/test/regress/expected/phantomcid.out    28 Sep 2006 08:51:09 -0000
***************
*** 0 ****
--- 1,28 ----
+ CREATE TEMP TABLE phantomcidtest (foobar int);
+ BEGIN;
+ INSERT INTO phantomcidtest VALUES (1);
+ SELECT * FROM phantomcidtest;
+  foobar
+ --------
+       1
+ (1 row)
+
+ DELETE FROM phantomcidtest;
+ SELECT * FROM phantomcidtest;
+  foobar
+ --------
+ (0 rows)
+
+ COMMIT;
+ /* Test phantom cids with portals */
+ BEGIN;
+ INSERT INTO phantomcidtest VALUES (1);
+ DECLARE c CURSOR FOR SELECT * FROM phantomcidtest;
+ DELETE FROM phantomcidtest;
+ FETCH ALL FROM c;
+  foobar
+ --------
+       1
+ (1 row)
+
+ COMMIT;
Index: src/test/regress/sql/phantomcid.sql
===================================================================
RCS file: src/test/regress/sql/phantomcid.sql
diff -N src/test/regress/sql/phantomcid.sql
*** /dev/null    1 Jan 1970 00:00:00 -0000
--- src/test/regress/sql/phantomcid.sql    27 Sep 2006 12:54:18 -0000
***************
*** 0 ****
--- 1,27 ----
+ CREATE TEMP TABLE phantomcidtest (foobar int);
+
+
+ BEGIN;
+
+ INSERT INTO phantomcidtest VALUES (1);
+
+ SELECT * FROM phantomcidtest;
+
+ DELETE FROM phantomcidtest;
+
+ SELECT * FROM phantomcidtest;
+
+ COMMIT;
+
+ /* Test phantom cids with portals */
+ BEGIN;
+
+ INSERT INTO phantomcidtest VALUES (1);
+
+ DECLARE c CURSOR FOR SELECT * FROM phantomcidtest;
+
+ DELETE FROM phantomcidtest;
+
+ FETCH ALL FROM c;
+
+ COMMIT;

pgsql-patches by date:

Previous
From: "Claudio Natoli"
Date:
Subject: Re: Bad bug in fopen() wrapper code
Next
From: David Fetter
Date:
Subject: Numeric overflow problem + patch