← Back to team overview

zorba-coders team mailing list archive

[Merge] lp:~zorba-coders/zorba/bug-966706 into lp:zorba

 

Matthias Brantner has proposed merging lp:~zorba-coders/zorba/bug-966706 into lp:zorba.

Requested reviews:
  Matthias Brantner (matthias-brantner)
  Till Westmann (tillw)
  Markos Zaharioudakis (markos-za)
Related bugs:
  Bug #966706 in Zorba: "key uniqueness of value equality index not enforced"
  https://bugs.launchpad.net/zorba/+bug/966706

For more details, see:
https://code.launchpad.net/~zorba-coders/zorba/bug-966706/+merge/103835

fixed bug #966706 (key uniqueness of value equality index not enforced)
-- 
https://code.launchpad.net/~zorba-coders/zorba/bug-966706/+merge/103835
Your team Zorba Coders is subscribed to branch lp:zorba.
=== modified file 'ChangeLog'
--- ChangeLog	2012-04-25 17:16:48 +0000
+++ ChangeLog	2012-04-27 09:11:24 +0000
@@ -29,6 +29,7 @@
   * Fixed bug #967428 (do not hoist index creation outside a try-catch)
   * Fixed performance problem with the findNodeSources function of the no-copy rule
   * Fixed bug #872234 (prevent a rewritting to take place in case of sequential expr)
+  * Fixed bug #966706 (key uniqueness of index not enforced during incremental refresh)
   * Fixed bug #906494 (default compile with D_FILE_OFFSET_BITS=64)
   * Fixed bug #988412 (date:current-dateTime daylight saving)
   * Fixed bug #912586, #912593 and #912722 (assertion failures with lax validation)

=== modified file 'src/runtime/core/apply_updates.cpp'
--- src/runtime/core/apply_updates.cpp	2012-04-24 12:39:38 +0000
+++ src/runtime/core/apply_updates.cpp	2012-04-27 09:11:24 +0000
@@ -92,19 +92,24 @@
 
   store::Item_t item;
   ulong numItems = 0;
-  std::auto_ptr<store::PUL> pul;
+  store::PUL_t pul;
 
   ApplyIteratorState* state;
   DEFAULT_STACK_INIT(ApplyIteratorState, state, planState);
 
-  pul.reset(GENV_ITEMFACTORY->createPendingUpdateList());
-
   // Note: updating expr might not return a pul because of vacuous exprs
   while (consumeNext(item, theChild, planState))
   {
     if (item->isPul())
     {
-      pul->mergeUpdates(item);
+      if (pul)
+      {
+        pul->mergeUpdates(item);
+      }
+      else
+      {
+        pul.transfer(item);
+      }
     }
     else if (!theDiscardXDM)
     {
@@ -113,7 +118,8 @@
     }
   }
 
-  apply_updates(ccb, gdctx, theSctx, pul.get(), loc);
+  if(pul)
+    apply_updates(ccb, gdctx, theSctx, pul, loc);
 
   state->theXDMIte = state->theXDMItems.begin();
   state->theXDMEnd = state->theXDMItems.end();

=== modified file 'src/runtime/indexing/doc_indexer.cpp'
--- src/runtime/indexing/doc_indexer.cpp	2012-04-24 12:39:38 +0000
+++ src/runtime/indexing/doc_indexer.cpp	2012-04-27 09:11:24 +0000
@@ -84,7 +84,7 @@
                         QueryLoc::null,
                         tmp);
 
-  ulong numEntries = delta.size();
+  csize numEntries = delta.size();
   store::Item_t domainNode;
   store::IndexKey* key = NULL;
 
@@ -96,13 +96,11 @@
 
       //std::cout << domainNode.getp() << "  " << key << std::endl;
 
-      for (ulong i = 0; i < theNumColumns; ++i)
+      for (csize i = 0; i < theNumColumns; ++i)
       {
         if (!thePlanWrapper->next((*key)[i]))
-          throw ZORBA_EXCEPTION(
-            zerr::ZXQP0003_INTERNAL_ERROR,
-            ERROR_PARAMS( ZED( IncompleteKeyInIndexRefresh ) )
-          );
+          throw ZORBA_EXCEPTION(zerr::ZXQP0003_INTERNAL_ERROR,
+          ERROR_PARAMS(ZED(IncompleteKeyInIndexRefresh)));
       }
       
       delta.resize(numEntries + 1);

=== modified file 'src/store/naive/node_items.cpp'
--- src/store/naive/node_items.cpp	2012-04-24 12:39:38 +0000
+++ src/store/naive/node_items.cpp	2012-04-27 09:11:24 +0000
@@ -456,7 +456,7 @@
 #ifndef NDEBUG
 XmlNode::~XmlNode()
 {
-  NODE_TRACE1("Deleted " << store::StoreConsts::toString(getNodeKind()) << this);
+  STORE_TRACE1("Deleted " << store::StoreConsts::toString(getNodeKind()) << this);
 }
 #endif
 
@@ -1650,7 +1650,7 @@
   :
   InternalNode(store::StoreConsts::documentNode)
 {
-  NODE_TRACE1("Loaded doc node " << this);
+  STORE_TRACE1("Loaded doc node " << this);
 }
 
 
@@ -1666,7 +1666,7 @@
   theBaseUri(baseUri),
   theDocUri(docUri)
 {
-  NODE_TRACE1("{\nConstructing doc node " << this << " tree = "
+  STORE_TRACE1("{\nConstructing doc node " << this << " tree = "
               << getTree()->getId() << ":" << getTree()
               << " doc uri = " << docUri);
 }
@@ -1721,8 +1721,8 @@
     throw;
   }
 
-  NODE_TRACE1("}");
-  NODE_TRACE1("Copied doc node " << this << " to node " << copyNode);
+  STORE_TRACE1("}");
+  STORE_TRACE1("Copied doc node " << this << " to node " << copyNode);
 
   return copyNode;
 }
@@ -1891,7 +1891,7 @@
     theNodes.resize(numAttributes);
   }
 
-  NODE_TRACE1("Loaded elem node " << this << " name = " << theName->show()
+  STORE_TRACE1("Loaded elem node " << this << " name = " << theName->show()
               << " num bindings = " << numBindings << " num attributes = "
               << numAttributes << std::endl);
 }
@@ -2017,7 +2017,7 @@
     throw;
   }
 
-  NODE_TRACE1("Constructed element node " << this << " parent = "
+  STORE_TRACE1("Constructed element node " << this << " parent = "
               << std::hex << (parent ? (ulong)parent : 0) << " pos = " << pos
               << " tree = " << getTree()->getId() << ":" << getTree()
               << " ordpath = " << theOrdPath.show()
@@ -2358,7 +2358,7 @@
     throw;
   }
 
-  NODE_TRACE1("Copied elem node " << this << " to node " << copyNode
+  STORE_TRACE1("Copied elem node " << this << " to node " << copyNode
               << " name = " << theName->getStringValue() << " parent = "
               << (parent ? parent : 0x0)
               << " pos = " << pos << " copy mode = " << copymode.toString());
@@ -3326,7 +3326,7 @@
   if (qn->isBaseUri())
     theFlags |= IsBaseUri;
 
-  NODE_TRACE1("Loaded attr node " << this << " name = " << theName->getStringValue());
+  STORE_TRACE1("Loaded attr node " << this << " name = " << theName->getStringValue());
 }
 
 
@@ -3449,7 +3449,7 @@
     throw;
   }
 
