[Zodb-checkins] CVS: Zope3/src/zodb/btrees - BTreeTemplate.c:1.9 interfaces.py:1.9

Tim Peters tim.one@comcast.net
Tue, 4 Mar 2003 16:07:11 -0500


Update of /cvs-repository/Zope3/src/zodb/btrees
In directory cvs.zope.org:/tmp/cvs-serv8392/src/zodb/btrees

Modified Files:
	BTreeTemplate.c interfaces.py 
Log Message:
A joint production from Jeremy and Tim.

BTree__p_resolveConflict():  Fixed a bug unique to ZODB4.  We can't
resolve a BTree node conflict unless it's the special case of a BTree
with a single bucket.  Turns out the test suite didn't provoke any
kind of BTree conflict other than the special case, but one of Tim's
"stress test" programs did, and when the input wasn't the single-
bucket special case the code raised TypeError instead of ConflictError.
It raises ConflictError now (as it should), but raises TypeError for
"impossible" inputs (corrupt or hostile state).

The new test testCantResolveBTreeConflict() ensures that the non-special
case is provoked and handled correctly.


=== Zope3/src/zodb/btrees/BTreeTemplate.c 1.8 => 1.9 ===
--- Zope3/src/zodb/btrees/BTreeTemplate.c:1.8	Sat Feb 22 01:00:17 2003
+++ Zope3/src/zodb/btrees/BTreeTemplate.c	Tue Mar  4 16:06:39 2003
@@ -1049,74 +1049,97 @@
 
 #ifdef PERSISTENT
 
-/* Return the caar of a tuple, where caar means t[0][0].
-   If t or t[0] is None, return None.
-   If caar is not None or a tuple, raise TypeError and return NULL.
-   Otherwise, raise TypeError and return NULL.
-
-   Always returns a borrowed reference.
-
-   Purpose: The state of a BTree should be a deeply nested tuple or
-   None.  Extract the caar and make sure it has the right type.
-*/
-
+/* Recognize the special cases of a BTree that's empty or contains a single
+ * bucket.  In the former case, return a borrowed reference to Py_None.
+ * In this single-bucket case, the bucket state is embedded directly in the
+ * BTree state, like so:
+ *
+ *     (
+ *         (
+ *              thebucket.__getstate__(),
+ *         ),
+ *     )
+ *
+ * When this obtains, return a borrowed reference to thebucket.__getstate__().
+ * Else return NULL with an exception set.  The exception should always be
+ * ConflictError then, but may be TypeError if the state makes no sense at all
+ * for a BTree (corrupted or hostile state).
+ */
 PyObject *
-caar(PyObject *t)
+get_bucket_state(PyObject *t)
 {
-    int i;
+	if (t == Py_None)
+		return Py_None;		/* an empty BTree */
+	if (! PyTuple_Check(t)) {
+		PyErr_SetString(PyExc_TypeError,
+			"_p_resolveConflict: expected tuple or None for state");
+		return NULL;
+	}
 
-    if (t == Py_None)
-	return Py_None;
-    if (!PyTuple_Check(t)) {
-	PyErr_SetString(PyExc_TypeError,
-		"_p_resolveConflict: expected tuple or None for state");
-	return NULL;
-    }
+	if (PyTuple_GET_SIZE(t) == 2) {
+		/* A non-degenerate BTree. */
+		return merge_error(-1, -1, -1, 11);
+	}
+
+	/* We're in the one-bucket case. */
+
+	if (PyTuple_GET_SIZE(t) != 1) {
+		PyErr_SetString(PyExc_TypeError,
+			"_p_resolveConflict: expected 1- or 2-tuple for state");
+		return NULL;
+	}
 
-    for (i = 0; i < 2; i++) {
 	t = PyTuple_GET_ITEM(t, 0);
-	if (t == Py_None)
-	    return Py_None;
-	if (!PyTuple_Check(t)) {
-	    PyErr_SetString(PyExc_TypeError,
-		"_p_resolveConflict: expected state to contain tuple or None");
-	    return NULL;
+	if (! PyTuple_Check(t) || PyTuple_GET_SIZE(t) != 1) {
+		PyErr_SetString(PyExc_TypeError,
+			"_p_resolveConflict: expected 1-tuple containing "
+			"bucket state");
+		return NULL;
+	}
+
+	t = PyTuple_GET_ITEM(t, 0);
+	if (! PyTuple_Check(t)) {
+		PyErr_SetString(PyExc_TypeError,
+			"_p_resolveConflict: expected tuple for bucket state");
+		return NULL;
 	}
-    }
 
-    return t;
+	return t;
 }
 
+/* Tricky.  The only kind of BTree conflict we can actually potentially
+ * resolve is the special case of a BTree containing a single bucket,
+ * in which case this becomes a fancy way of calling the bucket conflict
+ * resolution code.
+ */
 static PyObject *
 BTree__p_resolveConflict(BTree *self, PyObject *args)
 {
-    PyObject *s[3], *r, *t;
+    PyObject *s[3];
+    PyObject *x, *y, *z;
 
-    if (!PyArg_ParseTuple(args, "OOO", s, s+1, s+2))
+    if (!PyArg_ParseTuple(args, "OOO", &x, &y, &z))
 	return NULL;
 
-    s[0] = caar(s[0]);
+    s[0] = get_bucket_state(x);
     if (s[0] == NULL)
 	return NULL;
-    s[1] = caar(s[1]);
+    s[1] = get_bucket_state(y);
     if (s[1] == NULL)
 	return NULL;
-    s[2] = caar(s[2]);
+    s[2] = get_bucket_state(z);
     if (s[2] == NULL)
 	return NULL;
 
-
     if (PyObject_IsInstance((PyObject *)self, (PyObject *)&BTreeType))
-	r = _bucket__p_resolveConflict(OBJECT(&BucketType), s);
+	x = _bucket__p_resolveConflict(OBJECT(&BucketType), s);
     else
-	r = _bucket__p_resolveConflict(OBJECT(&SetType), s);
+	x = _bucket__p_resolveConflict(OBJECT(&SetType), s);
 
-    if (r == NULL)
+    if (x == NULL)
 	return NULL;
 
-    t = Py_BuildValue("((O))", r);
-    Py_DECREF(r);
-    return t;
+    return Py_BuildValue("((N))", x);
 }
 #endif
 


=== Zope3/src/zodb/btrees/interfaces.py 1.8 => 1.9 ===
--- Zope3/src/zodb/btrees/interfaces.py:1.8	Fri Feb 21 12:33:01 2003
+++ Zope3/src/zodb/btrees/interfaces.py	Tue Mar  4 16:06:39 2003
@@ -55,6 +55,9 @@
             # enough info to unlink an empty bucket from its containing
             # BTree correctly
             'Empty bucket from deleting all keys',
+
+            # 11; conflicting changes in an internal BTree node
+            'Conflicting changes in an internal BTree node',
             ]
 
     def __init__(self, p1, p2, p3, reason):