Description: Increment cursor version of cancel or reopen
 Potentially increment the cursor version on cancel() or when the database is
 reopened, and flag the current cursor version as used when a cursor is
 rebuilt.
 .
 Fixes database corruption issues with certain usage patterns, which recoll
 can trigger.
Author: Olly Betts <olly@survex.com>
Origin: upstream, https://trac.xapian.org/changeset/826d1a19cc356e7bf66c1681626e70af32967447/git and https://trac.xapian.org/changeset/d784290ce015958474f965817f7a41f1483c3e03/git
Bug: https://trac.xapian.org/ticket/675
Bug-Debian: https://bugs.debian.org/808610
Forwarded: https://trac.xapian.org/ticket/675
Last-Update: 2016-04-19

--- a/backends/brass/brass_cursor.cc
+++ b/backends/brass/brass_cursor.cc
@@ -1,7 +1,7 @@
 /* brass_cursor.cc: Btree cursor implementation
  *
  * Copyright 1999,2000,2001 BrightStation PLC
- * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2012 Olly Betts
+ * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2012,2015 Olly Betts
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -99,6 +99,7 @@
     C[level].n = B->C[level].n;
     C[level].p = B->C[level].p;
     version = B->cursor_version;
+    B->cursor_created_since_last_modification = true;
 }
 
 BrassCursor::~BrassCursor()
--- a/backends/brass/brass_table.cc
+++ b/backends/brass/brass_table.cc
@@ -1435,6 +1435,11 @@
 
     base_letter = ch;
 
+    if (cursor_created_since_last_modification) {
+	cursor_created_since_last_modification = false;
+	++cursor_version;
+    }
+
     /* ready to open the main file */
 
     RETURN(true);
@@ -1975,6 +1980,11 @@
     changed_n = 0;
     changed_c = DIR_START;
     seq_count = SEQ_START_POINT;
+
+    if (cursor_created_since_last_modification) {
+	cursor_created_since_last_modification = false;
+	++cursor_version;
+    }
 }
 
 /************ B-tree reading ************/
--- a/backends/chert/chert_cursor.cc
+++ b/backends/chert/chert_cursor.cc
@@ -1,7 +1,7 @@
 /* chert_cursor.cc: Btree cursor implementation
  *
  * Copyright 1999,2000,2001 BrightStation PLC
- * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2012 Olly Betts
+ * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2012,2015 Olly Betts
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -97,6 +97,7 @@
     C[level].n = B->C[level].n;
     C[level].p = B->C[level].p;
     version = B->cursor_version;
+    B->cursor_created_since_last_modification = true;
 }
 
 ChertCursor::~ChertCursor()
--- a/backends/chert/chert_table.cc
+++ b/backends/chert/chert_table.cc
@@ -1438,6 +1438,11 @@
 
     base_letter = ch;
 
+    if (cursor_created_since_last_modification) {
+	cursor_created_since_last_modification = false;
+	++cursor_version;
+    }
+
     /* ready to open the main file */
 
     RETURN(true);
@@ -1994,6 +1999,11 @@
     changed_n = 0;
     changed_c = DIR_START;
     seq_count = SEQ_START_POINT;
+
+    if (cursor_created_since_last_modification) {
+	cursor_created_since_last_modification = false;
+	++cursor_version;
+    }
 }
 
 /************ B-tree reading ************/
--- a/backends/flint/flint_cursor.cc
+++ b/backends/flint/flint_cursor.cc
@@ -1,7 +1,7 @@
 /* flint_cursor.cc: Btree cursor implementation
  *
  * Copyright 1999,2000,2001 BrightStation PLC
- * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2012 Olly Betts
+ * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2012,2015 Olly Betts
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -97,6 +97,7 @@
     C[level].n = B->C[level].n;
     C[level].p = B->C[level].p;
     version = B->cursor_version;
+    B->cursor_created_since_last_modification = true;
 }
 
 FlintCursor::~FlintCursor()
--- a/backends/flint/flint_table.cc
+++ b/backends/flint/flint_table.cc
@@ -1427,6 +1427,11 @@
 
     base_letter = ch;
 
+    if (cursor_created_since_last_modification) {
+	cursor_created_since_last_modification = false;
+	++cursor_version;
+    }
+
     /* ready to open the main file */
 
     return true;
@@ -1976,6 +1981,11 @@
     changed_n = 0;
     changed_c = DIR_START;
     seq_count = SEQ_START_POINT;
+
+    if (cursor_created_since_last_modification) {
+	cursor_created_since_last_modification = false;
+	++cursor_version;
+    }
 }
 
 /************ B-tree reading ************/
--- a/tests/api_backend.cc
+++ b/tests/api_backend.cc
@@ -788,3 +788,45 @@
 		   Xapian::Auto::open_stub("nosuchdirectory", Xapian::DB_OPEN));
     return true;
 }
+
+/// Regression test for #675, fixed in 1.3.3 and 1.2.21.
+DEFINE_TESTCASE(cursorbug1, brass || chert || flint) {
+    Xapian::WritableDatabase wdb = get_writable_database();
+    Xapian::Database db = get_writable_database_as_database();
+    Xapian::Enquire enq(db);
+    enq.set_query(Xapian::Query::MatchAll);
+    Xapian::MSet mset;
+    // The original problem triggers for chert and glass on repeat==7.
+    for (int repeat = 0; repeat < 10; ++repeat) {
+	tout.str(string());
+	tout << "iteration #" << repeat << endl;
+
+	const int ITEMS = 10;
+	int free_id = db.get_doccount();
+	int offset = max(free_id, ITEMS * 2) - (ITEMS * 2);
+	int limit = offset + (ITEMS * 2);
+
+	mset = enq.get_mset(offset, limit);
+	for (Xapian::MSetIterator m1 = mset.begin(); m1 != mset.end(); ++m1) {
+	    (void)m1.get_document().get_value(0);
+	}
+
+	for (int i = free_id; i <= free_id + ITEMS; ++i) {
+	    Xapian::Document doc;
+	    const string & id = str(i);
+	    string qterm = "Q" + id;
+	    doc.add_value(0, id);
+	    doc.add_boolean_term(qterm);
+	    wdb.replace_document(qterm, doc);
+	}
+	wdb.commit();
+
+	db.reopen();
+	mset = enq.get_mset(offset, limit);
+	for (Xapian::MSetIterator m2 = mset.begin(); m2 != mset.end(); ++m2) {
+	    (void)m2.get_document().get_value(0);
+	}
+    }
+
+    return true;
+}