-  NODE_TRACE1("Constructed attribute node " << this << " parent = "
+  STORE_TRACE1("Constructed attribute node " << this << " parent = "
               << std::hex << (parent ? (ulong)parent : 0) << " pos = " << pos
               << " tree = " << getTree()->getId() << ":" << getTree()
               << " ordpath = " << theOrdPath.show()
@@ -3532,7 +3532,7 @@
     throw;
   }
 
-  NODE_TRACE1("Copied attribute node " << this << " to node " << copyNode
+  STORE_TRACE1("Copied attribute node " << this << " to node " << copyNode
               << " name = " << theName->show() << " parent = "
               << std::hex << (parent ? (ulong)parent : 0) << " pos = " << pos
               << " copy mode = " << copymode.toString());
@@ -3788,7 +3788,7 @@
 {
   setText(content);
 
-  NODE_TRACE1("Loaded text node " << this << " content = " << getText());
+  STORE_TRACE1("Loaded text node " << this << " content = " << getText());
 }
 
 
@@ -3833,13 +3833,13 @@
   }
 
 #ifdef TEXT_ORDPATH
-  NODE_TRACE1("Constructed text node " << this << " parent = "
+  STORE_TRACE1("Constructed text node " << this << " parent = "
               << std::hex << (parent ? (ulong)parent : 0) << " pos = " << pos
               << " tree = " << getTree()->getId() << ":" << getTree()
               << " ordpath = " << theOrdPath.show()
               << " content = " << getText());
 #else
-  NODE_TRACE1("Constructed text node " << this << " parent = "
+  STORE_TRACE1("Constructed text node " << this << " parent = "
               << std::hex << (parent ? (ulong)parent : 0) << " pos = " << pos
               << " tree = " << getTree()->getId() << ":" << getTree()
               << " content = " << getText());
@@ -3894,12 +3894,12 @@
   p->insertChild(this, p->numChildren());
 
 #ifdef TEXT_ORDPATH
-  NODE_TRACE1("Constructed text node " << this << " parent = "
+  STORE_TRACE1("Constructed text node " << this << " parent = "
               << std::hex << (parent ? (ulong)parent : 0)
               << " ordpath = " << theOrdPath.show()
               << " content = " << getValue()->getStringValue());
 #else
-  NODE_TRACE1("Constructed text node " << this << " parent = "
+  STORE_TRACE1("Constructed text node " << this << " parent = "
               << std::hex << (parent ? (ulong)parent : 0)
               << " content = " << getValue()->getStringValue());
 #endif
@@ -4011,7 +4011,7 @@
     throw;
   }
 
-  NODE_TRACE1("Copied text node " << this << " to node " << copyNode
+  STORE_TRACE1("Copied text node " << this << " to node " << copyNode
               << " parent = " << std::hex << (parent ? (ulong)parent : 0)
               << " pos = " << pos);
 
@@ -4496,7 +4496,7 @@
 
   theName = qnpool.insert(zstring(), zstring(), theTarget);
 
-  NODE_TRACE1("Loaded pi node " << this << " target = " << theTarget
+  STORE_TRACE1("Loaded pi node " << this << " target = " << theTarget
               << std::endl);
 }
 
@@ -4529,7 +4529,7 @@
     parent->insertChild(this, pos);
   }
 
-  NODE_TRACE1("Constructed pi node " << this << " parent = "
+  STORE_TRACE1("Constructed pi node " << this << " parent = "
               << std::hex << (parent ? (ulong)parent : 0) << " pos = " << pos
               << " tree = " << getTree()->getId() << ":" << getTree()
               << " ordpath = " << theOrdPath.show() << " target = " << theTarget);
@@ -4578,7 +4578,7 @@
     throw;
   }
 
-  NODE_TRACE1("Copied pi node " << this << " to node " << copyNode
+  STORE_TRACE1("Copied pi node " << this << " to node " << copyNode
               << " parent = " << std::hex << (parent ? (ulong)parent : 0)
               << " pos = " << pos);
 
@@ -4636,7 +4636,7 @@
 {
   theContent.take(content);
 
-  NODE_TRACE1("Loaded comment node " << this << " content = " << theContent);
+  STORE_TRACE1("Loaded comment node " << this << " content = " << theContent);
 }
 
 
@@ -4662,7 +4662,7 @@
     parent->insertChild(this, pos);
   }
 
-  NODE_TRACE1("Constructed comment node " << this << " parent = "
+  STORE_TRACE1("Constructed comment node " << this << " parent = "
               << std::hex << (parent ? (ulong)parent : 0) << " pos = " << pos
               << " tree = " << getTree()->getId() << ":" << getTree()
               << " ordpath = " << theOrdPath.show() << " content = "
@@ -4709,7 +4709,7 @@
     throw;
   }
 
-  NODE_TRACE1("Copied coment node " << this << " to node " << copyNode
+  STORE_TRACE1("Copied coment node " << this << " to node " << copyNode
               << " parent = " << std::hex << (parent ? (ulong)parent : 0)
               << " pos = " << pos);
 

=== modified file 'src/store/naive/node_items.h'
--- src/store/naive/node_items.h	2012-04-24 12:39:38 +0000
+++ src/store/naive/node_items.h	2012-04-27 09:11:24 +0000
@@ -107,28 +107,6 @@
               << store::StoreConsts::toString(getNodeKind()))
 
 
-#ifndef NDEBUG
-
-#define NODE_TRACE(level, msg)                \
-{                                             \
-  if (level <= GET_STORE().getTraceLevel())   \
-    std::cout << msg << std::endl;            \
-}
-
-#define NODE_TRACE1(msg) NODE_TRACE(1, msg);
-#define NODE_TRACE2(msg) NODE_TRACE(2, msg);
-#define NODE_TRACE3(msg) NODE_TRACE(3, msg);
-
-#else
-
-#define NODE_TRACE(msg)
-#define NODE_TRACE1(msg)
-#define NODE_TRACE2(msg)
-#define NODE_TRACE3(msg)
-
-#endif
-
-
 /*******************************************************************************
 
   theRefCount    : It is the sum of theRefCounts of all the nodes belonging to

=== modified file 'src/store/naive/pul_primitives.cpp'
--- src/store/naive/pul_primitives.cpp	2012-04-24 12:39:38 +0000
+++ src/store/naive/pul_primitives.cpp	2012-04-27 09:11:24 +0000
@@ -35,6 +35,7 @@
 #include "store/api/validator.h"
 
 #include "diagnostics/xquery_diagnostics.h"
+#include "diagnostics/util_macros.h"
 
 
 namespace zorba {
@@ -946,42 +947,36 @@
 void UpdDeleteCollection::apply()
 {
   theCollection = GET_STORE().getCollection(theName, theDynamicCollection);
+
   if (theCollection == NULL)
     return;//If two delete collection are issued in the same snapshot is a noop
+
   Collection* collection = static_cast<Collection*>(theCollection.getp());
 
   std::vector<store::Index*> indexes;
   collection->getIndexes(indexes);
 
   if (!indexes.empty())
-    throw XQUERY_EXCEPTION(
-      zerr::ZDDY0013_COLLECTION_BAD_DESTROY_INDEXES,
-      ERROR_PARAMS( collection->getName()->getStringValue() ),
-      ERROR_LOC( theLoc )
-    );
+    RAISE_ERROR(zerr::ZDDY0013_COLLECTION_BAD_DESTROY_INDEXES, theLoc,
+    ERROR_PARAMS(collection->getName()->getStringValue()));
 
   std::vector<store::IC*> activeICs;
   collection->getActiveICs(activeICs);
 
   if (!activeICs.empty())
-    throw XQUERY_EXCEPTION(
-      zerr::ZDDY0014_COLLECTION_BAD_DESTROY_ICS,
-      ERROR_PARAMS( collection->getName()->getStringValue() ),
-      ERROR_LOC( theLoc )
-    );
+    RAISE_ERROR(zerr::ZDDY0014_COLLECTION_BAD_DESTROY_ICS, theLoc,
+    ERROR_PARAMS(collection->getName()->getStringValue()));
 
   uint64_t size;
-  try {
+  try 
+  {
     size = to_xs_unsignedLong(collection->size());
-  } catch (std::range_error& e)
+  }
+  catch (std::range_error& e)
   {
-    throw ZORBA_EXCEPTION(
-        zerr::ZSTR0060_RANGE_EXCEPTION,
-        ERROR_PARAMS(
-          BUILD_STRING("collection too big ("
-            << e.what() << "; " << theName << ")")
-        )
-      );
+    throw ZORBA_EXCEPTION(zerr::ZSTR0060_RANGE_EXCEPTION,
+    ERROR_PARAMS(BUILD_STRING("collection too big ("
+                              << e.what() << "; " << theName << ")")));
   }
 
   for (uint64_t i = 0; i < size; ++i)
@@ -989,11 +984,10 @@
     XmlNode* root = static_cast<XmlNode*>(collection->nodeAt(xs_integer(i)).getp());
     XmlTree* tree = root->getTree();
     if (tree->getRefCount() > 1)
-      throw XQUERY_EXCEPTION(
-        zerr::ZDDY0015_COLLECTION_BAD_DESTROY_NODES,
-        ERROR_PARAMS( collection->getName()->getStringValue() ),
-        ERROR_LOC( theLoc )
-      );
+    {
+      RAISE_ERROR(zerr::ZDDY0015_COLLECTION_BAD_DESTROY_NODES, theLoc,
+      ERROR_PARAMS(collection->getName()->getStringValue()));
+    }
   }
 
   GET_STORE().deleteCollection(theName, theDynamicCollection);
@@ -1013,13 +1007,13 @@
 void UpdInsertIntoCollection::apply()
 {
   Collection* lColl = static_cast<Collection*>
-                            (GET_STORE().getCollection(theName, theDynamicCollection).getp());
+  (GET_STORE().getCollection(theName, theDynamicCollection).getp());
   assert(lColl);
 
   theIsApplied = true;
 
-  std::size_t numNodes = theNodes.size();
-  for (std::size_t i = 0; i < numNodes; ++i)
+  csize numNodes = theNodes.size();
+  for (csize i = 0; i < numNodes; ++i)
   {
     lColl->addNode(theNodes[i], xs_integer(-1));
     ++theNumApplied;
@@ -1030,7 +1024,7 @@
 void UpdInsertIntoCollection::undo()
 {
   Collection* lColl = static_cast<Collection*>
-                            (GET_STORE().getCollection(theName, theDynamicCollection).getp());
+  (GET_STORE().getCollection(theName, theDynamicCollection).getp());
   assert(lColl);
 
   uint64_t lastPos;
@@ -1040,13 +1034,9 @@
   }
   catch (std::range_error& e)
   {
-    throw ZORBA_EXCEPTION(
-        zerr::ZSTR0060_RANGE_EXCEPTION,
-        ERROR_PARAMS(
-          BUILD_STRING("collection too big ("
-            << e.what() << "; " << theName << ")")
-        )
-      );
+    throw ZORBA_EXCEPTION(zerr::ZSTR0060_RANGE_EXCEPTION,
+    ERROR_PARAMS(BUILD_STRING("collection too big ("
+                              << e.what() << "; " << theName << ")")));
   }
 
   for (long i = theNumApplied-1; i >= 0; --i)
@@ -1154,7 +1144,7 @@
 void UpdInsertBeforeIntoCollection::apply()
 {
   Collection* lColl = static_cast<Collection*>
-                            (GET_STORE().getCollection(theName, theDynamicCollection).getp());
+  (GET_STORE().getCollection(theName, theDynamicCollection).getp());
   assert(lColl);
 
   if (!theNodes.empty())
@@ -1170,7 +1160,7 @@
 void UpdInsertBeforeIntoCollection::undo()
 {
   Collection* lColl = static_cast<Collection*>
-                            (GET_STORE().getCollection(theName, theDynamicCollection).getp());
+  (GET_STORE().getCollection(theName, theDynamicCollection).getp());
   assert(lColl);
   ZORBA_ASSERT(theFirstNode == lColl->nodeAt(theFirstPos));
 
@@ -1184,7 +1174,7 @@
 void UpdInsertAfterIntoCollection::apply()
 {
   Collection* lColl = static_cast<Collection*>
-                            (GET_STORE().getCollection(theName, theDynamicCollection).getp());
+  (GET_STORE().getCollection(theName, theDynamicCollection).getp());
   assert(lColl);
 
   if (!theNodes.empty())
@@ -1201,7 +1191,7 @@
 void UpdInsertAfterIntoCollection::undo()
 {
   Collection* lColl = static_cast<Collection*>
-                            (GET_STORE().getCollection(theName, theDynamicCollection).getp());
+  (GET_STORE().getCollection(theName, theDynamicCollection).getp());
   assert(lColl);
   ZORBA_ASSERT(theFirstNode == lColl->nodeAt(theFirstPos));
 
@@ -1236,13 +1226,13 @@
       );
   }
 
-  std::size_t numNodes = theNodes.size();
+  csize numNodes = theNodes.size();
 
   bool isLast = theIsLast;
 
   if (theIsLast)
   {
-    for (std::size_t i = numNodes; i > 0; --i)
+    for (csize i = numNodes; i > 0; --i)
     {
       if (theNodes[i-1] != lColl->nodeAt(xs_integer(size - i)))
       {
@@ -1270,7 +1260,7 @@
 void UpdDeleteNodesFromCollection::undo()
 {
   Collection* lColl = static_cast<Collection*>
-                            (GET_STORE().getCollection(theName, theDynamicCollection).getp());
+  (GET_STORE().getCollection(theName, theDynamicCollection).getp());
   assert(lColl);
 
   for (std::size_t i = 0; i < theNumApplied; ++i)
@@ -1289,7 +1279,7 @@
 void UpdTruncateCollection::apply()
 {
   Collection* lColl = static_cast<Collection*>
-                      (GET_STORE().getCollection(theName, theDynamicCollection).getp());
+  (GET_STORE().getCollection(theName, theDynamicCollection).getp());
   assert(lColl);
   
   lColl->removeAll();

=== modified file 'src/store/naive/simple_index_value.cpp'
--- src/store/naive/simple_index_value.cpp	2012-04-24 12:39:38 +0000
+++ src/store/naive/simple_index_value.cpp	2012-04-27 09:11:24 +0000
@@ -366,9 +366,9 @@
     ERROR_PARAMS(key->toString(), theQname->getStringValue()));
   }
 
-  ValueIndexValue* valueSet = NULL;
+  IndexMap::iterator pos = theMap.find(key);
 
-  if (theMap.get(key, valueSet))
+  if (pos != theMap.end())
   {
     if (isUnique())
     {
@@ -376,21 +376,20 @@
       ERROR_PARAMS(theQname->getStringValue()));
     }
 
-    valueSet->resize(valueSet->size() + 1);
-    (*valueSet)[valueSet->size()-1].transfer(value);
-    
+    (*pos).second->transfer_back(value);
+    key = const_cast<store::IndexKey*>((*pos).first);
+
     return true;
   }
 
-  valueSet = new ValueIndexValue(1);
+  ValueIndexValue* valueSet = new ValueIndexValue(1);
   (*valueSet)[0].transfer(value);
   
   //std::cout << "Index Entry Insert [" << key << "," 
   //          << valueSet << "]" << std::endl;
 
-  const store::IndexKey* key2 = key;
-  theMap.insert(key2, valueSet);
-  key = NULL; // ownership of the key obj passes to the index.
+  // Note: ownership of the key obj passes to the index.
+  theMap.insert(key, valueSet);
 
   return false;
 } 
@@ -407,7 +406,7 @@
 ********************************************************************************/
 bool ValueHashIndex::remove(
     const store::IndexKey* key,
-    store::Item_t& value,
+    const store::Item_t& value,
     bool all)
 {
   if (key->size() != getNumColumns())
@@ -649,7 +648,7 @@
 #if 0
   std::cout << "inserting entry : [(";
 
-  for (ulong i = 0; i < getNumColumns(); i++)
+  for (csize i = 0; i < getNumColumns(); i++)
   {
     if (key[i] != NULL)
       std::cout << key[i]->getStringValue() << ", ";
@@ -672,14 +671,16 @@
     }
 
     pos->second->transfer_back(value);
+    key = const_cast<store::IndexKey*>(pos->first);
+
     return true;
   }
 
   ValueIndexValue* valueSet = new ValueIndexValue(1);
   (*valueSet)[0].transfer(value);
 
+  // Note: ownership of the key obj passes to the index.
   theMap.insert(IndexMapPair(key, valueSet));
-  key = NULL; // ownership of the key obj passes to the index.
 
   return false;
 }
@@ -690,7 +691,7 @@
 ********************************************************************************/
 bool ValueTreeIndex::remove(
     const store::IndexKey* key,
-    store::Item_t& value,
+    const store::Item_t& value,
     bool all)
 {
   if (key->size() != getNumColumns())

=== modified file 'src/store/naive/simple_index_value.h'
--- src/store/naive/simple_index_value.h	2012-04-24 12:39:38 +0000
+++ src/store/naive/simple_index_value.h	2012-04-27 09:11:24 +0000
@@ -88,11 +88,13 @@
 public:
   const XQPCollator* getCollator(ulong i) const;
 
+  virtual bool isTreeIndex() = 0;
+
   virtual bool insert(store::IndexKey*& key, store::Item_t& item) = 0;
 
   virtual bool remove(
         const store::IndexKey* key,
-        store::Item_t& item,
+        const store::Item_t& item,
         bool all = false) = 0;
 };
 
@@ -142,6 +144,8 @@
   ~ValueHashIndex();
 
 public:
+  bool isTreeIndex() { return false; }
+
   void clear();
 
   ulong size() const;
@@ -150,7 +154,7 @@
 
   bool insert(store::IndexKey*& key, store::Item_t& item);
 
-  bool remove(const store::IndexKey* key, store::Item_t& item, bool all);
+  bool remove(const store::IndexKey* key, const store::Item_t& item, bool all);
 };
 
 
@@ -227,6 +231,8 @@
   ~ValueTreeIndex();
 
 public:
+  bool isTreeIndex() { return true; }
+
   void clear();
 
   ulong size() const;
@@ -235,7 +241,7 @@
 
   bool insert(store::IndexKey*& key, store::Item_t& item);
 
-  bool remove(const store::IndexKey* key, store::Item_t& item, bool all = false);
+  bool remove(const store::IndexKey* key, const store::Item_t& item, bool all = false);
 };
 
 

=== modified file 'src/store/naive/simple_pul.cpp'
--- src/store/naive/simple_pul.cpp	2012-04-24 12:39:38 +0000
+++ src/store/naive/simple_pul.cpp	2012-04-27 09:11:24 +0000
@@ -58,28 +58,6 @@
 /*******************************************************************************
 
 ********************************************************************************/
-void cleanIndexDeltas(std::vector<store::IndexDelta>& deltas)
-{
-  std::vector<store::IndexDelta>::iterator ite = deltas.begin();
-  std::vector<store::IndexDelta>::iterator end = deltas.end();
-
-  for (; ite != end; ++ite)
-  {
-    store::IndexDelta::iterator ite2 = (*ite).begin();
-    store::IndexDelta::iterator end2 = (*ite).end();
-
-    for (; ite2 != end2; ++ite2)
-    {
-      if ((*ite2).second)
-        delete (*ite2).second;
-    }
-  }
-}
-
-
-/*******************************************************************************
-
-********************************************************************************/
 void applyList(std::vector<UpdatePrimitive*>& aVector)
 {
   std::vector<UpdatePrimitive*>::iterator iter = aVector.begin();
@@ -97,9 +75,9 @@
 ********************************************************************************/
 void undoList(std::vector<UpdatePrimitive*>& list)
 {
-  ulong size = (ulong)list.size();
+  csize size = list.size();
 
-  for (ulong i = size; i > 0; --i) 
+  for (csize i = size; i > 0; --i) 
   {
     if (list[i-1]->isApplied())
       list[i-1]->undo();
@@ -150,14 +128,14 @@
 
   cleanList(theValidationList);
 
-  CollectionPulMap::iterator ite = theCollectionPuls.begin();
-  CollectionPulMap::iterator end = theCollectionPuls.end();
+  CollectionPuls::iterator ite = theCollectionPuls.begin();
+  CollectionPuls::iterator end = theCollectionPuls.end();
 
   for (; ite != end; ++ite)
   {
-    if ((*ite).second != NULL)
-      delete (*ite).second;
+    delete (*ite);
   }
+
   cleanList(theICActivationList);
   cleanList(theCreateDocumentList);
   cleanList(theDeleteDocumentList);
@@ -197,49 +175,53 @@
 
     if (collName == theLastCollection)
       return theLastPul;
-    return getCollectionPulByName(collName,collection->isDynamic());
+
+    return getCollectionPulByName(collName, collection->isDynamic());
   }
   else if (theNoCollectionPul != NULL)
   {
     return theNoCollectionPul;
   }
-  else if (theCollectionPuls[NULL] != NULL)
-  {
-    theNoCollectionPul = theCollectionPuls[NULL];
-    return theNoCollectionPul;
-  }
   else
   {
     theNoCollectionPul = new CollectionPul(this, NULL);
-    theCollectionPuls[NULL] = theNoCollectionPul;
+    theCollectionPuls.push_back(theNoCollectionPul);
+    theCollectionPulsMap[NULL] = theCollectionPuls.size() - 1;
     return theNoCollectionPul;
   }
 }
 
-CollectionPul* PULImpl::getCollectionPulByName(const store::Item* name, bool dynamicCollection)
+
+CollectionPul* PULImpl::getCollectionPulByName(
+    const store::Item* name, 
+    bool isDynamic)
 {
   const QNameItem* collName = static_cast<const QNameItem*>(name)->getNormalized();
 
-  assert(!name->isNode());
+  assert(name->isAtomic());
 
   // "name" is the name of a collection.
   if (name == theLastCollection)
     return theLastPul;
 
-  CollectionPulMap::iterator ite = theCollectionPuls.find(collName);
+  CollectionPulMap::iterator ite = theCollectionPulsMap.find(collName);
 
   theLastCollection = collName;
 
-  if (ite != theCollectionPuls.end())
+  if (ite != theCollectionPulsMap.end())
   {
-    theLastPul = ite->second;
+    theLastPul = theCollectionPuls[ite->second];
   }
   else
   {
     Collection* collection = static_cast<Collection*>
-    (GET_STORE().getCollection(collName,dynamicCollection).getp());
+    (GET_STORE().getCollection(collName, isDynamic).getp());
+
     theLastPul = new CollectionPul(this, collection);
-    theCollectionPuls[collName] = theLastPul;
+
+    theCollectionPuls.push_back(theLastPul);
+
+    theCollectionPulsMap[collName] = theCollectionPuls.size() - 1;
   }
 
   return theLastPul;
@@ -1107,17 +1089,17 @@
   PULImpl* otherp = reinterpret_cast<PULImpl*>(other);
 
   // Merge collection-specific primitives
-  CollectionPulMap::iterator thisIte = theCollectionPuls.begin();
-  CollectionPulMap::iterator thisEnd = theCollectionPuls.end();
-  CollectionPulMap::iterator otherIte = otherp->theCollectionPuls.begin();
-  CollectionPulMap::iterator otherEnd = otherp->theCollectionPuls.end();
+  CollectionPulMap::iterator thisIte = theCollectionPulsMap.begin();
+  CollectionPulMap::iterator thisEnd = theCollectionPulsMap.end();
+  CollectionPulMap::iterator otherIte = otherp->theCollectionPulsMap.begin();
+  CollectionPulMap::iterator otherEnd = otherp->theCollectionPulsMap.end();
 
   while (thisIte != thisEnd && otherIte != otherEnd)
   {
     if (thisIte->first == otherIte->first)
     {
-      CollectionPul* thisPul = thisIte->second;
-      CollectionPul* otherPul = otherIte->second;
+      CollectionPul* thisPul = theCollectionPuls[thisIte->second];
+      CollectionPul* otherPul = otherp->theCollectionPuls[otherIte->second];
 
       // Merge XQUF primitives
       mergeUpdateList(thisPul,
@@ -1186,18 +1168,27 @@
     }
     else
     {
-      theCollectionPuls[otherIte->first] = otherIte->second;
-      otherIte->second->thePul = this;
-      otherIte->second = NULL;
+      CollectionPul* otherPul = otherp->theCollectionPuls[otherIte->second];
+      otherp->theCollectionPuls[otherIte->second] = NULL;
+
+      theCollectionPuls.push_back(otherPul);
+      theCollectionPulsMap[otherIte->first] = theCollectionPuls.size() - 1;
+
+      otherPul->switchPul(this);
       ++otherIte;
     }
   }
 
   while (otherIte != otherEnd)
   {
-    theCollectionPuls[otherIte->first] = otherIte->second;
-    otherIte->second->switchPul(this);
-    otherIte->second = NULL;
+    CollectionPul* otherPul = otherp->theCollectionPuls[otherIte->second];
+    otherp->theCollectionPuls[otherIte->second] = NULL;
+
+    theCollectionPuls.push_back(otherPul);
+    theCollectionPulsMap[otherIte->first] = theCollectionPuls.size() - 1;
+
+    otherPul->switchPul(this);
+
     ++otherIte;
   }
 
@@ -1470,14 +1461,14 @@
 ********************************************************************************/
 void PULImpl::checkTransformUpdates(const std::vector<store::Item*>& rootNodes) const
 {
-  ulong numRoots = (ulong)rootNodes.size();
+  csize numRoots = rootNodes.size();
 
-  CollectionPulMap::const_iterator collIte = theCollectionPuls.begin();
-  CollectionPulMap::const_iterator collEnd = theCollectionPuls.end();
+  std::vector<CollectionPul*>::const_iterator collIte = theCollectionPuls.begin();
+  std::vector<CollectionPul*>::const_iterator collEnd = theCollectionPuls.end();
 
   for (; collIte != collEnd; ++collIte)
   {
-    CollectionPul* pul = collIte->second;
+    CollectionPul* pul = *collIte;
 
     NodeToUpdatesMap::iterator it = pul->theNodeToUpdatesMap.begin();
     NodeToUpdatesMap::iterator end = pul->theNodeToUpdatesMap.end();
@@ -1488,7 +1479,7 @@
 
       bool found = false;
 
-      for (ulong i = 0; i < numRoots; i++)
+      for (csize i = 0; i < numRoots; i++)
       {
         XmlNode* rootNode = reinterpret_cast<XmlNode*>(rootNodes[i]);
         
@@ -1533,12 +1524,12 @@
   std::set<store::Collection*> collections;
   std::set<store::Collection*> truncated_collections;
 
-  CollectionPulMap::iterator collIte = theCollectionPuls.begin();
-  CollectionPulMap::iterator collEnd = theCollectionPuls.end();
+  CollectionPuls::iterator collIte = theCollectionPuls.begin();
+  CollectionPuls::iterator collEnd = theCollectionPuls.end();
 
   for (; collIte != collEnd; ++collIte)
   {
-    store::Collection* collection = store->getCollection(collIte->first);
+    store::Collection* collection = (*collIte)->theCollection;
 
     // The collection may not be created yet.
     if (collection == NULL)
@@ -1546,7 +1537,7 @@
 
     collections.insert(collection);
 
-    CollectionPul* pul = collIte->second;
+    CollectionPul* pul = *collIte;
 
     if (pul->theTruncateCollectionList.size() > 0)
     {
@@ -1570,6 +1561,7 @@
                            (pul->theInsertIntoCollectionList[i]);
 
       csize numDocs = upd->numNodes();
+
       for (csize j = 0; j < numDocs; ++j)
         pul->theInsertedDocs.push_back(static_cast<XmlNode*>(upd->getNode(j)));
     }
@@ -1582,6 +1574,7 @@
                            (pul->theDeleteFromCollectionList[i]);
 
       csize numDocs = upd->numNodes();
+
       for (csize j = 0; j < numDocs; ++j)
         pul->theDeletedDocs.push_back(static_cast<XmlNode*>(upd->getNode(j)));
     }
@@ -1689,8 +1682,8 @@
 ********************************************************************************/
 void PULImpl::applyUpdates(bool inheritNSBindings)
 {
-  CollectionPulMap::iterator collIte = theCollectionPuls.begin();
-  CollectionPulMap::iterator collEnd = theCollectionPuls.end();
+  CollectionPuls::iterator collIte = theCollectionPuls.begin();
+  CollectionPuls::iterator collEnd = theCollectionPuls.end();
 
   theInheritNSBindings = inheritNSBindings;
 
@@ -1701,7 +1694,7 @@
     // maintained incrementally w.r.t. updates in C.
     for (; collIte != collEnd; ++collIte)
     {
-      CollectionPul* pul = collIte->second;
+      CollectionPul* pul = *collIte;
       pul->applyUpdates();
     }
 
@@ -1726,7 +1719,7 @@
     // check integrity constraints for involved collections
     for (collIte = theCollectionPuls.begin(); collIte != collEnd; ++collIte)
     {
-      CollectionPul* pul = collIte->second;      
+      CollectionPul* pul = *collIte;      
 
       if (pul->theCollection != NULL)
       {
@@ -1743,9 +1736,18 @@
     // Apply delete-collection primitives
     for (collIte = theCollectionPuls.begin(); collIte != collEnd; ++collIte)
     {
-      CollectionPul* pul = collIte->second;
+      CollectionPul* pul = *collIte;
       applyList(pul->theDeleteCollectionList);
     }
+
+    // Refresh each incrementally maintained index. We need to do this here
+    // because refreshIndices can raise an error (e.g. if the unique constraint
+    // of an index is violated).
+    for (collIte = theCollectionPuls.begin(); collIte != collEnd; ++collIte)
+    {
+      CollectionPul* pul = *collIte;
+      pul->refreshIndexes();
+    }
   }
   catch (...)
   {
@@ -1753,10 +1755,10 @@
     throw;
   }
 
-  //
+  // Perform actions that are not expected to raise any errors
   for (collIte = theCollectionPuls.begin(); collIte != collEnd; ++collIte)
   {
-    CollectionPul* pul = collIte->second;
+    CollectionPul* pul = *collIte;
     pul->finalizeUpdates();
   }
 
@@ -1790,12 +1792,12 @@
   {
     undoList(theValidationList);
 
-    CollectionPulMap::iterator collIte = theCollectionPuls.begin();
-    CollectionPulMap::iterator collEnd = theCollectionPuls.end();
+    CollectionPuls::iterator collIte = theCollectionPuls.begin();
+    CollectionPuls::iterator collEnd = theCollectionPuls.end();
 
     for (; collIte != collEnd; ++collIte)
     {
-      CollectionPul* pul = collIte->second;
+      CollectionPul* pul = *collIte;
       undoList(pul->theDeleteCollectionList);
     }
 
@@ -1807,7 +1809,7 @@
 
     for (collIte = theCollectionPuls.begin(); collIte != collEnd; ++collIte)
     {
-      CollectionPul* pul = collIte->second;
+      CollectionPul* pul = *collIte;
       pul->undoUpdates();
     }
 
@@ -1851,10 +1853,7 @@
   cleanList(theTruncateCollectionList);
   cleanList(theDeleteCollectionList);
 
-  cleanIndexDeltas(theBeforeIndexDeltas);
-  cleanIndexDeltas(theAfterIndexDeltas);
-  cleanIndexDeltas(theInsertedDocsIndexDeltas);
-  cleanIndexDeltas(theDeletedDocsIndexDeltas);
+  cleanIndexDeltas();
 }
 
 
@@ -1890,22 +1889,44 @@
   }
 }
 
-/*******************************************************************************
-  The comparison function for sorting the entries of an IndexDelta by the doc node
-********************************************************************************/
-static bool cmp(const std::pair<store::Item_t, store::IndexKey*>& e1,
-                const std::pair<store::Item_t, store::IndexKey*>& e2)
-{
-  return e1.first.getp() < e2.first.getp();
-}
-
-
-/*******************************************************************************
-  Compute the index contents on the modified docs, before any modifications
-  are actually applied.
+
+/*******************************************************************************
+  For each incrementally-maintained index associated with this collection,
+  compute the index contents on the modified and deleted docs, before any 
+  modifications are actually applied.
+
+  Note 1: If any docs are deleted, we have to remove from the before and after
+  deltas any entries for nodes belonging to the deleted docs. This is required
+  for the undo to work properly. For example, let E = [N, K] be an after-delta
+  entry, and let N be a node in a doc D that is going to be removed from the 
+  collection. Then, during undo, the key pointer in E may be a dangling pointer.
+
+  Note 2: Given note 1, we actually have to compute the delete-docs deltas
+  *before* any modification are actually applied.
 ********************************************************************************/
 void CollectionPul::computeIndexBeforeDeltas()
 {
+  csize numIncrementalIndices = theIncrementalIndices.size();
+
+  if (numIncrementalIndices == 0)
+    return;
+
+  std::vector<XmlNode*>::const_iterator docIte = theDeletedDocs.begin();
+  std::vector<XmlNode*>::const_iterator docEnd = theDeletedDocs.end();
+
+  for (; docIte != docEnd; ++docIte)
+  {
+    theModifiedDocs.erase(*docIte);
+
+    for (csize i = 0; i < numIncrementalIndices; ++i)
+    {
+      store::IndexEntryCreator* docIndexer = theIndexEntryCreators[i].getp();
+      store::IndexDelta& indexDelta = theDeletedDocsIndexDeltas[i];
+
+      docIndexer->createIndexEntries((*docIte), indexDelta);
+    }
+  }
+
   computeIndexDeltas(theBeforeIndexDeltas);
 }
 
@@ -1917,21 +1938,19 @@
 ********************************************************************************/
 void CollectionPul::computeIndexAfterDeltas()
 {
+  csize numIncrementalIndices = theIncrementalIndices.size();
+
+  if (numIncrementalIndices == 0)
+    return;
+
   computeIndexDeltas(theAfterIndexDeltas);
 
-  csize numIncrementalIndices = theIncrementalIndices.size();
-
-  if (numIncrementalIndices == 0)
-    return;
-
-  theInsertedDocsIndexDeltas.resize(numIncrementalIndices);
-
   std::vector<XmlNode*>::const_iterator docIte = theInsertedDocs.begin();
   std::vector<XmlNode*>::const_iterator docEnd = theInsertedDocs.end();
 
   for (; docIte != docEnd; ++docIte)
   {
-    for (ulong i = 0; i < numIncrementalIndices; ++i)
+    for (csize i = 0; i < numIncrementalIndices; ++i)
     {
       store::IndexEntryCreator* docIndexer = theIndexEntryCreators[i].getp();
       store::IndexDelta& indexDelta = theInsertedDocsIndexDeltas[i];
@@ -1939,22 +1958,6 @@
       docIndexer->createIndexEntries((*docIte), indexDelta);
     }
   }
-
-  theDeletedDocsIndexDeltas.resize(numIncrementalIndices);
-
-  docIte = theDeletedDocs.begin();
-  docEnd = theDeletedDocs.end();
-
-  for (; docIte != docEnd; ++docIte)
-  {
-    for (ulong i = 0; i < numIncrementalIndices; ++i)
-    {
-      store::IndexEntryCreator* docIndexer = theIndexEntryCreators[i].getp();
-      store::IndexDelta& indexDelta = theDeletedDocsIndexDeltas[i];
-
-      docIndexer->createIndexEntries((*docIte), indexDelta);
-    }
-  }
 }
 
 
@@ -1965,19 +1968,14 @@
 ********************************************************************************/
 void CollectionPul::computeIndexDeltas(std::vector<store::IndexDelta>& deltas)
 {
-  ulong numIncrementalIndices = (ulong)theIncrementalIndices.size();
-
-  if (numIncrementalIndices == 0)
-    return;
-
-  deltas.resize(numIncrementalIndices);
+  csize numIncrementalIndices = theIncrementalIndices.size();
 
   std::set<XmlNode*>::const_iterator docIte = theModifiedDocs.begin();
   std::set<XmlNode*>::const_iterator docEnd = theModifiedDocs.end();
 
   for (; docIte != docEnd; ++docIte)
   {
-    for (ulong i = 0; i < numIncrementalIndices; ++i)
+    for (csize i = 0; i < numIncrementalIndices; ++i)
     {
       store::IndexEntryCreator* docIndexer = theIndexEntryCreators[i].getp();
       store::IndexDelta& indexDelta = deltas[i];
@@ -1985,113 +1983,268 @@
       docIndexer->createIndexEntries((*docIte), indexDelta);
     }
   }
-
-  for (ulong i = 0; i < numIncrementalIndices; ++i)
-  {
-    store::IndexDelta& indexDelta = deltas[i];
-
-    std::sort(indexDelta.begin(), indexDelta.end(), cmp);
-  }
-}
-
-
-/*******************************************************************************
-
-********************************************************************************/
-void CollectionPul::refreshIndices()
-{
-  csize numIncrementalIndices = theTruncatedIndices.size();
-  for (csize idx = 0; idx < numIncrementalIndices; ++idx)
+}
+
+
+/*******************************************************************************
+
+********************************************************************************/
+void CollectionPul::cleanIndexDeltas()
+{
+  csize numIncrementalIndices = theIncrementalIndices.size();
+
+  for (csize idx = 0; idx < numIncrementalIndices; ++idx)
+  {
+    store::IndexDelta::iterator ite;
+    store::IndexDelta::iterator end;
+    store::IndexDelta* delta;
+    csize numApplied;
+
+    delta = &theInsertedDocsIndexDeltas[idx];
+    if (delta)
+    {
+      numApplied = theNumInsertedDocsIndexDeltasApplied[idx];
+      ite = delta->begin() + numApplied;
+      end = delta->end();
+      for (; ite != end; ++ite)
+      {
+        delete (*ite).second;
+      }
+    }
+
+    delta = &theAfterIndexDeltas[idx];
+    if (delta)
+    {
+      numApplied = theNumAfterIndexDeltasApplied[idx];
+      ite = delta->begin() + numApplied;
+      end = delta->end();
+      for (; ite != end; ++ite)
+      {
+        delete (*ite).second;
+      }
+    }
+
+    delta = &theDeletedDocsIndexDeltas[idx];
+    if (delta)
+    {
+      ite = delta->begin();
+      end = delta->end();
+      for (; ite != end; ++ite)
+      {
+        delete (*ite).second;
+      }
+    }
+
+    delta = &theBeforeIndexDeltas[idx];
+    if (delta)
+    {
+      ite = delta->begin();
+      end = delta->end();
+      for (; ite != end; ++ite)
+      {
+        delete (*ite).second;
+      }
+    }
+  }
+}
+
+
+/*******************************************************************************
+  Refresh the incrementally maintained indexes.
+********************************************************************************/
+void CollectionPul::refreshIndexes()
+{
+  csize numIncrementalIndices = theIncrementalIndices.size();
+
+  STORE_TRACE1("Refreshing indexes for collection "
+               << (theCollection ?
+                   theCollection->getName()->getStringValue().c_str() :
+                   "NULL")); 
+
+  for (csize idx = 0; idx < numIncrementalIndices; ++idx)
+  {
+    ValueIndex* index = static_cast<ValueIndex*>(theIncrementalIndices[idx]);
+
+    STORE_TRACE2("Index size before do = " 
+                 << (!index->isTreeIndex() ? index->size() : 0));
+
+    store::IndexDelta& beforeDelta = theBeforeIndexDeltas[idx];
+    store::IndexDelta& afterDelta = theAfterIndexDeltas[idx];
+    store::IndexDelta& deletedDelta = theDeletedDocsIndexDeltas[idx];
+    store::IndexDelta& insertedDelta = theInsertedDocsIndexDeltas[idx];
+
+    csize& numBeforeApplied = theNumBeforeIndexDeltasApplied[idx];
+    csize& numAfterApplied = theNumAfterIndexDeltasApplied[idx];
+    csize& numDeletedApplied = theNumDeletedDocsIndexDeltasApplied[idx];
+    csize& numInsertedApplied = theNumInsertedDocsIndexDeltasApplied[idx];
+
+    store::IndexKey* key;
+    store::Item_t node;
+
+    store::IndexDelta::iterator ite;
+    store::IndexDelta::iterator end;
+
+    ite = beforeDelta.begin();
+    end = beforeDelta.end();
+    for (; ite != end; ++ite, ++numBeforeApplied)
+    {
+      index->remove((*ite).second, (*ite).first);
+    }
+
+    ite = afterDelta.begin();
+    end = afterDelta.end();
+    for (; ite != end; ++ite, ++numAfterApplied)
+    {
+      node = (*ite).first;
+      key = (*ite).second;
+
+      // If the index had its own key obj already, delete the key obj that was
+      // allocated during the delta creation.
+      if (index->insert((*ite).second, node))
+      {
+        assert(key != (*ite).second);
+        delete key;
+      }
+    }
+
+    STORE_TRACE2("deleted-delta size = " << deletedDelta.size());
+ 
+    ite = deletedDelta.begin();
+    end = deletedDelta.end();
+    for (; ite != end; ++ite, ++numDeletedApplied)
+    {
+      index->remove((*ite).second, (*ite).first);
+    }
+
+    STORE_TRACE2("inserted-delta size = " << insertedDelta.size());
+
+    ite = insertedDelta.begin();
+    end = insertedDelta.end();
+    for (; ite != end; ++ite, ++numInsertedApplied)
+    {
+      node = (*ite).first;
+      key = (*ite).second;
+
+      if (index->insert((*ite).second, node))
+      {
+        assert(key != (*ite).second);
+        delete key;
+      }
+    }
+
+    STORE_TRACE2("Index size after do = " 
+                 << (!index->isTreeIndex() ? index->size() : 0));
+  }
+
+  STORE_TRACE1("Refreshed indexes for collection " 
+               << (theCollection ?
+                   theCollection->getName()->getStringValue().c_str() :
+                   "NULL")
+               << std::endl);
+}
+
+
+/*******************************************************************************
+
+********************************************************************************/
+void CollectionPul::undoRefreshIndexes()
+{
+  csize numIncrementalIndices = theIncrementalIndices.size();
+
+  STORE_TRACE1("Reverting indexes for collection " 
+               << (theCollection ?
+                   theCollection->getName()->getStringValue().c_str() :
+                   "NULL")
+               << std::endl);
+
+  for (csize idx = 0; idx < numIncrementalIndices; ++idx)
+  {
+    ValueIndex* index = static_cast<ValueIndex*>(theIncrementalIndices[idx]);
+
+    STORE_TRACE2("Index size before undo = " 
+                 << (!index->isTreeIndex() ? index->size() : 0));
+    
+    store::IndexDelta& beforeDelta = theBeforeIndexDeltas[idx];
+    store::IndexDelta& afterDelta = theAfterIndexDeltas[idx];
+    store::IndexDelta& insertedDelta = theInsertedDocsIndexDeltas[idx];
+    store::IndexDelta& deletedDelta = theDeletedDocsIndexDeltas[idx];
+
+    csize numBeforeApplied = theNumBeforeIndexDeltasApplied[idx];
+    csize numAfterApplied = theNumAfterIndexDeltasApplied[idx];
+    csize numDeletedApplied = theNumDeletedDocsIndexDeltasApplied[idx];
+    csize numInsertedApplied = theNumInsertedDocsIndexDeltasApplied[idx];
+
+    store::IndexDelta::reverse_iterator ite;
+    store::IndexDelta::reverse_iterator end;
+
+    ite = insertedDelta.rbegin() + (insertedDelta.size() - numInsertedApplied);
+    end = insertedDelta.rend();
+    for (; ite != end; ++ite)
+    {
+      index->remove((*ite).second, (*ite).first);
+    }
+
+    ite = deletedDelta.rbegin() + (deletedDelta.size() - numDeletedApplied);
+    end = deletedDelta.rend();
+    for (; ite != end; ++ite)
+    {
+      store::IndexKey* key = (*ite).second;
+
+      // If the index takes ownership of the key obj, set the key ptr to null
+      // so that the key obj will not be deleted during cleanIndexDeltas().
+      if (!index->insert(key, (*ite).first))
+      {
+        assert(key == (*ite).second);
+        (*ite).second = NULL;
+      }
+    }
+
+    ite = afterDelta.rbegin() + (afterDelta.size() - numAfterApplied);
+    end = afterDelta.rend();
+    for (; ite != end; ++ite)
+    {
+      index->remove((*ite).second, (*ite).first);
+    }
+
+    ite = beforeDelta.rbegin() + (beforeDelta.size() - numBeforeApplied);
+    end = beforeDelta.rend();
+    for (; ite != end; ++ite)
+    {
+      store::IndexKey* key = (*ite).second;
+
+      // If the index takes ownership of the key obj, set the key ptr to null
+      // so that the key obj will not be deleted during cleanIndexDeltas().
+      if (!index->insert(key, (*ite).first))
+      {
+        assert(key == (*ite).second);
+        (*ite).second = NULL;
+      }
+    }
+
+    STORE_TRACE2("Index size after undo = " 
+                 << (!index->isTreeIndex() ? index->size() : 0));
+  }
+
+  STORE_TRACE1("Reverted indexes for collection " 
+               << (theCollection ?
+                   theCollection->getName()->getStringValue().c_str() :
+                   "NULL")
+               << std::endl);
+}
+
+
+/*******************************************************************************
+  The method is called from CollectionPul::finalizeUpdates()
+********************************************************************************/
+void CollectionPul::truncateIndexes()
+{
+  csize numTruncatedIndices = theTruncatedIndices.size();
+
+  for (csize idx = 0; idx < numTruncatedIndices; ++idx)
   {
     ValueIndex* index = static_cast<ValueIndex*>(theTruncatedIndices[idx]);
     index->clear();
   }
-
-  numIncrementalIndices = theIncrementalIndices.size();
-
-  for (csize idx = 0; idx < numIncrementalIndices; ++idx)
-  {
-    ValueIndex* index = static_cast<ValueIndex*>(theIncrementalIndices[idx]);
-
-    //
-    // Referesh the index w.r.t. modified docs.
-    //
-    ValueIndexCompareFunction keyCmp(index->getNumColumns(),
-                                     index->getTimezone(),
-                                     index->getCollations());
-    
-    store::IndexDelta& beforeDelta = theBeforeIndexDeltas[idx];
-    store::IndexDelta& afterDelta = theAfterIndexDeltas[idx];
-    store::IndexDelta& insertedDelta = theInsertedDocsIndexDeltas[idx];
-    store::IndexDelta& deletedDelta = theDeletedDocsIndexDeltas[idx];
-
-    store::IndexDelta::iterator beforeIte = beforeDelta.begin();
-    store::IndexDelta::iterator beforeEnd = beforeDelta.end();
-    store::IndexDelta::iterator afterIte = afterDelta.begin();
-    store::IndexDelta::iterator afterEnd = afterDelta.end();
-    store::IndexDelta::iterator insertedIte = insertedDelta.begin();
-    store::IndexDelta::iterator insertedEnd = insertedDelta.end();
-    store::IndexDelta::iterator deletedIte = deletedDelta.begin();
-    store::IndexDelta::iterator deletedEnd = deletedDelta.end();
-
-    while (beforeIte != beforeEnd && afterIte != afterEnd)
-    {
-      store::Item_t& beforeNode = (*beforeIte).first;
-      store::Item_t& afterNode = (*afterIte).first;
-      store::IndexKey* beforeKey = (*beforeIte).second;
-      store::IndexKey*& afterKey = (*afterIte).second;
-
-      if (beforeNode == afterNode)
-      {
-        if (!keyCmp.equal(beforeKey, afterKey))
-        {
-          index->remove(beforeKey, beforeNode);
-          index->insert(afterKey, afterNode);
-        }
-
-        ++beforeIte;
-        ++afterIte;
-      }
-      else if (beforeNode < afterNode)
-      {
-        index->remove(beforeKey, beforeNode);
-        ++beforeIte;
-      }
-      else
-      {
-        index->insert(afterKey, afterNode);
-        ++afterIte;
-      }
-    }
-
-    while (beforeIte != beforeEnd)
-    {
-      index->remove((*beforeIte).second, (*beforeIte).first);
-      ++beforeIte;
-    }
-
-    while (afterIte != afterEnd)
-    {
-      index->insert((*afterIte).second, (*afterIte).first);
-      ++afterIte;
-    }
-
-    //
-    // Referesh the index w.r.t. newly inserted docs.
-    //
-    for (; insertedIte != insertedEnd; ++insertedIte)
-    {
-      index->insert((*insertedIte).second, (*insertedIte).first);
-    }
-
-    //
-    // Referesh the index w.r.t. deleted docs,
-    //
-    for (; deletedIte != deletedEnd; ++deletedIte)
-    {
-      index->remove((*deletedIte).second, (*deletedIte).first);
-    }
-  }
 }
 
 
@@ -2100,6 +2253,37 @@
 ********************************************************************************/
 void CollectionPul::applyUpdates()
 {
+  csize numIncrementalIndices = theIncrementalIndices.size();
+
+#if 0
+  if (theCollection != NULL)
+  {
+    std::cout << "applying PUL for collection " 
+              << theCollection->getName()->getStringValue() << std::endl;
+  }
+#endif
+
+  if (numIncrementalIndices > 0)
+  {
+    theBeforeIndexDeltas.resize(numIncrementalIndices);
+    theAfterIndexDeltas.resize(numIncrementalIndices);
+    theDeletedDocsIndexDeltas.resize(numIncrementalIndices);
+    theInsertedDocsIndexDeltas.resize(numIncrementalIndices);
+
+    theNumBeforeIndexDeltasApplied.resize(numIncrementalIndices);
+    theNumAfterIndexDeltasApplied.resize(numIncrementalIndices);
+    theNumInsertedDocsIndexDeltasApplied.resize(numIncrementalIndices);
+    theNumDeletedDocsIndexDeltasApplied.resize(numIncrementalIndices);
+
+    for (csize idx = 0; idx < numIncrementalIndices; ++idx)
+    {
+      theNumBeforeIndexDeltasApplied[idx] = 0;
+      theNumAfterIndexDeltasApplied[idx] = 0;
+      theNumInsertedDocsIndexDeltasApplied[idx] = 0;
+      theNumDeletedDocsIndexDeltasApplied[idx] = 0;
+    }
+  }
+
   // Don't apply anything if the collection is going to be deleted. 
   if (!theDeleteCollectionList.empty())
     return;
@@ -2214,6 +2398,71 @@
 #endif
     throw;
   }
+
+#if 0
+  if (theCollection != NULL)
+  {
+    std::cout << "applied PUL for collection " 
+              << theCollection->getName()->getStringValue() << std::endl << std::endl;
+  }
+#endif
+}
+
+
+/*******************************************************************************
+
+********************************************************************************/
+void CollectionPul::undoUpdates()
+{
+  if (!theIsApplied)
+    return;
+
+  try
+  {
+    undoList(theTruncateCollectionList);
+    undoList(theDeleteFromCollectionList);
+    undoList(theInsertIntoCollectionList);
+    undoList(theCreateCollectionList);
+
+#ifndef ZORBA_NO_XMLSCHEMA
+    // Undo validate-in-place validation
+    undoList(theRevalidateList);
+
+    // Undo apply-updates caused validation
+    if (theValidationPul)
+    {
+      undoList(static_cast<PULImpl *>(theValidationPul.getp())->theValidationList);
+    }
+#endif
+
+    // Undo text node merging
+    std::vector<TextNodeMerge>::reverse_iterator rit = theMergeList.rbegin();
+    std::vector<TextNodeMerge>::reverse_iterator rend = theMergeList.rend();
+    for (; rit != rend; ++rit)
+    {
+      TextNodeMerge merge = (*rit);
+      XmlNode* newTextNode = merge.theParent->getChild(merge.thePos);
+      ZORBA_ASSERT(newTextNode->getNodeKind()== store::StoreConsts::textNode);
+
+      newTextNode->detach();
+
+      for (csize j = 0; j < merge.theMergedNodes.size(); ++j)
+        merge.theMergedNodes[j]->connect(merge.theParent, merge.thePos + j);
+    }
+    theMergeList.clear();
+
+    undoList(theDeleteList);
+    undoList(theReplaceContentList);
+    undoList(theReplaceNodeList);
+    undoList(theInsertList);
+    undoList(theDoFirstList);
+
+    undoRefreshIndexes();
+  }
+  catch (...)
+  {
+    ZORBA_FATAL(0, "Unexpected error during pul undo");
+  }
 }
 
 
@@ -2228,9 +2477,7 @@
 {
   try
   {
-    // Refresh each incrementally maintained index using its before and after
-    // deltas. 
-    refreshIndices();
+    truncateIndexes();
 
     // If necessary, adjust the position of trees inside this collection.
     if (theAdjustTreePositions)
@@ -2241,9 +2488,8 @@
 
     // Detach nodes that were deleted from their trees due to replace-node,
     // replace-content, or delete-node XQUF primitives.
-    csize numUpdates;
+    csize numUpdates = theReplaceNodeList.size();
 
-    numUpdates = theReplaceNodeList.size();
     for (csize i = 0; i < numUpdates; ++i)
     {
       UpdatePrimitive* upd = theReplaceNodeList[i];
@@ -2318,61 +2564,6 @@
 }
 
 
-/*******************************************************************************
-
-********************************************************************************/
-void CollectionPul::undoUpdates()
-{
-  if (!theIsApplied)
-    return;
-
-  try
-  {
-    undoList(theTruncateCollectionList);
-    undoList(theDeleteFromCollectionList);
-    undoList(theInsertIntoCollectionList);
-    undoList(theCreateCollectionList);
-
-#ifndef ZORBA_NO_XMLSCHEMA
-    // Undo validate-in-place validation
-    undoList(theRevalidateList);
-
-    // Undo apply-updates caused validation
-    if (theValidationPul)
-    {
-      undoList(static_cast<PULImpl *>(theValidationPul.getp())->theValidationList);
-    }
-#endif
-
-    // Undo text node merging
-    std::vector<TextNodeMerge>::reverse_iterator rit = theMergeList.rbegin();
-    std::vector<TextNodeMerge>::reverse_iterator rend = theMergeList.rend();
-    for (; rit != rend; ++rit)
-    {
-      TextNodeMerge merge = (*rit);
-      XmlNode* newTextNode = merge.theParent->getChild(merge.thePos);
-      ZORBA_ASSERT(newTextNode->getNodeKind()== store::StoreConsts::textNode);
-
-      newTextNode->detach();
-
-      for (csize j = 0; j < merge.theMergedNodes.size(); ++j)
-        merge.theMergedNodes[j]->connect(merge.theParent, merge.thePos + j);
-    }
-    theMergeList.clear();
-
-    undoList(theDeleteList);
-    undoList(theReplaceContentList);
-    undoList(theReplaceNodeList);
-    undoList(theInsertList);
-    undoList(theDoFirstList);
-  }
-  catch (...)
-  {
-    ZORBA_FATAL(0, "Unexpected error during pul undo");
-  }
-}
-
-
 } // namespace simplestore
 } // namespace zorba
 /* vim:set et sw=2 ts=2: */

=== modified file 'src/store/naive/simple_pul.h'
--- src/store/naive/simple_pul.h	2012-04-24 12:39:38 +0000
+++ src/store/naive/simple_pul.h	2012-04-27 09:11:24 +0000
@@ -204,6 +204,11 @@
   std::vector<store::IndexDelta>     theInsertedDocsIndexDeltas;
   std::vector<store::IndexDelta>     theDeletedDocsIndexDeltas;
 
+  std::vector<csize>                 theNumBeforeIndexDeltasApplied;
+  std::vector<csize>                 theNumAfterIndexDeltasApplied;
+  std::vector<csize>                 theNumInsertedDocsIndexDeltasApplied;
+  std::vector<csize>                 theNumDeletedDocsIndexDeltasApplied;
+
 public:
   CollectionPul(PULImpl* pul, Collection* collection);
 
@@ -211,18 +216,18 @@
 
   void switchPul(PULImpl* pul);
 
+  void computeIndexBeforeDeltas();
+
+  void computeIndexAfterDeltas();
+
+  void refreshIndexes();
+
   void applyUpdates();
 
+  void undoUpdates();
+
   void finalizeUpdates();
 
-  void undoUpdates();
-
-  void computeIndexBeforeDeltas();
-
-  void computeIndexAfterDeltas();
-
-  void refreshIndices();
-
   void setAdjustTreePositions() { theAdjustTreePositions = true; }
 
   void addToCheckForMerge(InternalNode* parent) { theMergeToCheckSet.insert(parent); }
@@ -231,6 +236,12 @@
   void switchPulInPrimitivesList(std::vector<UpdatePrimitive*>& list);
 
   void computeIndexDeltas(std::vector<store::IndexDelta>& deltas);
+
+  void cleanIndexDeltas();
+
+  void truncateIndexes();
+
+  void undoRefreshIndexes();
 };
 
 
@@ -273,11 +284,14 @@
     UP_LIST_CREATE_INDEX
   };
 
-  typedef std::map<const QNameItem*, CollectionPul*> CollectionPulMap;
+  typedef std::vector<CollectionPul*> CollectionPuls;
+
+  typedef std::map<const QNameItem*, csize> CollectionPulMap;
 
 protected:
   // XQUF and collection primitives, grouped by the collection that is being updated.
-  CollectionPulMap                   theCollectionPuls;
+  CollectionPuls                     theCollectionPuls;
+  CollectionPulMap                   theCollectionPulsMap;
   CollectionPul                    * theNoCollectionPul;
   CollectionPul                    * theLastPul;
   const QNameItem                  * theLastCollection;

=== modified file 'src/store/naive/store.cpp'
--- src/store/naive/store.cpp	2012-04-18 11:16:33 +0000
+++ src/store/naive/store.cpp	2012-04-27 09:11:24 +0000
@@ -523,23 +523,24 @@
 
 ********************************************************************************/
 void Store::populateValueIndex(
-    const store::Index_t& aIndex,
-    store::Iterator* aSourceIter,
-    ulong aNumColumns)
+    const store::Index_t& idx,
+    store::Iterator* sourceIter,
+    ulong numColumns)
 {
-  if (!aSourceIter)
+  if (!sourceIter)
     return;
 
   store::Item_t domainItem;
   store::IndexKey* key = NULL;
-
-  ValueIndex* index = static_cast<ValueIndex*>(aIndex.getp());
-
-  aSourceIter->open();
+  store::IndexKey* key2 = NULL;
+
+  ValueIndex* index = static_cast<ValueIndex*>(idx.getp());
+
+  sourceIter->open();
 
   try
   {
-    while (aSourceIter->next(domainItem))
+    while (sourceIter->next(domainItem))
     {
       if (domainItem->isNode() &&
           domainItem->getCollection() == NULL &&
@@ -549,12 +550,12 @@
         ERROR_PARAMS(index->getName()->getStringValue()));
       }
 
-      if (key == NULL)
-        key = new store::IndexKey(aNumColumns);
+      if (key2 == key)
+        key = new store::IndexKey(numColumns);
 
-      for (ulong i = 0; i < aNumColumns; ++i)
+      for (ulong i = 0; i < numColumns; ++i)
       {
-        if (!aSourceIter->next((*key)[i]))
+        if (!sourceIter->next((*key)[i]))
         {
           // The source iter is a ValueIndexEntryBuilderIterator, whose next()
           // method is guaranteed to return true exactly once. The result from
@@ -564,7 +565,8 @@
         }
       }
 
-      index->insert(key, domainItem);
+      key2 = key;
+      index->insert(key2, domainItem);
     }
   }
   catch(...)
@@ -572,14 +574,11 @@
     if (key != NULL)
       delete key;
 
-    aSourceIter->close();
+    sourceIter->close();
     throw;
   }
 
-  if (key != NULL)
-    delete key;
-
-  aSourceIter->close();
+  sourceIter->close();
 }
 
 

=== modified file 'src/store/naive/store_defs.h'
--- src/store/naive/store_defs.h	2012-04-24 12:39:38 +0000
+++ src/store/naive/store_defs.h	2012-04-27 09:11:24 +0000
@@ -55,6 +55,28 @@
 
 #define COMMENT_NODE(item) (reinterpret_cast<CommentNode*>((item).getp()))
 
+
+#ifndef NDEBUG
+
+#define STORE_TRACE(level, msg)               \
+{                                             \
+  if (level <= GET_STORE().getTraceLevel())   \
+    std::cout << msg << std::endl;            \
+}
+
+#define STORE_TRACE1(msg) STORE_TRACE(1, msg);
+#define STORE_TRACE2(msg) STORE_TRACE(2, msg);
+#define STORE_TRACE3(msg) STORE_TRACE(3, msg);
+
+#else
+
+#define STORE_TRACE(msg)
+#define STORE_TRACE1(msg)
+#define STORE_TRACE2(msg)
+#define STORE_TRACE3(msg)
+
+#endif
+
 }
 }
 #endif

=== added file 'test/rbkt/ExpQueryResults/zorba/index/delete_from_collection_01.xml.res'
--- test/rbkt/ExpQueryResults/zorba/index/delete_from_collection_01.xml.res	1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/index/delete_from_collection_01.xml.res	2012-04-27 09:11:24 +0000
@@ -0,0 +1,1 @@
+<foo uri="1"/>

=== added file 'test/rbkt/ExpQueryResults/zorba/index/undo2.xml.res'
--- test/rbkt/ExpQueryResults/zorba/index/undo2.xml.res	1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/index/undo2.xml.res	2012-04-27 09:11:24 +0000
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+zerr:ZDDY0024<person id="1" key="5"/><person id="2" key="5"/><person id="1" key="5"/><person id="2" key="5"/>

=== added file 'test/rbkt/ExpQueryResults/zorba/index/undo3.xml.res'
--- test/rbkt/ExpQueryResults/zorba/index/undo3.xml.res	1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/index/undo3.xml.res	2012-04-27 09:11:24 +0000
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+zerr:ZDDY0024 
+<person id="1" key="5"/><person id="2" key="5"/>
+ 
+ 
+<person id="1" key="5"/><person id="2" key="5"/>

=== added file 'test/rbkt/ExpQueryResults/zorba/index/unique.xml.res'
--- test/rbkt/ExpQueryResults/zorba/index/unique.xml.res	1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/index/unique.xml.res	2012-04-27 09:11:24 +0000
@@ -0,0 +1,1 @@
+zerr:ZDDY0024 zerr:ZDDY0024<person id="5"/><person id="5"/>

=== added file 'test/rbkt/Queries/zorba/index/delete_from_collection_01.xq'
--- test/rbkt/Queries/zorba/index/delete_from_collection_01.xq	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/index/delete_from_collection_01.xq	2012-04-27 09:11:24 +0000
@@ -0,0 +1,13 @@
+
+import module namespace seq = "http://www.foo.com/default"; at "delete_from_collection_01.xqlib";
+
+seq:init();
+
+variable $foo := seq:index();
+
+seq:reset();
+
+variable $bar := seq:index();
+
+
+$foo, $bar

=== added file 'test/rbkt/Queries/zorba/index/delete_from_collection_01.xqlib'
--- test/rbkt/Queries/zorba/index/delete_from_collection_01.xqlib	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/index/delete_from_collection_01.xqlib	2012-04-27 09:11:24 +0000
@@ -0,0 +1,42 @@
+module namespace seq = "http://www.foo.com/default";;
+
+import module namespace ddl = "http://www.zorba-xquery.com/modules/store/static/collections/ddl";;     
+
+import module namespace dml = "http://www.zorba-xquery.com/modules/store/static/collections/dml";;     
+
+import module namespace iddl = "http://www.zorba-xquery.com/modules/store/static/indexes/ddl";;        
+
+import module namespace idml = "http://www.zorba-xquery.com/modules/store/static/indexes/dml";;        
+
+declare namespace an = "http://www.zorba-xquery.com/annotations";;
+
+
+declare collection seq:counters as node()*;
+
+
+declare %an:automatic %an:unique %an:value-equality index seq:counters-by-uri
+  on nodes dml:collection(xs:QName("seq:counters"))
+  by xs:string(./@uri) as xs:string;
+
+
+declare %an:sequential function seq:init()
+{
+ ddl:create(xs:QName("seq:counters"));
+ iddl:create(xs:QName("seq:counters-by-uri"));
+ dml:insert-nodes(xs:QName("seq:counters"), <foo uri="1"/>);
+};
+
+
+declare function seq:index ()
+{
+  idml:probe-index-point-value(xs:QName("seq:counters-by-uri"), "1")
+};
+
+
+declare %an:sequential function seq:reset ()
+{
+  dml:delete-nodes(idml:probe-index-point-value(xs:QName("seq:counters-by-uri"), "1"));
+};
+
+
+

=== added file 'test/rbkt/Queries/zorba/index/undo2.xq'
--- test/rbkt/Queries/zorba/index/undo2.xq	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/index/undo2.xq	2012-04-27 09:11:24 +0000
@@ -0,0 +1,43 @@
+import module namespace u = "http://www.zorba-xquery.com/unique-index"; at "undo2.xqlib";
+
+import module namespace dml = "http://www.zorba-xquery.com/modules/store/static/collections/dml";;
+import module namespace idml = "http://www.zorba-xquery.com/modules/store/static/indexes/dml";;
+
+declare namespace zerr = "http://www.zorba-xquery.com/errors";;
+declare namespace err = "http://www.w3.org/2005/xqt-errors";;
+
+u:create-db();
+
+dml:insert-nodes($u:auctions1, <person id="1" key="5"/>);
+dml:insert-nodes($u:auctions1, <person id="2" key="5"/>);
+
+try
+{{
+  (
+   dml:insert-nodes($u:auctions1, <person id="3" key="1"/>),
+
+   insert node <foo/> into dml:collection($u:auctions1)[1],
+
+   dml:insert-nodes($u:auctions1, <person id="4" key="5"/>),
+
+   replace value of node dml:collection($u:auctions1)[1]/@key with "1",
+
+   dml:insert-nodes($u:auctions2, <person id="1"/>),
+   dml:insert-nodes($u:auctions2, <person id="1"/>));
+  ()
+}}
+catch * 
+{
+  $err:code
+}
+,
+
+dml:collection($u:auctions1)
+,
+idml:probe-index-point-value($u:PersonId1, "1")
+,
+idml:probe-index-point-value($u:PersonId1, "3")
+,
+idml:probe-index-point-value($u:PersonId1, "5")
+,
+dml:collection($u:auctions2)

=== added file 'test/rbkt/Queries/zorba/index/undo2.xqlib'
--- test/rbkt/Queries/zorba/index/undo2.xqlib	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/index/undo2.xqlib	2012-04-27 09:11:24 +0000
@@ -0,0 +1,36 @@
+module namespace auctions = "http://www.zorba-xquery.com/unique-index";;
+
+import module namespace ddl = "http://www.zorba-xquery.com/modules/store/static/collections/ddl";;
+import module namespace iddl = "http://www.zorba-xquery.com/modules/store/static/indexes/ddl";;
+import module namespace dml = "http://www.zorba-xquery.com/modules/store/static/collections/dml";;
+import module namespace idml = "http://www.zorba-xquery.com/modules/store/static/indexes/dml";;
+
+declare namespace an = "http://www.zorba-xquery.com/annotations";;
+
+declare variable $auctions:auctions1 := xs:QName("auctions:auctions1");
+declare variable $auctions:PersonId1 := xs:QName("auctions:PersonId1");
+declare variable $auctions:auctions2 := xs:QName("auctions:auctions2");
+declare variable $auctions:PersonId2 := xs:QName("auctions:PersonId2");
+
+declare %an:ordered collection auctions:auctions1 as node()*;
+
+declare %an:automatic %an:value-equality index auctions:PersonId1
+on nodes dml:collection(xs:QName("auctions:auctions1"))
+by xs:string(./@key) as xs:string;
+
+declare %an:ordered collection auctions:auctions2 as node()*;
+
+declare %an:unique %an:automatic %an:value-range index auctions:PersonId2
+on nodes dml:collection(xs:QName("auctions:auctions2"))
+by xs:string(./@id) as xs:string;
+
+declare %an:sequential function auctions:create-db()
+{
+  ddl:create($auctions:auctions1);
+
+  ddl:create($auctions:auctions2);
+
+  iddl:create($auctions:PersonId1);
+
+  iddl:create($auctions:PersonId2);
+};

=== added file 'test/rbkt/Queries/zorba/index/undo3.xq'
--- test/rbkt/Queries/zorba/index/undo3.xq	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/index/undo3.xq	2012-04-27 09:11:24 +0000
@@ -0,0 +1,59 @@
+import module namespace u = "http://www.zorba-xquery.com/unique-index"; at "undo2.xqlib";
+
+import module namespace dml = "http://www.zorba-xquery.com/modules/store/static/collections/dml";;
+import module namespace idml = "http://www.zorba-xquery.com/modules/store/static/indexes/dml";;
+
+declare namespace zerr = "http://www.zorba-xquery.com/errors";;
+declare namespace err = "http://www.w3.org/2005/xqt-errors";;
+
+u:create-db();
+
+dml:insert-nodes($u:auctions1, <person id="1" key="5"/>);
+dml:insert-nodes($u:auctions1, <person id="2" key="5"/>);
+
+try
+{{
+  (
+   dml:insert-nodes($u:auctions1, <person id="3" key="1"/>),
+
+   insert node <foo/> into dml:collection($u:auctions1)[1],
+
+   dml:insert-nodes($u:auctions1, <person id="4" key="5"/>),
+
+   replace value of node dml:collection($u:auctions1)[1]/@key with "1",
+
+   dml:delete-nodes-first($u:auctions1, 1),
+
+   dml:insert-nodes($u:auctions2, <person id="1"/>),
+   dml:insert-nodes($u:auctions2, <person id="1"/>));
+  ()
+}}
+catch * 
+{
+  $err:code
+}
+,
+"
+"
+,
+dml:collection($u:auctions1)
+,
+"
+"
+,
+idml:probe-index-point-value($u:PersonId1, "1")
+,
+"
+"
+,
+idml:probe-index-point-value($u:PersonId1, "3")
+,
+"
+"
+,
+idml:probe-index-point-value($u:PersonId1, "5")
+,
+"
+"
+,
+dml:collection($u:auctions2)

=== added file 'test/rbkt/Queries/zorba/index/unique.xq'
--- test/rbkt/Queries/zorba/index/unique.xq	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/index/unique.xq	2012-04-27 09:11:24 +0000
@@ -0,0 +1,50 @@
+import module namespace u = "http://www.zorba-xquery.com/unique-index"; at "unique.xqlib";
+
+import module namespace dml = "http://www.zorba-xquery.com/modules/store/static/collections/dml";;
+import module namespace idml = "http://www.zorba-xquery.com/modules/store/static/indexes/dml";;
+
+declare namespace zerr = "http://www.zorba-xquery.com/errors";;
+declare namespace err = "http://www.w3.org/2005/xqt-errors";;
+
+u:create-db();
+
+dml:insert-nodes($u:auctions1, <person id="5"/>);
+
+try
+{{
+  (dml:insert-nodes($u:auctions1, <person id="1"/>),
+   dml:insert-nodes($u:auctions1, <person id="1"/>));
+  ()
+}}
+catch * 
+{
+  $err:code
+}
+,
+
+try
+{{
+  (
+   dml:insert-nodes($u:auctions1, <person id="1"/>),
+   dml:insert-nodes($u:auctions1, <person id="3"/>),
+   dml:delete-nodes-first($u:auctions1, 1),
+
+   dml:insert-nodes($u:auctions2, <person id="1"/>),
+   dml:insert-nodes($u:auctions2, <person id="1"/>));
+  ()
+}}
+catch * 
+{
+  $err:code
+}
+,
+
+dml:collection($u:auctions1)
+,
+idml:probe-index-point-value($u:PersonId1, "1")
+,
+idml:probe-index-point-value($u:PersonId1, "3")
+,
+idml:probe-index-point-value($u:PersonId1, "5")
+,
+dml:collection($u:auctions2)

=== added file 'test/rbkt/Queries/zorba/index/unique.xqlib'
--- test/rbkt/Queries/zorba/index/unique.xqlib	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/index/unique.xqlib	2012-04-27 09:11:24 +0000
@@ -0,0 +1,37 @@
+module namespace auctions = "http://www.zorba-xquery.com/unique-index";;
+
+import module namespace ddl = "http://www.zorba-xquery.com/modules/store/static/collections/ddl";;
+import module namespace iddl = "http://www.zorba-xquery.com/modules/store/static/indexes/ddl";;
+import module namespace dml = "http://www.zorba-xquery.com/modules/store/static/collections/dml";;
+import module namespace idml = "http://www.zorba-xquery.com/modules/store/static/indexes/dml";;
+
+declare namespace an = "http://www.zorba-xquery.com/annotations";;
+
+declare variable $auctions:auctions1 := xs:QName("auctions:auctions1");
+declare variable $auctions:PersonId1 := xs:QName("auctions:PersonId1");
+declare variable $auctions:auctions2 := xs:QName("auctions:auctions2");
+declare variable $auctions:PersonId2 := xs:QName("auctions:PersonId2");
+
+declare %an:ordered collection auctions:auctions1 as node()*;
+
+declare %an:unique %an:automatic %an:value-equality index auctions:PersonId1
+on nodes dml:collection(xs:QName("auctions:auctions1"))
+by xs:string(./@id) as xs:string;
+
+
+declare %an:ordered collection auctions:auctions2 as node()*;
+
+declare %an:unique %an:automatic %an:value-range index auctions:PersonId2
+on nodes dml:collection(xs:QName("auctions:auctions2"))
+by xs:string(./@id) as xs:string;
+
+declare %an:sequential function auctions:create-db()
+{
+  ddl:create($auctions:auctions1);
+
+  ddl:create($auctions:auctions2);
+
+  iddl:create($auctions:PersonId1);
+
+  iddl:create($auctions:PersonId2);
+};


Follow ups