]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
ldb:tests: make api_search module
authorDouglas Bagnall <douglas.bagnall@catalyst.net.nz>
Sat, 17 Aug 2024 02:41:18 +0000 (14:41 +1200)
committerAndreas Schneider <asn@cryptomilk.org>
Tue, 24 Sep 2024 09:14:38 +0000 (09:14 +0000)
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andreas Schneider <asn@samba.org>
lib/ldb/tests/python/api.py
lib/ldb/tests/python/api_search.py [new file with mode: 0644]
selftest/tests.py

index 6ab12c64f49714f951d3b57d8760b597bae1b91a..802ce437f0e2057fe6c97503f44598f0f20235d8 100755 (executable)
@@ -6,8 +6,6 @@ import os
 from unittest import TestCase
 import sys
 sys.path.insert(0, "bin/python")
-import gc
-import time
 import ldb
 import shutil
 import errno
@@ -792,1326 +790,6 @@ class SimpleLdbNoLmdb(LdbBaseTest):
             self.assertEqual(enum, ldb.ERR_OTHER)
 
 
-class SearchTests(LdbBaseTest):
-    def tearDown(self):
-        shutil.rmtree(self.testdir)
-        super().tearDown()
-
-        # Ensure the LDB is closed now, so we close the FD
-        del(self.l)
-
-    def setUp(self):
-        super().setUp()
-        self.testdir = tempdir()
-        self.filename = os.path.join(self.testdir, "search_test.ldb")
-        options = ["modules:rdn_name"]
-        if hasattr(self, 'IDXCHECK'):
-            options.append("disable_full_db_scan_for_self_test:1")
-        self.l = ldb.Ldb(self.url(),
-                         flags=self.flags(),
-                         options=options)
-        try:
-            self.l.add(self.index)
-        except AttributeError:
-            pass
-
-        self.l.add({"dn": "@ATTRIBUTES",
-                    "DC": "CASE_INSENSITIVE"})
-
-        # Note that we can't use the name objectGUID here, as we
-        # want to stay clear of the objectGUID handler in LDB and
-        # instead use just the 16 bytes raw, which we just keep
-        # to printable chars here for ease of handling.
-
-        self.l.add({"dn": "DC=ORG",
-                    "name": b"org",
-                    "objectUUID": b"0000000000abcdef"})
-        self.l.add({"dn": "DC=EXAMPLE,DC=ORG",
-                    "name": b"org",
-                    "objectUUID": b"0000000001abcdef"})
-        self.l.add({"dn": "OU=OU1,DC=EXAMPLE,DC=ORG",
-                    "name": b"OU #1",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0023456789abcde3"})
-        self.l.add({"dn": "OU=OU2,DC=EXAMPLE,DC=ORG",
-                    "name": b"OU #2",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0023456789abcde4"})
-        self.l.add({"dn": "OU=OU3,DC=EXAMPLE,DC=ORG",
-                    "name": b"OU #3",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0023456789abcde5"})
-        self.l.add({"dn": "OU=OU4,DC=EXAMPLE,DC=ORG",
-                    "name": b"OU #4",
-                    "x": "z", "y": "b",
-                    "objectUUID": b"0023456789abcde6"})
-        self.l.add({"dn": "OU=OU5,DC=EXAMPLE,DC=ORG",
-                    "name": b"OU #5",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0023456789abcde7"})
-        self.l.add({"dn": "OU=OU6,DC=EXAMPLE,DC=ORG",
-                    "name": b"OU #6",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0023456789abcde8"})
-        self.l.add({"dn": "OU=OU7,DC=EXAMPLE,DC=ORG",
-                    "name": b"OU #7",
-                    "x": "y", "y": "c",
-                    "objectUUID": b"0023456789abcde9"})
-        self.l.add({"dn": "OU=OU8,DC=EXAMPLE,DC=ORG",
-                    "name": b"OU #8",
-                    "x": "y", "y": "b",
-                    "objectUUID": b"0023456789abcde0"})
-        self.l.add({"dn": "OU=OU9,DC=EXAMPLE,DC=ORG",
-                    "name": b"OU #9",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0023456789abcdea"})
-
-        self.l.add({"dn": "DC=EXAMPLE,DC=COM",
-                    "name": b"org",
-                    "objectUUID": b"0000000011abcdef"})
-
-        self.l.add({"dn": "DC=EXAMPLE,DC=NET",
-                    "name": b"org",
-                    "objectUUID": b"0000000021abcdef"})
-
-        self.l.add({"dn": "OU=UNIQUE,DC=EXAMPLE,DC=NET",
-                    "objectUUID": b"0000000022abcdef"})
-
-        self.l.add({"dn": "DC=SAMBA,DC=ORG",
-                    "name": b"samba.org",
-                    "objectUUID": b"0123456789abcdef"})
-        self.l.add({"dn": "OU=ADMIN,DC=SAMBA,DC=ORG",
-                    "name": b"Admins",
-                    "x": "z", "y": "a",
-                    "objectUUID": b"0123456789abcde1"})
-        self.l.add({"dn": "OU=USERS,DC=SAMBA,DC=ORG",
-                    "name": b"Users",
-                    "x": "z", "y": "a",
-                    "objectUUID": b"0123456789abcde2"})
-        self.l.add({"dn": "OU=OU1,DC=SAMBA,DC=ORG",
-                    "name": b"OU #1",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0123456789abcde3"})
-        self.l.add({"dn": "OU=OU2,DC=SAMBA,DC=ORG",
-                    "name": b"OU #2",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0123456789abcde4"})
-        self.l.add({"dn": "OU=OU3,DC=SAMBA,DC=ORG",
-                    "name": b"OU #3",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0123456789abcde5"})
-        self.l.add({"dn": "OU=OU4,DC=SAMBA,DC=ORG",
-                    "name": b"OU #4",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0123456789abcde6"})
-        self.l.add({"dn": "OU=OU5,DC=SAMBA,DC=ORG",
-                    "name": b"OU #5",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0123456789abcde7"})
-        self.l.add({"dn": "OU=OU6,DC=SAMBA,DC=ORG",
-                    "name": b"OU #6",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0123456789abcde8"})
-        self.l.add({"dn": "OU=OU7,DC=SAMBA,DC=ORG",
-                    "name": b"OU #7",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0123456789abcde9"})
-        self.l.add({"dn": "OU=OU8,DC=SAMBA,DC=ORG",
-                    "name": b"OU #8",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0123456789abcde0"})
-        self.l.add({"dn": "OU=OU9,DC=SAMBA,DC=ORG",
-                    "name": b"OU #9",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0123456789abcdea"})
-        self.l.add({"dn": "OU=OU10,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0123456789abcdeb"})
-        self.l.add({"dn": "OU=OU11,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "y", "y": "a",
-                    "objectUUID": b"0123456789abcdec"})
-        self.l.add({"dn": "OU=OU12,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "y", "y": "b",
-                    "objectUUID": b"0123456789abcded"})
-        self.l.add({"dn": "OU=OU13,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "x", "y": "b",
-                    "objectUUID": b"0123456789abcdee"})
-        self.l.add({"dn": "OU=OU14,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "x", "y": "b",
-                    "objectUUID": b"0123456789abcd01"})
-        self.l.add({"dn": "OU=OU15,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "x", "y": "b",
-                    "objectUUID": b"0123456789abcd02"})
-        self.l.add({"dn": "OU=OU16,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "x", "y": "b",
-                    "objectUUID": b"0123456789abcd03"})
-        self.l.add({"dn": "OU=OU17,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "x", "y": "b",
-                    "objectUUID": b"0123456789abcd04"})
-        self.l.add({"dn": "OU=OU18,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "x", "y": "b",
-                    "objectUUID": b"0123456789abcd05"})
-        self.l.add({"dn": "OU=OU19,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "x", "y": "b",
-                    "objectUUID": b"0123456789abcd06"})
-        self.l.add({"dn": "OU=OU20,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "x", "y": "b",
-                    "objectUUID": b"0123456789abcd07"})
-        self.l.add({"dn": "OU=OU21,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "x", "y": "c",
-                    "objectUUID": b"0123456789abcd08"})
-        self.l.add({"dn": "OU=OU22,DC=SAMBA,DC=ORG",
-                    "name": b"OU #10",
-                    "x": "x", "y": "c",
-                    "objectUUID": b"0123456789abcd09"})
-
-    def test_base(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE)
-        self.assertEqual(len(res11), 1)
-
-    def test_base_lower(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="OU=OU11,DC=samba,DC=org",
-                              scope=ldb.SCOPE_BASE)
-        self.assertEqual(len(res11), 1)
-
-    def test_base_or(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE,
-                              expression="(|(ou=ou11)(ou=ou12))")
-        self.assertEqual(len(res11), 1)
-
-    def test_base_or2(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE,
-                              expression="(|(x=y)(y=b))")
-        self.assertEqual(len(res11), 1)
-
-    def test_base_and(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE,
-                              expression="(&(ou=ou11)(ou=ou12))")
-        self.assertEqual(len(res11), 0)
-
-    def test_base_and2(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE,
-                              expression="(&(x=y)(y=a))")
-        self.assertEqual(len(res11), 1)
-
-    def test_base_false(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE,
-                              expression="(|(ou=ou13)(ou=ou12))")
-        self.assertEqual(len(res11), 0)
-
-    def test_check_base_false(self):
-        """Testing a search"""
-        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE,
-                              expression="(|(ou=ou13)(ou=ou12))")
-        self.assertEqual(len(res11), 0)
-
-    def test_check_base_error(self):
-        """Testing a search"""
-        checkbaseonsearch = {"dn": "@OPTIONS",
-                             "checkBaseOnSearch": b"TRUE"}
-        try:
-            self.l.add(checkbaseonsearch)
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            self.assertEqual(enum, ldb.ERR_ENTRY_ALREADY_EXISTS)
-            m = ldb.Message.from_dict(self.l,
-                                      checkbaseonsearch)
-            self.l.modify(m)
-
-        try:
-            res11 = self.l.search(base="OU=OU11x,DC=SAMBA,DC=ORG",
-                                  scope=ldb.SCOPE_BASE,
-                                  expression="(|(ou=ou13)(ou=ou12))")
-            self.fail("Should have failed on missing base")
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            self.assertEqual(enum, ldb.ERR_NO_SUCH_OBJECT)
-
-    def test_subtree(self):
-        """Testing a search"""
-
-        try:
-            res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                                  scope=ldb.SCOPE_SUBTREE)
-            if hasattr(self, 'IDXCHECK'):
-                self.fail()
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            estr = err.args[1]
-            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
-            self.assertIn(estr, "ldb FULL SEARCH disabled")
-        else:
-            self.assertEqual(len(res11), 25)
-
-    def test_subtree2(self):
-        """Testing a search"""
-
-        try:
-            res11 = self.l.search(base="DC=ORG",
-                                  scope=ldb.SCOPE_SUBTREE)
-            if hasattr(self, 'IDXCHECK'):
-                self.fail()
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            estr = err.args[1]
-            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
-            self.assertIn(estr, "ldb FULL SEARCH disabled")
-        else:
-            self.assertEqual(len(res11), 36)
-
-    def test_subtree_and(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(&(ou=ou11)(ou=ou12))")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_and2(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(&(x=y)(|(y=b)(y=c)))")
-        self.assertEqual(len(res11), 1)
-
-    def test_subtree_and2_lower(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=samba,DC=org",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(&(x=y)(|(y=b)(y=c)))")
-        self.assertEqual(len(res11), 1)
-
-    def test_subtree_or(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(|(ou=ou11)(ou=ou12))")
-        self.assertEqual(len(res11), 2)
-
-    def test_subtree_or2(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(|(x=y)(y=b))")
-        self.assertEqual(len(res11), 20)
-
-    def test_subtree_or3(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(|(x=y)(y=b)(y=c))")
-        self.assertEqual(len(res11), 22)
-
-    def test_one_and(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(ou=ou11)(ou=ou12))")
-        self.assertEqual(len(res11), 0)
-
-    def test_one_and2(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(x=y)(y=b))")
-        self.assertEqual(len(res11), 1)
-
-    def test_one_or(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(|(ou=ou11)(ou=ou12))")
-        self.assertEqual(len(res11), 2)
-
-    def test_one_or2(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(|(x=y)(y=b))")
-        self.assertEqual(len(res11), 20)
-
-    def test_one_or2_lower(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=samba,DC=org",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(|(x=y)(y=b))")
-        self.assertEqual(len(res11), 20)
-
-    def test_one_unindexable(self):
-        """Testing a search"""
-
-        try:
-            res11 = self.l.search(base="DC=samba,DC=org",
-                                  scope=ldb.SCOPE_ONELEVEL,
-                                  expression="(y=b*)")
-            if hasattr(self, 'IDX') and \
-               not hasattr(self, 'IDXONE') and \
-               hasattr(self, 'IDXCHECK'):
-                self.fail("Should have failed as un-indexed search")
-
-            self.assertEqual(len(res11), 9)
-
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            estr = err.args[1]
-            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
-            self.assertIn(estr, "ldb FULL SEARCH disabled")
-
-    def test_one_unindexable_presence(self):
-        """Testing a search"""
-
-        try:
-            res11 = self.l.search(base="DC=samba,DC=org",
-                                  scope=ldb.SCOPE_ONELEVEL,
-                                  expression="(y=*)")
-            if hasattr(self, 'IDX') and \
-               not hasattr(self, 'IDXONE') and \
-               hasattr(self, 'IDXCHECK'):
-                self.fail("Should have failed as un-indexed search")
-
-            self.assertEqual(len(res11), 24)
-
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            estr = err.args[1]
-            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
-            self.assertIn(estr, "ldb FULL SEARCH disabled")
-
-    def test_subtree_and_or(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(&(|(x=z)(y=b))(x=x)(y=c))")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_and_or2(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(&(x=x)(y=c)(|(x=z)(y=b)))")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_and_or3(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(&(|(ou=ou11)(ou=ou10))(|(x=y)(y=b)(y=c)))")
-        self.assertEqual(len(res11), 2)
-
-    def test_subtree_and_or4(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(&(|(x=y)(y=b)(y=c))(|(ou=ou11)(ou=ou10)))")
-        self.assertEqual(len(res11), 2)
-
-    def test_subtree_and_or5(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(&(|(x=y)(y=b)(y=c))(ou=ou11))")
-        self.assertEqual(len(res11), 1)
-
-    def test_subtree_or_and(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(|(x=x)(y=c)(&(x=z)(y=b)))")
-        self.assertEqual(len(res11), 10)
-
-    def test_subtree_large_and_unique(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(&(ou=ou10)(y=a))")
-        self.assertEqual(len(res11), 1)
-
-    def test_subtree_unique(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(ou=ou10)")
-        self.assertEqual(len(res11), 1)
-
-    def test_subtree_unique_elsewhere(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(ou=ou10)")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_unique_elsewhere2(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=NET",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(ou=unique)")
-        self.assertEqual(len(res11), 1)
-
-    def test_subtree_uni123_elsewhere(self):
-        """Testing a search, where the search term contains a (normal ASCII)
-        dotted-i, that will be upper-cased to 'İ', U+0130, LATIN
-        CAPITAL LETTER I WITH DOT ABOVE in certain locales including
-        tr_TR in which this test is sometimes run.
-
-        The search term should fail because the ou does not exist, but
-        we used to get it wrong in unindexed searches which stopped
-        comparing at the i, ignoring the rest of the string, which is
-        not the same as the existing ou ('123' != 'que').
-        """
-        res11 = self.l.search(base="DC=EXAMPLE,DC=NET",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(ou=uni123)")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_unique_elsewhere3(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(ou=unique)")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_unique_elsewhere4(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(ou=unique)")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_unique_elsewhere5(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=COM",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(ou=unique)")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_unique_elsewhere6(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(ou=unique)")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_unique_elsewhere7(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=COM",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(ou=ou10)")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_unique_here(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="OU=UNIQUE,DC=EXAMPLE,DC=NET",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(ou=unique)")
-        self.assertEqual(len(res11), 1)
-
-    def test_subtree_and_none(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(&(ou=ouX)(y=a))")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_and_idx_record(self):
-        """Testing a search against the index record"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(@IDXDN=DC=SAMBA,DC=ORG)")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_and_idxone_record(self):
-        """Testing a search against the index record"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(@IDXONE=DC=SAMBA,DC=ORG)")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel(self):
-        """Testing a search"""
-
-        try:
-            res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                                  scope=ldb.SCOPE_ONELEVEL)
-            if hasattr(self, 'IDXCHECK') \
-               and not hasattr(self, 'IDXONE'):
-                self.fail()
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            estr = err.args[1]
-            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
-            self.assertIn(estr, "ldb FULL SEARCH disabled")
-        else:
-            self.assertEqual(len(res11), 24)
-
-    def test_onelevel2(self):
-        """Testing a search"""
-
-        try:
-            res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                                  scope=ldb.SCOPE_ONELEVEL)
-            if hasattr(self, 'IDXCHECK') \
-               and not hasattr(self, 'IDXONE'):
-                self.fail()
-                self.fail()
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            estr = err.args[1]
-            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
-            self.assertIn(estr, "ldb FULL SEARCH disabled")
-        else:
-            self.assertEqual(len(res11), 9)
-
-    def test_onelevel_and_or(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(|(x=z)(y=b))(x=x)(y=c))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_and_or2(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(x=x)(y=c)(|(x=z)(y=b)))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_and_or3(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(|(ou=ou11)(ou=ou10))(|(x=y)(y=b)(y=c)))")
-        self.assertEqual(len(res11), 2)
-
-    def test_onelevel_and_or4(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(|(x=y)(y=b)(y=c))(|(ou=ou11)(ou=ou10)))")
-        self.assertEqual(len(res11), 2)
-
-    def test_onelevel_and_or5(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(|(x=y)(y=b)(y=c))(ou=ou11))")
-        self.assertEqual(len(res11), 1)
-
-    def test_onelevel_or_and(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(|(x=x)(y=c)(&(x=z)(y=b)))")
-        self.assertEqual(len(res11), 10)
-
-    def test_onelevel_large_and_unique(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(ou=ou10)(y=a))")
-        self.assertEqual(len(res11), 1)
-
-    def test_onelevel_unique(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(ou=ou10)")
-        self.assertEqual(len(res11), 1)
-
-    def test_onelevel_unique_elsewhere(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(ou=ou10)")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_unique_elsewhere2(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=NET",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(ou=unique)")
-        self.assertEqual(len(res11), 1)
-
-    def test_onelevel_unique_elsewhere3(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(ou=unique)")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_unique_elsewhere4(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(ou=unique)")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_unique_elsewhere5(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=COM",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(ou=unique)")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_unique_elsewhere6(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=COM",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(ou=ou10)")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_unique_here(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="OU=UNIQUE,DC=EXAMPLE,DC=NET",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(ou=unique)")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_and_none(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(ou=ouX)(y=a))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_and_idx_record(self):
-        """Testing a search against the index record"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(@IDXDN=DC=SAMBA,DC=ORG)")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_and_idxone_record(self):
-        """Testing a search against the index record"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(@IDXONE=DC=SAMBA,DC=ORG)")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_unindexable(self):
-        """Testing a search"""
-
-        try:
-            res11 = self.l.search(base="DC=samba,DC=org",
-                                  scope=ldb.SCOPE_SUBTREE,
-                                  expression="(y=b*)")
-            if hasattr(self, 'IDX') and \
-               hasattr(self, 'IDXCHECK'):
-                self.fail("Should have failed as un-indexed search")
-
-            self.assertEqual(len(res11), 9)
-
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            estr = err.args[1]
-            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
-            self.assertIn(estr, "ldb FULL SEARCH disabled")
-
-    def test_onelevel_only_and_or(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(|(x=z)(y=b))(x=x)(y=c))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_only_and_or2(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(x=x)(y=c)(|(x=z)(y=b)))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_only_and_or3(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(|(ou=ou11)(ou=ou10))(|(x=y)(y=b)(y=c)))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_only_and_or4(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(|(x=y)(y=b)(y=c))(|(ou=ou11)(ou=ou10)))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_only_and_or5(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(|(x=y)(y=b)(y=c))(ou=ou11))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_only_or_and(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(|(x=x)(y=c)(&(x=z)(y=b)))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_only_large_and_unique(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(ou=ou10)(y=a))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_only_unique(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(ou=ou10)")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_only_unique2(self):
-        """Testing a search"""
-
-        res11 = self.l.search(base="DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(ou=unique)")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_only_and_none(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(ou=ouX)(y=a))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_small_and_or(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(|(x=z)(y=b))(x=x)(y=c))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_small_and_or2(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(x=x)(y=c)(|(x=z)(y=b)))")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_small_and_or3(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(|(ou=ou1)(ou=ou2))(|(x=y)(y=b)(y=c)))")
-        self.assertEqual(len(res11), 2)
-
-    def test_onelevel_small_and_or4(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(|(x=y)(y=b)(y=c))(|(ou=ou1)(ou=ou2)))")
-        self.assertEqual(len(res11), 2)
-
-    def test_onelevel_small_and_or5(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(|(x=y)(y=b)(y=c))(ou=ou1))")
-        self.assertEqual(len(res11), 1)
-
-    def test_onelevel_small_or_and(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(|(x=x)(y=c)(&(x=z)(y=b)))")
-        self.assertEqual(len(res11), 2)
-
-    def test_onelevel_small_large_and_unique(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(ou=ou9)(y=a))")
-        self.assertEqual(len(res11), 1)
-
-    def test_onelevel_small_unique_elsewhere(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(ou=ou10)")
-        self.assertEqual(len(res11), 0)
-
-    def test_onelevel_small_and_none(self):
-        """Testing a search (showing that onelevel is not subtree)"""
-
-        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(&(ou=ouX)(y=a))")
-        self.assertEqual(len(res11), 0)
-
-    def test_subtree_unindexable_presence(self):
-        """Testing a search"""
-
-        try:
-            res11 = self.l.search(base="DC=samba,DC=org",
-                                  scope=ldb.SCOPE_SUBTREE,
-                                  expression="(y=*)")
-            if hasattr(self, 'IDX') and \
-               hasattr(self, 'IDXCHECK'):
-                self.fail("Should have failed as un-indexed search")
-
-            self.assertEqual(len(res11), 24)
-
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            estr = err.args[1]
-            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
-            self.assertIn(estr, "ldb FULL SEARCH disabled")
-
-    def test_dn_filter_one(self):
-        """Testing that a dn= filter succeeds
-        (or fails with disallowDNFilter
-        set and IDXGUID or (IDX and not IDXONE) mode)
-        when the scope is SCOPE_ONELEVEL.
-
-        This should be made more consistent, but for now lock in
-        the behaviour
-
-        """
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(dn=OU=OU1,DC=SAMBA,DC=ORG)")
-        if hasattr(self, 'disallowDNFilter') and \
-           hasattr(self, 'IDX') and \
-           (hasattr(self, 'IDXGUID') or
-            ((not hasattr(self, 'IDXONE') and hasattr(self, 'IDX')))):
-            self.assertEqual(len(res11), 0)
-        else:
-            self.assertEqual(len(res11), 1)
-
-    def test_dn_filter_subtree(self):
-        """Testing that a dn= filter succeeds
-        (or fails with disallowDNFilter set)
-        when the scope is SCOPE_SUBTREE"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(dn=OU=OU1,DC=SAMBA,DC=ORG)")
-        if hasattr(self, 'disallowDNFilter') \
-           and hasattr(self, 'IDX'):
-            self.assertEqual(len(res11), 0)
-        else:
-            self.assertEqual(len(res11), 1)
-
-    def test_dn_filter_base(self):
-        """Testing that (incorrectly) a dn= filter works
-        when the scope is SCOPE_BASE"""
-
-        res11 = self.l.search(base="OU=OU1,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE,
-                              expression="(dn=OU=OU1,DC=SAMBA,DC=ORG)")
-
-        # At some point we should fix this, but it isn't trivial
-        self.assertEqual(len(res11), 1)
-
-    def test_distinguishedName_filter_one(self):
-        """Testing that a distinguishedName= filter succeeds
-        when the scope is SCOPE_ONELEVEL.
-
-        This should be made more consistent, but for now lock in
-        the behaviour
-
-        """
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(distinguishedName=OU=OU1,DC=SAMBA,DC=ORG)")
-        self.assertEqual(len(res11), 1)
-
-    def test_distinguishedName_filter_subtree(self):
-        """Testing that a distinguishedName= filter succeeds
-        when the scope is SCOPE_SUBTREE"""
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(distinguishedName=OU=OU1,DC=SAMBA,DC=ORG)")
-        self.assertEqual(len(res11), 1)
-
-    def test_distinguishedName_filter_base(self):
-        """Testing that (incorrectly) a distinguishedName= filter works
-        when the scope is SCOPE_BASE"""
-
-        res11 = self.l.search(base="OU=OU1,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE,
-                              expression="(distinguishedName=OU=OU1,DC=SAMBA,DC=ORG)")
-
-        # At some point we should fix this, but it isn't trivial
-        self.assertEqual(len(res11), 1)
-
-    def test_bad_dn_filter_base(self):
-        """Testing that a dn= filter on an invalid DN works
-        when the scope is SCOPE_BASE but
-        returns zero results"""
-
-        res11 = self.l.search(base="OU=OU1,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE,
-                              expression="(dn=OU=OU1,DC=SAMBA,DCXXXX)")
-
-        # At some point we should fix this, but it isn't trivial
-        self.assertEqual(len(res11), 0)
-
-
-    def test_bad_dn_filter_one(self):
-        """Testing that a dn= filter succeeds but returns zero
-        results when the DN is not valid on a SCOPE_ONELEVEL search
-
-        """
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(dn=OU=OU1,DC=SAMBA,DCXXXX)")
-        self.assertEqual(len(res11), 0)
-
-    def test_bad_dn_filter_subtree(self):
-        """Testing that a dn= filter succeeds but returns zero
-        results when the DN is not valid on a SCOPE_SUBTREE search
-
-        """
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(dn=OU=OU1,DC=SAMBA,DCXXXX)")
-        self.assertEqual(len(res11), 0)
-
-    def test_bad_distinguishedName_filter_base(self):
-        """Testing that a distinguishedName= filter on an invalid DN works
-        when the scope is SCOPE_BASE but
-        returns zero results"""
-
-        res11 = self.l.search(base="OU=OU1,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE,
-                              expression="(distinguishedName=OU=OU1,DC=SAMBA,DCXXXX)")
-
-        # At some point we should fix this, but it isn't trivial
-        self.assertEqual(len(res11), 0)
-
-
-    def test_bad_distinguishedName_filter_one(self):
-        """Testing that a distinguishedName= filter succeeds but returns zero
-        results when the DN is not valid on a SCOPE_ONELEVEL search
-
-        """
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_ONELEVEL,
-                              expression="(distinguishedName=OU=OU1,DC=SAMBA,DCXXXX)")
-        self.assertEqual(len(res11), 0)
-
-    def test_bad_distinguishedName_filter_subtree(self):
-        """Testing that a distinguishedName= filter succeeds but returns zero
-        results when the DN is not valid on a SCOPE_SUBTREE search
-
-        """
-
-        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_SUBTREE,
-                              expression="(distinguishedName=OU=OU1,DC=SAMBA,DCXXXX)")
-        self.assertEqual(len(res11), 0)
-
-    def test_bad_dn_search_base(self):
-        """Testing with a bad base DN (SCOPE_BASE)"""
-
-        try:
-            res11 = self.l.search(base="OU=OU1,DC=SAMBA,DCXXX",
-                                  scope=ldb.SCOPE_BASE)
-            self.fail("Should have failed with ERR_INVALID_DN_SYNTAX")
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            self.assertEqual(enum, ldb.ERR_INVALID_DN_SYNTAX)
-
-
-    def test_bad_dn_search_one(self):
-        """Testing with a bad base DN (SCOPE_ONELEVEL)"""
-
-        try:
-            res11 = self.l.search(base="DC=SAMBA,DCXXXX",
-                                  scope=ldb.SCOPE_ONELEVEL)
-            self.fail("Should have failed with ERR_INVALID_DN_SYNTAX")
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            self.assertEqual(enum, ldb.ERR_INVALID_DN_SYNTAX)
-
-    def test_bad_dn_search_subtree(self):
-        """Testing with a bad base DN (SCOPE_SUBTREE)"""
-
-        try:
-            res11 = self.l.search(base="DC=SAMBA,DCXXXX",
-                                  scope=ldb.SCOPE_SUBTREE)
-            self.fail("Should have failed with ERR_INVALID_DN_SYNTAX")
-        except ldb.LdbError as err:
-            enum = err.args[0]
-            self.assertEqual(enum, ldb.ERR_INVALID_DN_SYNTAX)
-
-
-
-# Run the search tests against an lmdb backend
-class SearchTestsLmdb(SearchTests):
-
-    def setUp(self):
-        if os.environ.get('HAVE_LMDB', '1') == '0':
-            self.skipTest("No lmdb backend")
-        self.prefix = MDB_PREFIX
-        self.index = MDB_INDEX_OBJ
-        super().setUp()
-
-
-class IndexedSearchTests(SearchTests):
-    """Test searches using the index, to ensure the index doesn't
-       break things"""
-
-    def setUp(self):
-        super().setUp()
-        self.l.add({"dn": "@INDEXLIST",
-                    "@IDXATTR": [b"x", b"y", b"ou"]})
-        self.IDX = True
-
-
-class IndexedCheckSearchTests(IndexedSearchTests):
-    """Test searches using the index, to ensure the index doesn't
-       break things (full scan disabled)"""
-
-    def setUp(self):
-        self.IDXCHECK = True
-        super().setUp()
-
-
-class IndexedSearchDnFilterTests(SearchTests):
-    """Test searches using the index, to ensure the index doesn't
-       break things"""
-
-    def setUp(self):
-        super().setUp()
-        self.l.add({"dn": "@OPTIONS",
-                    "disallowDNFilter": "TRUE"})
-        self.disallowDNFilter = True
-
-        self.l.add({"dn": "@INDEXLIST",
-                    "@IDXATTR": [b"x", b"y", b"ou"]})
-        self.IDX = True
-
-
-class IndexedAndOneLevelSearchTests(SearchTests):
-    """Test searches using the index including @IDXONE, to ensure
-       the index doesn't break things"""
-
-    def setUp(self):
-        super().setUp()
-        self.l.add({"dn": "@INDEXLIST",
-                    "@IDXATTR": [b"x", b"y", b"ou"],
-                    "@IDXONE": [b"1"]})
-        self.IDX = True
-        self.IDXONE = True
-
-
-class IndexedCheckedAndOneLevelSearchTests(IndexedAndOneLevelSearchTests):
-    """Test searches using the index including @IDXONE, to ensure
-       the index doesn't break things (full scan disabled)"""
-
-    def setUp(self):
-        self.IDXCHECK = True
-        super().setUp()
-
-
-class IndexedAndOneLevelDNFilterSearchTests(SearchTests):
-    """Test searches using the index including @IDXONE, to ensure
-       the index doesn't break things"""
-
-    def setUp(self):
-        super().setUp()
-        self.l.add({"dn": "@OPTIONS",
-                    "disallowDNFilter": "TRUE",
-                    "checkBaseOnSearch": "TRUE"})
-        self.disallowDNFilter = True
-        self.checkBaseOnSearch = True
-
-        self.l.add({"dn": "@INDEXLIST",
-                    "@IDXATTR": [b"x", b"y", b"ou"],
-                    "@IDXONE": [b"1"]})
-        self.IDX = True
-        self.IDXONE = True
-
-
-class GUIDIndexedSearchTests(SearchTests):
-    """Test searches using the index, to ensure the index doesn't
-       break things"""
-
-    def setUp(self):
-        self.index = {"dn": "@INDEXLIST",
-                      "@IDXATTR": [b"x", b"y", b"ou"],
-                      "@IDXGUID": [b"objectUUID"],
-                      "@IDX_DN_GUID": [b"GUID"]}
-        super().setUp()
-
-        self.IDXGUID = True
-
-
-class GUIDIndexedDNFilterSearchTests(SearchTests):
-    """Test searches using the index, to ensure the index doesn't
-       break things"""
-
-    def setUp(self):
-        self.index = {"dn": "@INDEXLIST",
-                      "@IDXATTR": [b"x", b"y", b"ou"],
-                      "@IDXGUID": [b"objectUUID"],
-                      "@IDX_DN_GUID": [b"GUID"]}
-        super().setUp()
-        self.l.add({"dn": "@OPTIONS",
-                    "disallowDNFilter": "TRUE",
-                    "checkBaseOnSearch": "TRUE"})
-        self.disallowDNFilter = True
-        self.checkBaseOnSearch = True
-        self.IDX = True
-        self.IDXGUID = True
-
-
-class GUIDAndOneLevelIndexedSearchTests(SearchTests):
-    """Test searches using the index including @IDXONE, to ensure
-       the index doesn't break things"""
-
-    def setUp(self):
-        self.index = {"dn": "@INDEXLIST",
-                      "@IDXATTR": [b"x", b"y", b"ou"],
-                      "@IDXONE": [b"1"],
-                      "@IDXGUID": [b"objectUUID"],
-                      "@IDX_DN_GUID": [b"GUID"]}
-        super().setUp()
-        self.l.add({"dn": "@OPTIONS",
-                    "disallowDNFilter": "TRUE",
-                    "checkBaseOnSearch": "TRUE"})
-        self.disallowDNFilter = True
-        self.checkBaseOnSearch = True
-        self.IDX = True
-        self.IDXGUID = True
-        self.IDXONE = True
-
-
-class GUIDIndexedSearchTestsLmdb(GUIDIndexedSearchTests):
-
-    def setUp(self):
-        if os.environ.get('HAVE_LMDB', '1') == '0':
-            self.skipTest("No lmdb backend")
-        self.prefix = MDB_PREFIX
-        super().setUp()
-
-
-class GUIDIndexedDNFilterSearchTestsLmdb(GUIDIndexedDNFilterSearchTests):
-
-    def setUp(self):
-        if os.environ.get('HAVE_LMDB', '1') == '0':
-            self.skipTest("No lmdb backend")
-        self.prefix = MDB_PREFIX
-        super().setUp()
-
-
-class GUIDAndOneLevelIndexedSearchTestsLmdb(GUIDAndOneLevelIndexedSearchTests):
-
-    def setUp(self):
-        if os.environ.get('HAVE_LMDB', '1') == '0':
-            self.skipTest("No lmdb backend")
-        self.prefix = MDB_PREFIX
-        super().setUp()
-
-
 class AddModifyTests(LdbBaseTest):
     def tearDown(self):
         shutil.rmtree(self.testdir)
@@ -3403,285 +2081,6 @@ class MessageElementTests(TestCase):
         self.assertRaises(UnicodeDecodeError, el.text.__getitem__, 0)
 
 
-class LdbResultTests(LdbBaseTest):
-
-    def setUp(self):
-        super().setUp()
-        self.testdir = tempdir()
-        self.filename = os.path.join(self.testdir, "test.ldb")
-        self.l = ldb.Ldb(self.url(), flags=self.flags())
-        try:
-            self.l.add(self.index)
-        except AttributeError:
-            pass
-        self.l.add({"dn": "DC=SAMBA,DC=ORG", "name": b"samba.org",
-                    "objectUUID": b"0123456789abcde0"})
-        self.l.add({"dn": "OU=ADMIN,DC=SAMBA,DC=ORG", "name": b"Admins",
-                    "objectUUID": b"0123456789abcde1"})
-        self.l.add({"dn": "OU=USERS,DC=SAMBA,DC=ORG", "name": b"Users",
-                    "objectUUID": b"0123456789abcde2"})
-        self.l.add({"dn": "OU=OU1,DC=SAMBA,DC=ORG", "name": b"OU #1",
-                    "objectUUID": b"0123456789abcde3"})
-        self.l.add({"dn": "OU=OU2,DC=SAMBA,DC=ORG", "name": b"OU #2",
-                    "objectUUID": b"0123456789abcde4"})
-        self.l.add({"dn": "OU=OU3,DC=SAMBA,DC=ORG", "name": b"OU #3",
-                    "objectUUID": b"0123456789abcde5"})
-        self.l.add({"dn": "OU=OU4,DC=SAMBA,DC=ORG", "name": b"OU #4",
-                    "objectUUID": b"0123456789abcde6"})
-        self.l.add({"dn": "OU=OU5,DC=SAMBA,DC=ORG", "name": b"OU #5",
-                    "objectUUID": b"0123456789abcde7"})
-        self.l.add({"dn": "OU=OU6,DC=SAMBA,DC=ORG", "name": b"OU #6",
-                    "objectUUID": b"0123456789abcde8"})
-        self.l.add({"dn": "OU=OU7,DC=SAMBA,DC=ORG", "name": b"OU #7",
-                    "objectUUID": b"0123456789abcde9"})
-        self.l.add({"dn": "OU=OU8,DC=SAMBA,DC=ORG", "name": b"OU #8",
-                    "objectUUID": b"0123456789abcdea"})
-        self.l.add({"dn": "OU=OU9,DC=SAMBA,DC=ORG", "name": b"OU #9",
-                    "objectUUID": b"0123456789abcdeb"})
-        self.l.add({"dn": "OU=OU10,DC=SAMBA,DC=ORG", "name": b"OU #10",
-                    "objectUUID": b"0123456789abcdec"})
-
-    def tearDown(self):
-        shutil.rmtree(self.testdir)
-        super().tearDown()
-        # Ensure the LDB is closed now, so we close the FD
-        del(self.l)
-
-    def test_return_type(self):
-        res = self.l.search()
-        self.assertEqual(str(res), "<ldb result>")
-
-    def test_get_msgs(self):
-        res = self.l.search()
-        list = res.msgs
-
-    def test_get_controls(self):
-        res = self.l.search()
-        list = res.controls
-
-    def test_get_referals(self):
-        res = self.l.search()
-        list = res.referals
-
-    def test_iter_msgs(self):
-        found = False
-        for l in self.l.search().msgs:
-            if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
-                found = True
-        self.assertTrue(found)
-
-    def test_iter_msgs_count(self):
-        self.assertTrue(self.l.search().count > 0)
-        # 13 objects has been added to the DC=SAMBA, DC=ORG
-        self.assertEqual(self.l.search(base="DC=SAMBA,DC=ORG").count, 13)
-
-    def test_iter_controls(self):
-        res = self.l.search().controls
-        it = iter(res)
-
-    def test_create_control(self):
-        self.assertRaises(ValueError, ldb.Control, self.l, "tatayoyo:0")
-        c = ldb.Control(self.l, "relax:1")
-        self.assertEqual(c.critical, True)
-        self.assertEqual(c.oid, "1.3.6.1.4.1.4203.666.5.12")
-
-    def test_iter_refs(self):
-        res = self.l.search().referals
-        it = iter(res)
-
-    def test_search_sequence_msgs(self):
-        found = False
-        res = self.l.search().msgs
-
-        for i in range(0, len(res)):
-            l = res[i]
-            if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
-                found = True
-        self.assertTrue(found)
-
-    def test_search_as_iter(self):
-        found = False
-        res = self.l.search()
-
-        for l in res:
-            if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
-                found = True
-        self.assertTrue(found)
-
-    def test_search_iter(self):
-        found = False
-        res = self.l.search_iterator()
-
-        for l in res:
-            if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
-                found = True
-        self.assertTrue(found)
-
-    # Show that search results can't see into a transaction
-
-    def test_search_against_trans(self):
-        found11 = False
-
-        (r1, w1) = os.pipe()
-
-        (r2, w2) = os.pipe()
-
-        # For the first element, fork a child that will
-        # write to the DB
-        pid = os.fork()
-        if pid == 0:
-            # In the child, re-open
-            del(self.l)
-            gc.collect()
-
-            child_ldb = ldb.Ldb(self.url(), flags=self.flags())
-            # start a transaction
-            child_ldb.transaction_start()
-
-            # write to it
-            child_ldb.add({"dn": "OU=OU11,DC=SAMBA,DC=ORG",
-                           "name": b"samba.org",
-                           "objectUUID": b"o123456789acbdef"})
-
-            os.write(w1, b"added")
-
-            # Now wait for the search to be done
-            os.read(r2, 6)
-
-            # and commit
-            try:
-                child_ldb.transaction_commit()
-            except ldb.LdbError as err:
-                # We print this here to see what went wrong in the child
-                print(err)
-                os._exit(1)
-
-            os.write(w1, b"transaction")
-            os._exit(0)
-
-        self.assertEqual(os.read(r1, 5), b"added")
-
-        # This should not turn up until the transaction is concluded
-        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE)
-        self.assertEqual(len(res11), 0)
-
-        os.write(w2, b"search")
-
-        # Now wait for the transaction to be done.  This should
-        # deadlock, but the search doesn't hold a read lock for the
-        # iterator lifetime currently.
-        self.assertEqual(os.read(r1, 11), b"transaction")
-
-        # This should now turn up, as the transaction is over
-        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE)
-        self.assertEqual(len(res11), 1)
-
-        self.assertFalse(found11)
-
-        (got_pid, status) = os.waitpid(pid, 0)
-        self.assertEqual(got_pid, pid)
-
-    def test_search_iter_against_trans(self):
-        found = False
-        found11 = False
-
-        # We need to hold this iterator open to hold the all-record
-        # lock
-        res = self.l.search_iterator()
-
-        (r1, w1) = os.pipe()
-
-        (r2, w2) = os.pipe()
-
-        # For the first element, with the sequence open (which
-        # means with ldb locks held), fork a child that will
-        # write to the DB
-        pid = os.fork()
-        if pid == 0:
-            # In the child, re-open
-            del(res)
-            del(self.l)
-            gc.collect()
-
-            child_ldb = ldb.Ldb(self.url(), flags=self.flags())
-            # start a transaction
-            child_ldb.transaction_start()
-
-            # write to it
-            child_ldb.add({"dn": "OU=OU11,DC=SAMBA,DC=ORG",
-                           "name": b"samba.org",
-                           "objectUUID": b"o123456789acbdef"})
-
-            os.write(w1, b"added")
-
-            # Now wait for the search to be done
-            os.read(r2, 6)
-
-            # and commit
-            try:
-                child_ldb.transaction_commit()
-            except ldb.LdbError as err:
-                # We print this here to see what went wrong in the child
-                print(err)
-                os._exit(1)
-
-            os.write(w1, b"transaction")
-            os._exit(0)
-
-        self.assertEqual(os.read(r1, 5), b"added")
-
-        # This should not turn up until the transaction is concluded
-        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE)
-        self.assertEqual(len(res11), 0)
-
-        os.write(w2, b"search")
-
-        # allow the transaction to start
-        time.sleep(1)
-
-        # This should not turn up until the search finishes and
-        # removed the read lock, but for ldb_tdb that happened as soon
-        # as we called the first res.next()
-        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE)
-        self.assertEqual(len(res11), 0)
-
-        # These results are all collected at the first next(res) call
-        for l in res:
-            if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
-                found = True
-            if str(l.dn) == "OU=OU11,DC=SAMBA,DC=ORG":
-                found11 = True
-
-        # Now wait for the transaction to be done.
-        self.assertEqual(os.read(r1, 11), b"transaction")
-
-        # This should now turn up, as the transaction is over and all
-        # read locks are gone
-        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
-                              scope=ldb.SCOPE_BASE)
-        self.assertEqual(len(res11), 1)
-
-        self.assertTrue(found)
-        self.assertFalse(found11)
-
-        (got_pid, status) = os.waitpid(pid, 0)
-        self.assertEqual(got_pid, pid)
-
-
-class LdbResultTestsLmdb(LdbResultTests):
-
-    def setUp(self):
-        if os.environ.get('HAVE_LMDB', '1') == '0':
-            self.skipTest("No lmdb backend")
-        self.prefix = MDB_PREFIX
-        self.index = MDB_INDEX_OBJ
-        super().setUp()
-
-
 class BadTypeTests(TestCase):
     def test_control(self):
         l = ldb.Ldb()
@@ -3729,103 +2128,6 @@ class VersionTests(TestCase):
     def test_version(self):
         self.assertTrue(isinstance(ldb.__version__, str))
 
-class NestedTransactionTests(LdbBaseTest):
-    def setUp(self):
-        super().setUp()
-        self.testdir = tempdir()
-        self.filename = os.path.join(self.testdir, "test.ldb")
-        self.ldb = ldb.Ldb(self.url(), flags=self.flags())
-        self.ldb.add({"dn": "@INDEXLIST",
-                      "@IDXATTR": [b"x", b"y", b"ou"],
-                      "@IDXGUID": [b"objectUUID"],
-                      "@IDX_DN_GUID": [b"GUID"]})
-
-        super().setUp()
-
-    #
-    # This test documents that currently ldb does not support true nested
-    # transactions.
-    #
-    # Note: The test is written so that it treats failure as pass.
-    #       It is done this way as standalone ldb builds do not use the samba
-    #       known fail mechanism
-    #
-    def test_nested_transactions(self):
-
-        self.ldb.transaction_start()
-
-        self.ldb.add({"dn": "x=x1,dc=samba,dc=org",
-                      "objectUUID": b"0123456789abcde1"})
-        res = self.ldb.search(expression="(objectUUID=0123456789abcde1)",
-                              base="dc=samba,dc=org")
-        self.assertEqual(len(res), 1)
-
-        self.ldb.add({"dn": "x=x2,dc=samba,dc=org",
-                      "objectUUID": b"0123456789abcde2"})
-        res = self.ldb.search(expression="(objectUUID=0123456789abcde2)",
-                              base="dc=samba,dc=org")
-        self.assertEqual(len(res), 1)
-
-        self.ldb.transaction_start()
-        self.ldb.add({"dn": "x=x3,dc=samba,dc=org",
-                      "objectUUID": b"0123456789abcde3"})
-        res = self.ldb.search(expression="(objectUUID=0123456789abcde3)",
-                              base="dc=samba,dc=org")
-        self.assertEqual(len(res), 1)
-        self.ldb.transaction_cancel()
-        #
-        # Check that we can not see the record added by the cancelled
-        # transaction.
-        # Currently this fails as ldb does not support true nested
-        # transactions, and only the outer commits and cancels have an effect
-        #
-        res = self.ldb.search(expression="(objectUUID=0123456789abcde3)",
-                              base="dc=samba,dc=org")
-        #
-        # FIXME this test currently passes on a failure, i.e. if nested
-        #       transaction support worked correctly the correct test would
-        #       be.
-        #         self.assertEqual(len(res), 0)
-        #       as the add of objectUUID=0123456789abcde3 would reverted when
-        #       the sub transaction it was nested in was rolled back.
-        #
-        #       Currently this is not the case so the record is still present.
-        self.assertEqual(len(res), 1)
-
-
-        # Commit the outer transaction
-        #
-        self.ldb.transaction_commit()
-        #
-        # Now check we can still see the records added in the outer
-        # transaction.
-        #
-        res = self.ldb.search(expression="(objectUUID=0123456789abcde1)",
-                              base="dc=samba,dc=org")
-        self.assertEqual(len(res), 1)
-        res = self.ldb.search(expression="(objectUUID=0123456789abcde2)",
-                              base="dc=samba,dc=org")
-        self.assertEqual(len(res), 1)
-        #
-        # And that we can't see the records added by the nested transaction.
-        #
-        res = self.ldb.search(expression="(objectUUID=0123456789abcde3)",
-                              base="dc=samba,dc=org")
-        # FIXME again if nested transactions worked correctly we would not
-        #       see this record. The test should be.
-        #         self.assertEqual(len(res), 0)
-        self.assertEqual(len(res), 1)
-
-
-class LmdbNestedTransactionTests(NestedTransactionTests):
-
-    def setUp(self):
-        if os.environ.get('HAVE_LMDB', '1') == '0':
-            self.skipTest("No lmdb backend")
-        self.prefix = MDB_PREFIX
-        self.index = MDB_INDEX_OBJ
-        super().setUp()
-
 
 if __name__ == '__main__':
     import unittest
diff --git a/lib/ldb/tests/python/api_search.py b/lib/ldb/tests/python/api_search.py
new file mode 100644 (file)
index 0000000..e9c69b7
--- /dev/null
@@ -0,0 +1,1720 @@
+#!/usr/bin/env python3
+# Simple tests for the ldb python bindings.
+# Copyright (C) 2007 Jelmer Vernooij <jelmer@samba.org>
+
+import os
+import sys
+sys.path.insert(0, "bin/python")
+import ldb
+import gc
+import time
+import shutil
+
+from api_base import (
+    MDB_PREFIX,
+    MDB_INDEX_OBJ,
+    tempdir,
+    LdbBaseTest
+)
+
+
+class SearchTests(LdbBaseTest):
+    def tearDown(self):
+        shutil.rmtree(self.testdir)
+        super().tearDown()
+
+        # Ensure the LDB is closed now, so we close the FD
+        del(self.l)
+
+    def setUp(self):
+        super().setUp()
+        self.testdir = tempdir()
+        self.filename = os.path.join(self.testdir, "search_test.ldb")
+        options = ["modules:rdn_name"]
+        if hasattr(self, 'IDXCHECK'):
+            options.append("disable_full_db_scan_for_self_test:1")
+        self.l = ldb.Ldb(self.url(),
+                         flags=self.flags(),
+                         options=options)
+        try:
+            self.l.add(self.index)
+        except AttributeError:
+            pass
+
+        self.l.add({"dn": "@ATTRIBUTES",
+                    "DC": "CASE_INSENSITIVE"})
+
+        # Note that we can't use the name objectGUID here, as we
+        # want to stay clear of the objectGUID handler in LDB and
+        # instead use just the 16 bytes raw, which we just keep
+        # to printable chars here for ease of handling.
+
+        self.l.add({"dn": "DC=ORG",
+                    "name": b"org",
+                    "objectUUID": b"0000000000abcdef"})
+        self.l.add({"dn": "DC=EXAMPLE,DC=ORG",
+                    "name": b"org",
+                    "objectUUID": b"0000000001abcdef"})
+        self.l.add({"dn": "OU=OU1,DC=EXAMPLE,DC=ORG",
+                    "name": b"OU #1",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0023456789abcde3"})
+        self.l.add({"dn": "OU=OU2,DC=EXAMPLE,DC=ORG",
+                    "name": b"OU #2",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0023456789abcde4"})
+        self.l.add({"dn": "OU=OU3,DC=EXAMPLE,DC=ORG",
+                    "name": b"OU #3",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0023456789abcde5"})
+        self.l.add({"dn": "OU=OU4,DC=EXAMPLE,DC=ORG",
+                    "name": b"OU #4",
+                    "x": "z", "y": "b",
+                    "objectUUID": b"0023456789abcde6"})
+        self.l.add({"dn": "OU=OU5,DC=EXAMPLE,DC=ORG",
+                    "name": b"OU #5",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0023456789abcde7"})
+        self.l.add({"dn": "OU=OU6,DC=EXAMPLE,DC=ORG",
+                    "name": b"OU #6",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0023456789abcde8"})
+        self.l.add({"dn": "OU=OU7,DC=EXAMPLE,DC=ORG",
+                    "name": b"OU #7",
+                    "x": "y", "y": "c",
+                    "objectUUID": b"0023456789abcde9"})
+        self.l.add({"dn": "OU=OU8,DC=EXAMPLE,DC=ORG",
+                    "name": b"OU #8",
+                    "x": "y", "y": "b",
+                    "objectUUID": b"0023456789abcde0"})
+        self.l.add({"dn": "OU=OU9,DC=EXAMPLE,DC=ORG",
+                    "name": b"OU #9",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0023456789abcdea"})
+
+        self.l.add({"dn": "DC=EXAMPLE,DC=COM",
+                    "name": b"org",
+                    "objectUUID": b"0000000011abcdef"})
+
+        self.l.add({"dn": "DC=EXAMPLE,DC=NET",
+                    "name": b"org",
+                    "objectUUID": b"0000000021abcdef"})
+
+        self.l.add({"dn": "OU=UNIQUE,DC=EXAMPLE,DC=NET",
+                    "objectUUID": b"0000000022abcdef"})
+
+        self.l.add({"dn": "DC=SAMBA,DC=ORG",
+                    "name": b"samba.org",
+                    "objectUUID": b"0123456789abcdef"})
+        self.l.add({"dn": "OU=ADMIN,DC=SAMBA,DC=ORG",
+                    "name": b"Admins",
+                    "x": "z", "y": "a",
+                    "objectUUID": b"0123456789abcde1"})
+        self.l.add({"dn": "OU=USERS,DC=SAMBA,DC=ORG",
+                    "name": b"Users",
+                    "x": "z", "y": "a",
+                    "objectUUID": b"0123456789abcde2"})
+        self.l.add({"dn": "OU=OU1,DC=SAMBA,DC=ORG",
+                    "name": b"OU #1",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0123456789abcde3"})
+        self.l.add({"dn": "OU=OU2,DC=SAMBA,DC=ORG",
+                    "name": b"OU #2",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0123456789abcde4"})
+        self.l.add({"dn": "OU=OU3,DC=SAMBA,DC=ORG",
+                    "name": b"OU #3",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0123456789abcde5"})
+        self.l.add({"dn": "OU=OU4,DC=SAMBA,DC=ORG",
+                    "name": b"OU #4",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0123456789abcde6"})
+        self.l.add({"dn": "OU=OU5,DC=SAMBA,DC=ORG",
+                    "name": b"OU #5",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0123456789abcde7"})
+        self.l.add({"dn": "OU=OU6,DC=SAMBA,DC=ORG",
+                    "name": b"OU #6",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0123456789abcde8"})
+        self.l.add({"dn": "OU=OU7,DC=SAMBA,DC=ORG",
+                    "name": b"OU #7",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0123456789abcde9"})
+        self.l.add({"dn": "OU=OU8,DC=SAMBA,DC=ORG",
+                    "name": b"OU #8",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0123456789abcde0"})
+        self.l.add({"dn": "OU=OU9,DC=SAMBA,DC=ORG",
+                    "name": b"OU #9",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0123456789abcdea"})
+        self.l.add({"dn": "OU=OU10,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0123456789abcdeb"})
+        self.l.add({"dn": "OU=OU11,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "y", "y": "a",
+                    "objectUUID": b"0123456789abcdec"})
+        self.l.add({"dn": "OU=OU12,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "y", "y": "b",
+                    "objectUUID": b"0123456789abcded"})
+        self.l.add({"dn": "OU=OU13,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "x", "y": "b",
+                    "objectUUID": b"0123456789abcdee"})
+        self.l.add({"dn": "OU=OU14,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "x", "y": "b",
+                    "objectUUID": b"0123456789abcd01"})
+        self.l.add({"dn": "OU=OU15,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "x", "y": "b",
+                    "objectUUID": b"0123456789abcd02"})
+        self.l.add({"dn": "OU=OU16,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "x", "y": "b",
+                    "objectUUID": b"0123456789abcd03"})
+        self.l.add({"dn": "OU=OU17,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "x", "y": "b",
+                    "objectUUID": b"0123456789abcd04"})
+        self.l.add({"dn": "OU=OU18,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "x", "y": "b",
+                    "objectUUID": b"0123456789abcd05"})
+        self.l.add({"dn": "OU=OU19,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "x", "y": "b",
+                    "objectUUID": b"0123456789abcd06"})
+        self.l.add({"dn": "OU=OU20,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "x", "y": "b",
+                    "objectUUID": b"0123456789abcd07"})
+        self.l.add({"dn": "OU=OU21,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "x", "y": "c",
+                    "objectUUID": b"0123456789abcd08"})
+        self.l.add({"dn": "OU=OU22,DC=SAMBA,DC=ORG",
+                    "name": b"OU #10",
+                    "x": "x", "y": "c",
+                    "objectUUID": b"0123456789abcd09"})
+
+    def test_base(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE)
+        self.assertEqual(len(res11), 1)
+
+    def test_base_lower(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="OU=OU11,DC=samba,DC=org",
+                              scope=ldb.SCOPE_BASE)
+        self.assertEqual(len(res11), 1)
+
+    def test_base_or(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE,
+                              expression="(|(ou=ou11)(ou=ou12))")
+        self.assertEqual(len(res11), 1)
+
+    def test_base_or2(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE,
+                              expression="(|(x=y)(y=b))")
+        self.assertEqual(len(res11), 1)
+
+    def test_base_and(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE,
+                              expression="(&(ou=ou11)(ou=ou12))")
+        self.assertEqual(len(res11), 0)
+
+    def test_base_and2(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE,
+                              expression="(&(x=y)(y=a))")
+        self.assertEqual(len(res11), 1)
+
+    def test_base_false(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE,
+                              expression="(|(ou=ou13)(ou=ou12))")
+        self.assertEqual(len(res11), 0)
+
+    def test_check_base_false(self):
+        """Testing a search"""
+        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE,
+                              expression="(|(ou=ou13)(ou=ou12))")
+        self.assertEqual(len(res11), 0)
+
+    def test_check_base_error(self):
+        """Testing a search"""
+        checkbaseonsearch = {"dn": "@OPTIONS",
+                             "checkBaseOnSearch": b"TRUE"}
+        try:
+            self.l.add(checkbaseonsearch)
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            self.assertEqual(enum, ldb.ERR_ENTRY_ALREADY_EXISTS)
+            m = ldb.Message.from_dict(self.l,
+                                      checkbaseonsearch)
+            self.l.modify(m)
+
+        try:
+            res11 = self.l.search(base="OU=OU11x,DC=SAMBA,DC=ORG",
+                                  scope=ldb.SCOPE_BASE,
+                                  expression="(|(ou=ou13)(ou=ou12))")
+            self.fail("Should have failed on missing base")
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            self.assertEqual(enum, ldb.ERR_NO_SUCH_OBJECT)
+
+    def test_subtree(self):
+        """Testing a search"""
+
+        try:
+            res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                                  scope=ldb.SCOPE_SUBTREE)
+            if hasattr(self, 'IDXCHECK'):
+                self.fail()
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            estr = err.args[1]
+            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
+            self.assertIn(estr, "ldb FULL SEARCH disabled")
+        else:
+            self.assertEqual(len(res11), 25)
+
+    def test_subtree2(self):
+        """Testing a search"""
+
+        try:
+            res11 = self.l.search(base="DC=ORG",
+                                  scope=ldb.SCOPE_SUBTREE)
+            if hasattr(self, 'IDXCHECK'):
+                self.fail()
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            estr = err.args[1]
+            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
+            self.assertIn(estr, "ldb FULL SEARCH disabled")
+        else:
+            self.assertEqual(len(res11), 36)
+
+    def test_subtree_and(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(&(ou=ou11)(ou=ou12))")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_and2(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(&(x=y)(|(y=b)(y=c)))")
+        self.assertEqual(len(res11), 1)
+
+    def test_subtree_and2_lower(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=samba,DC=org",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(&(x=y)(|(y=b)(y=c)))")
+        self.assertEqual(len(res11), 1)
+
+    def test_subtree_or(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(|(ou=ou11)(ou=ou12))")
+        self.assertEqual(len(res11), 2)
+
+    def test_subtree_or2(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(|(x=y)(y=b))")
+        self.assertEqual(len(res11), 20)
+
+    def test_subtree_or3(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(|(x=y)(y=b)(y=c))")
+        self.assertEqual(len(res11), 22)
+
+    def test_one_and(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(ou=ou11)(ou=ou12))")
+        self.assertEqual(len(res11), 0)
+
+    def test_one_and2(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(x=y)(y=b))")
+        self.assertEqual(len(res11), 1)
+
+    def test_one_or(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(|(ou=ou11)(ou=ou12))")
+        self.assertEqual(len(res11), 2)
+
+    def test_one_or2(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(|(x=y)(y=b))")
+        self.assertEqual(len(res11), 20)
+
+    def test_one_or2_lower(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=samba,DC=org",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(|(x=y)(y=b))")
+        self.assertEqual(len(res11), 20)
+
+    def test_one_unindexable(self):
+        """Testing a search"""
+
+        try:
+            res11 = self.l.search(base="DC=samba,DC=org",
+                                  scope=ldb.SCOPE_ONELEVEL,
+                                  expression="(y=b*)")
+            if hasattr(self, 'IDX') and \
+               not hasattr(self, 'IDXONE') and \
+               hasattr(self, 'IDXCHECK'):
+                self.fail("Should have failed as un-indexed search")
+
+            self.assertEqual(len(res11), 9)
+
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            estr = err.args[1]
+            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
+            self.assertIn(estr, "ldb FULL SEARCH disabled")
+
+    def test_one_unindexable_presence(self):
+        """Testing a search"""
+
+        try:
+            res11 = self.l.search(base="DC=samba,DC=org",
+                                  scope=ldb.SCOPE_ONELEVEL,
+                                  expression="(y=*)")
+            if hasattr(self, 'IDX') and \
+               not hasattr(self, 'IDXONE') and \
+               hasattr(self, 'IDXCHECK'):
+                self.fail("Should have failed as un-indexed search")
+
+            self.assertEqual(len(res11), 24)
+
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            estr = err.args[1]
+            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
+            self.assertIn(estr, "ldb FULL SEARCH disabled")
+
+    def test_subtree_and_or(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(&(|(x=z)(y=b))(x=x)(y=c))")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_and_or2(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(&(x=x)(y=c)(|(x=z)(y=b)))")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_and_or3(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(&(|(ou=ou11)(ou=ou10))(|(x=y)(y=b)(y=c)))")
+        self.assertEqual(len(res11), 2)
+
+    def test_subtree_and_or4(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(&(|(x=y)(y=b)(y=c))(|(ou=ou11)(ou=ou10)))")
+        self.assertEqual(len(res11), 2)
+
+    def test_subtree_and_or5(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(&(|(x=y)(y=b)(y=c))(ou=ou11))")
+        self.assertEqual(len(res11), 1)
+
+    def test_subtree_or_and(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(|(x=x)(y=c)(&(x=z)(y=b)))")
+        self.assertEqual(len(res11), 10)
+
+    def test_subtree_large_and_unique(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(&(ou=ou10)(y=a))")
+        self.assertEqual(len(res11), 1)
+
+    def test_subtree_unique(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(ou=ou10)")
+        self.assertEqual(len(res11), 1)
+
+    def test_subtree_unique_elsewhere(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(ou=ou10)")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_unique_elsewhere2(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=NET",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(ou=unique)")
+        self.assertEqual(len(res11), 1)
+
+    def test_subtree_uni123_elsewhere(self):
+        """Testing a search, where the search term contains a (normal ASCII)
+        dotted-i, that will be upper-cased to 'İ', U+0130, LATIN
+        CAPITAL LETTER I WITH DOT ABOVE in certain locales including
+        tr_TR in which this test is sometimes run.
+
+        The search term should fail because the ou does not exist, but
+        we used to get it wrong in unindexed searches which stopped
+        comparing at the i, ignoring the rest of the string, which is
+        not the same as the existing ou ('123' != 'que').
+        """
+        res11 = self.l.search(base="DC=EXAMPLE,DC=NET",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(ou=uni123)")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_unique_elsewhere3(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(ou=unique)")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_unique_elsewhere4(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(ou=unique)")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_unique_elsewhere5(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=COM",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(ou=unique)")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_unique_elsewhere6(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(ou=unique)")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_unique_elsewhere7(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=COM",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(ou=ou10)")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_unique_here(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="OU=UNIQUE,DC=EXAMPLE,DC=NET",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(ou=unique)")
+        self.assertEqual(len(res11), 1)
+
+    def test_subtree_and_none(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(&(ou=ouX)(y=a))")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_and_idx_record(self):
+        """Testing a search against the index record"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(@IDXDN=DC=SAMBA,DC=ORG)")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_and_idxone_record(self):
+        """Testing a search against the index record"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(@IDXONE=DC=SAMBA,DC=ORG)")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel(self):
+        """Testing a search"""
+
+        try:
+            res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                                  scope=ldb.SCOPE_ONELEVEL)
+            if hasattr(self, 'IDXCHECK') \
+               and not hasattr(self, 'IDXONE'):
+                self.fail()
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            estr = err.args[1]
+            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
+            self.assertIn(estr, "ldb FULL SEARCH disabled")
+        else:
+            self.assertEqual(len(res11), 24)
+
+    def test_onelevel2(self):
+        """Testing a search"""
+
+        try:
+            res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                                  scope=ldb.SCOPE_ONELEVEL)
+            if hasattr(self, 'IDXCHECK') \
+               and not hasattr(self, 'IDXONE'):
+                self.fail()
+                self.fail()
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            estr = err.args[1]
+            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
+            self.assertIn(estr, "ldb FULL SEARCH disabled")
+        else:
+            self.assertEqual(len(res11), 9)
+
+    def test_onelevel_and_or(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(|(x=z)(y=b))(x=x)(y=c))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_and_or2(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(x=x)(y=c)(|(x=z)(y=b)))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_and_or3(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(|(ou=ou11)(ou=ou10))(|(x=y)(y=b)(y=c)))")
+        self.assertEqual(len(res11), 2)
+
+    def test_onelevel_and_or4(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(|(x=y)(y=b)(y=c))(|(ou=ou11)(ou=ou10)))")
+        self.assertEqual(len(res11), 2)
+
+    def test_onelevel_and_or5(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(|(x=y)(y=b)(y=c))(ou=ou11))")
+        self.assertEqual(len(res11), 1)
+
+    def test_onelevel_or_and(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(|(x=x)(y=c)(&(x=z)(y=b)))")
+        self.assertEqual(len(res11), 10)
+
+    def test_onelevel_large_and_unique(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(ou=ou10)(y=a))")
+        self.assertEqual(len(res11), 1)
+
+    def test_onelevel_unique(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(ou=ou10)")
+        self.assertEqual(len(res11), 1)
+
+    def test_onelevel_unique_elsewhere(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(ou=ou10)")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_unique_elsewhere2(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=NET",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(ou=unique)")
+        self.assertEqual(len(res11), 1)
+
+    def test_onelevel_unique_elsewhere3(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(ou=unique)")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_unique_elsewhere4(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(ou=unique)")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_unique_elsewhere5(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=COM",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(ou=unique)")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_unique_elsewhere6(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=COM",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(ou=ou10)")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_unique_here(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="OU=UNIQUE,DC=EXAMPLE,DC=NET",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(ou=unique)")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_and_none(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(ou=ouX)(y=a))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_and_idx_record(self):
+        """Testing a search against the index record"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(@IDXDN=DC=SAMBA,DC=ORG)")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_and_idxone_record(self):
+        """Testing a search against the index record"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(@IDXONE=DC=SAMBA,DC=ORG)")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_unindexable(self):
+        """Testing a search"""
+
+        try:
+            res11 = self.l.search(base="DC=samba,DC=org",
+                                  scope=ldb.SCOPE_SUBTREE,
+                                  expression="(y=b*)")
+            if hasattr(self, 'IDX') and \
+               hasattr(self, 'IDXCHECK'):
+                self.fail("Should have failed as un-indexed search")
+
+            self.assertEqual(len(res11), 9)
+
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            estr = err.args[1]
+            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
+            self.assertIn(estr, "ldb FULL SEARCH disabled")
+
+    def test_onelevel_only_and_or(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(|(x=z)(y=b))(x=x)(y=c))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_only_and_or2(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(x=x)(y=c)(|(x=z)(y=b)))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_only_and_or3(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(|(ou=ou11)(ou=ou10))(|(x=y)(y=b)(y=c)))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_only_and_or4(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(|(x=y)(y=b)(y=c))(|(ou=ou11)(ou=ou10)))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_only_and_or5(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(|(x=y)(y=b)(y=c))(ou=ou11))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_only_or_and(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(|(x=x)(y=c)(&(x=z)(y=b)))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_only_large_and_unique(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(ou=ou10)(y=a))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_only_unique(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(ou=ou10)")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_only_unique2(self):
+        """Testing a search"""
+
+        res11 = self.l.search(base="DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(ou=unique)")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_only_and_none(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(ou=ouX)(y=a))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_small_and_or(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(|(x=z)(y=b))(x=x)(y=c))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_small_and_or2(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(x=x)(y=c)(|(x=z)(y=b)))")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_small_and_or3(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(|(ou=ou1)(ou=ou2))(|(x=y)(y=b)(y=c)))")
+        self.assertEqual(len(res11), 2)
+
+    def test_onelevel_small_and_or4(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(|(x=y)(y=b)(y=c))(|(ou=ou1)(ou=ou2)))")
+        self.assertEqual(len(res11), 2)
+
+    def test_onelevel_small_and_or5(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(|(x=y)(y=b)(y=c))(ou=ou1))")
+        self.assertEqual(len(res11), 1)
+
+    def test_onelevel_small_or_and(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(|(x=x)(y=c)(&(x=z)(y=b)))")
+        self.assertEqual(len(res11), 2)
+
+    def test_onelevel_small_large_and_unique(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(ou=ou9)(y=a))")
+        self.assertEqual(len(res11), 1)
+
+    def test_onelevel_small_unique_elsewhere(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(ou=ou10)")
+        self.assertEqual(len(res11), 0)
+
+    def test_onelevel_small_and_none(self):
+        """Testing a search (showing that onelevel is not subtree)"""
+
+        res11 = self.l.search(base="DC=EXAMPLE,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(&(ou=ouX)(y=a))")
+        self.assertEqual(len(res11), 0)
+
+    def test_subtree_unindexable_presence(self):
+        """Testing a search"""
+
+        try:
+            res11 = self.l.search(base="DC=samba,DC=org",
+                                  scope=ldb.SCOPE_SUBTREE,
+                                  expression="(y=*)")
+            if hasattr(self, 'IDX') and \
+               hasattr(self, 'IDXCHECK'):
+                self.fail("Should have failed as un-indexed search")
+
+            self.assertEqual(len(res11), 24)
+
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            estr = err.args[1]
+            self.assertEqual(enum, ldb.ERR_INAPPROPRIATE_MATCHING)
+            self.assertIn(estr, "ldb FULL SEARCH disabled")
+
+    def test_dn_filter_one(self):
+        """Testing that a dn= filter succeeds
+        (or fails with disallowDNFilter
+        set and IDXGUID or (IDX and not IDXONE) mode)
+        when the scope is SCOPE_ONELEVEL.
+
+        This should be made more consistent, but for now lock in
+        the behaviour
+
+        """
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(dn=OU=OU1,DC=SAMBA,DC=ORG)")
+        if hasattr(self, 'disallowDNFilter') and \
+           hasattr(self, 'IDX') and \
+           (hasattr(self, 'IDXGUID') or
+            ((not hasattr(self, 'IDXONE') and hasattr(self, 'IDX')))):
+            self.assertEqual(len(res11), 0)
+        else:
+            self.assertEqual(len(res11), 1)
+
+    def test_dn_filter_subtree(self):
+        """Testing that a dn= filter succeeds
+        (or fails with disallowDNFilter set)
+        when the scope is SCOPE_SUBTREE"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(dn=OU=OU1,DC=SAMBA,DC=ORG)")
+        if hasattr(self, 'disallowDNFilter') \
+           and hasattr(self, 'IDX'):
+            self.assertEqual(len(res11), 0)
+        else:
+            self.assertEqual(len(res11), 1)
+
+    def test_dn_filter_base(self):
+        """Testing that (incorrectly) a dn= filter works
+        when the scope is SCOPE_BASE"""
+
+        res11 = self.l.search(base="OU=OU1,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE,
+                              expression="(dn=OU=OU1,DC=SAMBA,DC=ORG)")
+
+        # At some point we should fix this, but it isn't trivial
+        self.assertEqual(len(res11), 1)
+
+    def test_distinguishedName_filter_one(self):
+        """Testing that a distinguishedName= filter succeeds
+        when the scope is SCOPE_ONELEVEL.
+
+        This should be made more consistent, but for now lock in
+        the behaviour
+
+        """
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(distinguishedName=OU=OU1,DC=SAMBA,DC=ORG)")
+        self.assertEqual(len(res11), 1)
+
+    def test_distinguishedName_filter_subtree(self):
+        """Testing that a distinguishedName= filter succeeds
+        when the scope is SCOPE_SUBTREE"""
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(distinguishedName=OU=OU1,DC=SAMBA,DC=ORG)")
+        self.assertEqual(len(res11), 1)
+
+    def test_distinguishedName_filter_base(self):
+        """Testing that (incorrectly) a distinguishedName= filter works
+        when the scope is SCOPE_BASE"""
+
+        res11 = self.l.search(base="OU=OU1,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE,
+                              expression="(distinguishedName=OU=OU1,DC=SAMBA,DC=ORG)")
+
+        # At some point we should fix this, but it isn't trivial
+        self.assertEqual(len(res11), 1)
+
+    def test_bad_dn_filter_base(self):
+        """Testing that a dn= filter on an invalid DN works
+        when the scope is SCOPE_BASE but
+        returns zero results"""
+
+        res11 = self.l.search(base="OU=OU1,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE,
+                              expression="(dn=OU=OU1,DC=SAMBA,DCXXXX)")
+
+        # At some point we should fix this, but it isn't trivial
+        self.assertEqual(len(res11), 0)
+
+
+    def test_bad_dn_filter_one(self):
+        """Testing that a dn= filter succeeds but returns zero
+        results when the DN is not valid on a SCOPE_ONELEVEL search
+
+        """
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(dn=OU=OU1,DC=SAMBA,DCXXXX)")
+        self.assertEqual(len(res11), 0)
+
+    def test_bad_dn_filter_subtree(self):
+        """Testing that a dn= filter succeeds but returns zero
+        results when the DN is not valid on a SCOPE_SUBTREE search
+
+        """
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(dn=OU=OU1,DC=SAMBA,DCXXXX)")
+        self.assertEqual(len(res11), 0)
+
+    def test_bad_distinguishedName_filter_base(self):
+        """Testing that a distinguishedName= filter on an invalid DN works
+        when the scope is SCOPE_BASE but
+        returns zero results"""
+
+        res11 = self.l.search(base="OU=OU1,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE,
+                              expression="(distinguishedName=OU=OU1,DC=SAMBA,DCXXXX)")
+
+        # At some point we should fix this, but it isn't trivial
+        self.assertEqual(len(res11), 0)
+
+
+    def test_bad_distinguishedName_filter_one(self):
+        """Testing that a distinguishedName= filter succeeds but returns zero
+        results when the DN is not valid on a SCOPE_ONELEVEL search
+
+        """
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_ONELEVEL,
+                              expression="(distinguishedName=OU=OU1,DC=SAMBA,DCXXXX)")
+        self.assertEqual(len(res11), 0)
+
+    def test_bad_distinguishedName_filter_subtree(self):
+        """Testing that a distinguishedName= filter succeeds but returns zero
+        results when the DN is not valid on a SCOPE_SUBTREE search
+
+        """
+
+        res11 = self.l.search(base="DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_SUBTREE,
+                              expression="(distinguishedName=OU=OU1,DC=SAMBA,DCXXXX)")
+        self.assertEqual(len(res11), 0)
+
+    def test_bad_dn_search_base(self):
+        """Testing with a bad base DN (SCOPE_BASE)"""
+
+        try:
+            res11 = self.l.search(base="OU=OU1,DC=SAMBA,DCXXX",
+                                  scope=ldb.SCOPE_BASE)
+            self.fail("Should have failed with ERR_INVALID_DN_SYNTAX")
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            self.assertEqual(enum, ldb.ERR_INVALID_DN_SYNTAX)
+
+
+    def test_bad_dn_search_one(self):
+        """Testing with a bad base DN (SCOPE_ONELEVEL)"""
+
+        try:
+            res11 = self.l.search(base="DC=SAMBA,DCXXXX",
+                                  scope=ldb.SCOPE_ONELEVEL)
+            self.fail("Should have failed with ERR_INVALID_DN_SYNTAX")
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            self.assertEqual(enum, ldb.ERR_INVALID_DN_SYNTAX)
+
+    def test_bad_dn_search_subtree(self):
+        """Testing with a bad base DN (SCOPE_SUBTREE)"""
+
+        try:
+            res11 = self.l.search(base="DC=SAMBA,DCXXXX",
+                                  scope=ldb.SCOPE_SUBTREE)
+            self.fail("Should have failed with ERR_INVALID_DN_SYNTAX")
+        except ldb.LdbError as err:
+            enum = err.args[0]
+            self.assertEqual(enum, ldb.ERR_INVALID_DN_SYNTAX)
+
+
+
+# Run the search tests against an lmdb backend
+class SearchTestsLmdb(SearchTests):
+
+    def setUp(self):
+        if os.environ.get('HAVE_LMDB', '1') == '0':
+            self.skipTest("No lmdb backend")
+        self.prefix = MDB_PREFIX
+        self.index = MDB_INDEX_OBJ
+        super().setUp()
+
+
+class IndexedSearchTests(SearchTests):
+    """Test searches using the index, to ensure the index doesn't
+       break things"""
+
+    def setUp(self):
+        super().setUp()
+        self.l.add({"dn": "@INDEXLIST",
+                    "@IDXATTR": [b"x", b"y", b"ou"]})
+        self.IDX = True
+
+
+class IndexedCheckSearchTests(IndexedSearchTests):
+    """Test searches using the index, to ensure the index doesn't
+       break things (full scan disabled)"""
+
+    def setUp(self):
+        self.IDXCHECK = True
+        super().setUp()
+
+
+class IndexedSearchDnFilterTests(SearchTests):
+    """Test searches using the index, to ensure the index doesn't
+       break things"""
+
+    def setUp(self):
+        super().setUp()
+        self.l.add({"dn": "@OPTIONS",
+                    "disallowDNFilter": "TRUE"})
+        self.disallowDNFilter = True
+
+        self.l.add({"dn": "@INDEXLIST",
+                    "@IDXATTR": [b"x", b"y", b"ou"]})
+        self.IDX = True
+
+
+class IndexedAndOneLevelSearchTests(SearchTests):
+    """Test searches using the index including @IDXONE, to ensure
+       the index doesn't break things"""
+
+    def setUp(self):
+        super().setUp()
+        self.l.add({"dn": "@INDEXLIST",
+                    "@IDXATTR": [b"x", b"y", b"ou"],
+                    "@IDXONE": [b"1"]})
+        self.IDX = True
+        self.IDXONE = True
+
+
+class IndexedCheckedAndOneLevelSearchTests(IndexedAndOneLevelSearchTests):
+    """Test searches using the index including @IDXONE, to ensure
+       the index doesn't break things (full scan disabled)"""
+
+    def setUp(self):
+        self.IDXCHECK = True
+        super().setUp()
+
+
+class IndexedAndOneLevelDNFilterSearchTests(SearchTests):
+    """Test searches using the index including @IDXONE, to ensure
+       the index doesn't break things"""
+
+    def setUp(self):
+        super().setUp()
+        self.l.add({"dn": "@OPTIONS",
+                    "disallowDNFilter": "TRUE",
+                    "checkBaseOnSearch": "TRUE"})
+        self.disallowDNFilter = True
+        self.checkBaseOnSearch = True
+
+        self.l.add({"dn": "@INDEXLIST",
+                    "@IDXATTR": [b"x", b"y", b"ou"],
+                    "@IDXONE": [b"1"]})
+        self.IDX = True
+        self.IDXONE = True
+
+
+class GUIDIndexedSearchTests(SearchTests):
+    """Test searches using the index, to ensure the index doesn't
+       break things"""
+
+    def setUp(self):
+        self.index = {"dn": "@INDEXLIST",
+                      "@IDXATTR": [b"x", b"y", b"ou"],
+                      "@IDXGUID": [b"objectUUID"],
+                      "@IDX_DN_GUID": [b"GUID"]}
+        super().setUp()
+
+        self.IDXGUID = True
+
+
+class GUIDIndexedDNFilterSearchTests(SearchTests):
+    """Test searches using the index, to ensure the index doesn't
+       break things"""
+
+    def setUp(self):
+        self.index = {"dn": "@INDEXLIST",
+                      "@IDXATTR": [b"x", b"y", b"ou"],
+                      "@IDXGUID": [b"objectUUID"],
+                      "@IDX_DN_GUID": [b"GUID"]}
+        super().setUp()
+        self.l.add({"dn": "@OPTIONS",
+                    "disallowDNFilter": "TRUE",
+                    "checkBaseOnSearch": "TRUE"})
+        self.disallowDNFilter = True
+        self.checkBaseOnSearch = True
+        self.IDX = True
+        self.IDXGUID = True
+
+
+class GUIDAndOneLevelIndexedSearchTests(SearchTests):
+    """Test searches using the index including @IDXONE, to ensure
+       the index doesn't break things"""
+
+    def setUp(self):
+        self.index = {"dn": "@INDEXLIST",
+                      "@IDXATTR": [b"x", b"y", b"ou"],
+                      "@IDXONE": [b"1"],
+                      "@IDXGUID": [b"objectUUID"],
+                      "@IDX_DN_GUID": [b"GUID"]}
+        super().setUp()
+        self.l.add({"dn": "@OPTIONS",
+                    "disallowDNFilter": "TRUE",
+                    "checkBaseOnSearch": "TRUE"})
+        self.disallowDNFilter = True
+        self.checkBaseOnSearch = True
+        self.IDX = True
+        self.IDXGUID = True
+        self.IDXONE = True
+
+
+class GUIDIndexedSearchTestsLmdb(GUIDIndexedSearchTests):
+
+    def setUp(self):
+        if os.environ.get('HAVE_LMDB', '1') == '0':
+            self.skipTest("No lmdb backend")
+        self.prefix = MDB_PREFIX
+        super().setUp()
+
+
+class GUIDIndexedDNFilterSearchTestsLmdb(GUIDIndexedDNFilterSearchTests):
+
+    def setUp(self):
+        if os.environ.get('HAVE_LMDB', '1') == '0':
+            self.skipTest("No lmdb backend")
+        self.prefix = MDB_PREFIX
+        super().setUp()
+
+
+class GUIDAndOneLevelIndexedSearchTestsLmdb(GUIDAndOneLevelIndexedSearchTests):
+
+    def setUp(self):
+        if os.environ.get('HAVE_LMDB', '1') == '0':
+            self.skipTest("No lmdb backend")
+        self.prefix = MDB_PREFIX
+        super().setUp()
+
+
+class LdbResultTests(LdbBaseTest):
+
+    def setUp(self):
+        super().setUp()
+        self.testdir = tempdir()
+        self.filename = os.path.join(self.testdir, "test.ldb")
+        self.l = ldb.Ldb(self.url(), flags=self.flags())
+        try:
+            self.l.add(self.index)
+        except AttributeError:
+            pass
+        self.l.add({"dn": "DC=SAMBA,DC=ORG", "name": b"samba.org",
+                    "objectUUID": b"0123456789abcde0"})
+        self.l.add({"dn": "OU=ADMIN,DC=SAMBA,DC=ORG", "name": b"Admins",
+                    "objectUUID": b"0123456789abcde1"})
+        self.l.add({"dn": "OU=USERS,DC=SAMBA,DC=ORG", "name": b"Users",
+                    "objectUUID": b"0123456789abcde2"})
+        self.l.add({"dn": "OU=OU1,DC=SAMBA,DC=ORG", "name": b"OU #1",
+                    "objectUUID": b"0123456789abcde3"})
+        self.l.add({"dn": "OU=OU2,DC=SAMBA,DC=ORG", "name": b"OU #2",
+                    "objectUUID": b"0123456789abcde4"})
+        self.l.add({"dn": "OU=OU3,DC=SAMBA,DC=ORG", "name": b"OU #3",
+                    "objectUUID": b"0123456789abcde5"})
+        self.l.add({"dn": "OU=OU4,DC=SAMBA,DC=ORG", "name": b"OU #4",
+                    "objectUUID": b"0123456789abcde6"})
+        self.l.add({"dn": "OU=OU5,DC=SAMBA,DC=ORG", "name": b"OU #5",
+                    "objectUUID": b"0123456789abcde7"})
+        self.l.add({"dn": "OU=OU6,DC=SAMBA,DC=ORG", "name": b"OU #6",
+                    "objectUUID": b"0123456789abcde8"})
+        self.l.add({"dn": "OU=OU7,DC=SAMBA,DC=ORG", "name": b"OU #7",
+                    "objectUUID": b"0123456789abcde9"})
+        self.l.add({"dn": "OU=OU8,DC=SAMBA,DC=ORG", "name": b"OU #8",
+                    "objectUUID": b"0123456789abcdea"})
+        self.l.add({"dn": "OU=OU9,DC=SAMBA,DC=ORG", "name": b"OU #9",
+                    "objectUUID": b"0123456789abcdeb"})
+        self.l.add({"dn": "OU=OU10,DC=SAMBA,DC=ORG", "name": b"OU #10",
+                    "objectUUID": b"0123456789abcdec"})
+
+    def tearDown(self):
+        shutil.rmtree(self.testdir)
+        super().tearDown()
+        # Ensure the LDB is closed now, so we close the FD
+        del(self.l)
+
+    def test_return_type(self):
+        res = self.l.search()
+        self.assertEqual(str(res), "<ldb result>")
+
+    def test_get_msgs(self):
+        res = self.l.search()
+        list = res.msgs
+
+    def test_get_controls(self):
+        res = self.l.search()
+        list = res.controls
+
+    def test_get_referals(self):
+        res = self.l.search()
+        list = res.referals
+
+    def test_iter_msgs(self):
+        found = False
+        for l in self.l.search().msgs:
+            if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
+                found = True
+        self.assertTrue(found)
+
+    def test_iter_msgs_count(self):
+        self.assertTrue(self.l.search().count > 0)
+        # 13 objects has been added to the DC=SAMBA, DC=ORG
+        self.assertEqual(self.l.search(base="DC=SAMBA,DC=ORG").count, 13)
+
+    def test_iter_controls(self):
+        res = self.l.search().controls
+        it = iter(res)
+
+    def test_create_control(self):
+        self.assertRaises(ValueError, ldb.Control, self.l, "tatayoyo:0")
+        c = ldb.Control(self.l, "relax:1")
+        self.assertEqual(c.critical, True)
+        self.assertEqual(c.oid, "1.3.6.1.4.1.4203.666.5.12")
+
+    def test_iter_refs(self):
+        res = self.l.search().referals
+        it = iter(res)
+
+    def test_search_sequence_msgs(self):
+        found = False
+        res = self.l.search().msgs
+
+        for i in range(0, len(res)):
+            l = res[i]
+            if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
+                found = True
+        self.assertTrue(found)
+
+    def test_search_as_iter(self):
+        found = False
+        res = self.l.search()
+
+        for l in res:
+            if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
+                found = True
+        self.assertTrue(found)
+
+    def test_search_iter(self):
+        found = False
+        res = self.l.search_iterator()
+
+        for l in res:
+            if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
+                found = True
+        self.assertTrue(found)
+
+    # Show that search results can't see into a transaction
+
+    def test_search_against_trans(self):
+        found11 = False
+
+        (r1, w1) = os.pipe()
+
+        (r2, w2) = os.pipe()
+
+        # For the first element, fork a child that will
+        # write to the DB
+        pid = os.fork()
+        if pid == 0:
+            # In the child, re-open
+            del(self.l)
+            gc.collect()
+
+            child_ldb = ldb.Ldb(self.url(), flags=self.flags())
+            # start a transaction
+            child_ldb.transaction_start()
+
+            # write to it
+            child_ldb.add({"dn": "OU=OU11,DC=SAMBA,DC=ORG",
+                           "name": b"samba.org",
+                           "objectUUID": b"o123456789acbdef"})
+
+            os.write(w1, b"added")
+
+            # Now wait for the search to be done
+            os.read(r2, 6)
+
+            # and commit
+            try:
+                child_ldb.transaction_commit()
+            except ldb.LdbError as err:
+                # We print this here to see what went wrong in the child
+                print(err)
+                os._exit(1)
+
+            os.write(w1, b"transaction")
+            os._exit(0)
+
+        self.assertEqual(os.read(r1, 5), b"added")
+
+        # This should not turn up until the transaction is concluded
+        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE)
+        self.assertEqual(len(res11), 0)
+
+        os.write(w2, b"search")
+
+        # Now wait for the transaction to be done.  This should
+        # deadlock, but the search doesn't hold a read lock for the
+        # iterator lifetime currently.
+        self.assertEqual(os.read(r1, 11), b"transaction")
+
+        # This should now turn up, as the transaction is over
+        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE)
+        self.assertEqual(len(res11), 1)
+
+        self.assertFalse(found11)
+
+        (got_pid, status) = os.waitpid(pid, 0)
+        self.assertEqual(got_pid, pid)
+
+    def test_search_iter_against_trans(self):
+        found = False
+        found11 = False
+
+        # We need to hold this iterator open to hold the all-record
+        # lock
+        res = self.l.search_iterator()
+
+        (r1, w1) = os.pipe()
+
+        (r2, w2) = os.pipe()
+
+        # For the first element, with the sequence open (which
+        # means with ldb locks held), fork a child that will
+        # write to the DB
+        pid = os.fork()
+        if pid == 0:
+            # In the child, re-open
+            del(res)
+            del(self.l)
+            gc.collect()
+
+            child_ldb = ldb.Ldb(self.url(), flags=self.flags())
+            # start a transaction
+            child_ldb.transaction_start()
+
+            # write to it
+            child_ldb.add({"dn": "OU=OU11,DC=SAMBA,DC=ORG",
+                           "name": b"samba.org",
+                           "objectUUID": b"o123456789acbdef"})
+
+            os.write(w1, b"added")
+
+            # Now wait for the search to be done
+            os.read(r2, 6)
+
+            # and commit
+            try:
+                child_ldb.transaction_commit()
+            except ldb.LdbError as err:
+                # We print this here to see what went wrong in the child
+                print(err)
+                os._exit(1)
+
+            os.write(w1, b"transaction")
+            os._exit(0)
+
+        self.assertEqual(os.read(r1, 5), b"added")
+
+        # This should not turn up until the transaction is concluded
+        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE)
+        self.assertEqual(len(res11), 0)
+
+        os.write(w2, b"search")
+
+        # allow the transaction to start
+        time.sleep(1)
+
+        # This should not turn up until the search finishes and
+        # removed the read lock, but for ldb_tdb that happened as soon
+        # as we called the first res.next()
+        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE)
+        self.assertEqual(len(res11), 0)
+
+        # These results are all collected at the first next(res) call
+        for l in res:
+            if str(l.dn) == "OU=OU10,DC=SAMBA,DC=ORG":
+                found = True
+            if str(l.dn) == "OU=OU11,DC=SAMBA,DC=ORG":
+                found11 = True
+
+        # Now wait for the transaction to be done.
+        self.assertEqual(os.read(r1, 11), b"transaction")
+
+        # This should now turn up, as the transaction is over and all
+        # read locks are gone
+        res11 = self.l.search(base="OU=OU11,DC=SAMBA,DC=ORG",
+                              scope=ldb.SCOPE_BASE)
+        self.assertEqual(len(res11), 1)
+
+        self.assertTrue(found)
+        self.assertFalse(found11)
+
+        (got_pid, status) = os.waitpid(pid, 0)
+        self.assertEqual(got_pid, pid)
+
+
+class LdbResultTestsLmdb(LdbResultTests):
+
+    def setUp(self):
+        if os.environ.get('HAVE_LMDB', '1') == '0':
+            self.skipTest("No lmdb backend")
+        self.prefix = MDB_PREFIX
+        self.index = MDB_INDEX_OBJ
+        super().setUp()
+
+
+class NestedTransactionTests(LdbBaseTest):
+    def setUp(self):
+        super().setUp()
+        self.testdir = tempdir()
+        self.filename = os.path.join(self.testdir, "test.ldb")
+        self.ldb = ldb.Ldb(self.url(), flags=self.flags())
+        self.ldb.add({"dn": "@INDEXLIST",
+                      "@IDXATTR": [b"x", b"y", b"ou"],
+                      "@IDXGUID": [b"objectUUID"],
+                      "@IDX_DN_GUID": [b"GUID"]})
+
+        super().setUp()
+
+    #
+    # This test documents that currently ldb does not support true nested
+    # transactions.
+    #
+    # Note: The test is written so that it treats failure as pass.
+    #       It is done this way as standalone ldb builds do not use the samba
+    #       known fail mechanism
+    #
+    def test_nested_transactions(self):
+
+        self.ldb.transaction_start()
+
+        self.ldb.add({"dn": "x=x1,dc=samba,dc=org",
+                      "objectUUID": b"0123456789abcde1"})
+        res = self.ldb.search(expression="(objectUUID=0123456789abcde1)",
+                              base="dc=samba,dc=org")
+        self.assertEqual(len(res), 1)
+
+        self.ldb.add({"dn": "x=x2,dc=samba,dc=org",
+                      "objectUUID": b"0123456789abcde2"})
+        res = self.ldb.search(expression="(objectUUID=0123456789abcde2)",
+                              base="dc=samba,dc=org")
+        self.assertEqual(len(res), 1)
+
+        self.ldb.transaction_start()
+        self.ldb.add({"dn": "x=x3,dc=samba,dc=org",
+                      "objectUUID": b"0123456789abcde3"})
+        res = self.ldb.search(expression="(objectUUID=0123456789abcde3)",
+                              base="dc=samba,dc=org")
+        self.assertEqual(len(res), 1)
+        self.ldb.transaction_cancel()
+        #
+        # Check that we can not see the record added by the cancelled
+        # transaction.
+        # Currently this fails as ldb does not support true nested
+        # transactions, and only the outer commits and cancels have an effect
+        #
+        res = self.ldb.search(expression="(objectUUID=0123456789abcde3)",
+                              base="dc=samba,dc=org")
+        #
+        # FIXME this test currently passes on a failure, i.e. if nested
+        #       transaction support worked correctly the correct test would
+        #       be.
+        #         self.assertEqual(len(res), 0)
+        #       as the add of objectUUID=0123456789abcde3 would reverted when
+        #       the sub transaction it was nested in was rolled back.
+        #
+        #       Currently this is not the case so the record is still present.
+        self.assertEqual(len(res), 1)
+
+
+        # Commit the outer transaction
+        #
+        self.ldb.transaction_commit()
+        #
+        # Now check we can still see the records added in the outer
+        # transaction.
+        #
+        res = self.ldb.search(expression="(objectUUID=0123456789abcde1)",
+                              base="dc=samba,dc=org")
+        self.assertEqual(len(res), 1)
+        res = self.ldb.search(expression="(objectUUID=0123456789abcde2)",
+                              base="dc=samba,dc=org")
+        self.assertEqual(len(res), 1)
+        #
+        # And that we can't see the records added by the nested transaction.
+        #
+        res = self.ldb.search(expression="(objectUUID=0123456789abcde3)",
+                              base="dc=samba,dc=org")
+        # FIXME again if nested transactions worked correctly we would not
+        #       see this record. The test should be.
+        #         self.assertEqual(len(res), 0)
+        self.assertEqual(len(res), 1)
+
+
+class LmdbNestedTransactionTests(NestedTransactionTests):
+
+    def setUp(self):
+        if os.environ.get('HAVE_LMDB', '1') == '0':
+            self.skipTest("No lmdb backend")
+        self.prefix = MDB_PREFIX
+        self.index = MDB_INDEX_OBJ
+        super().setUp()
+
+
+if __name__ == '__main__':
+    import unittest
+    unittest.TestProgram()
index d585e7cda37b0719ae6c9c91d9e0fda49a213e66..80cf67add57991468faa396b397039dcf4be68d7 100644 (file)
@@ -73,6 +73,10 @@ planpythontestsuite("none", "api",
                     name="ldb.python.api",
                     extra_path=['lib/ldb/tests/python'],
                     environ={'HAVE_LMDB': str(int(have_lmdb))})
+planpythontestsuite("none", "api_search",
+                    name="ldb.python.api_search",
+                    extra_path=['lib/ldb/tests/python'],
+                    environ={'HAVE_LMDB': str(int(have_lmdb))})
 planpythontestsuite("none", "crash",
                     name="ldb.python.crash",
                     extra_path=['lib/ldb/tests/python'],
@@ -94,6 +98,11 @@ planpythontestsuite("none", "api",
                     extra_path=['lib/ldb/tests/python'],
                     environ={'LC_ALL': 'tr_TR.UTF-8',
                              'HAVE_LMDB': str(int(have_lmdb))})
+planpythontestsuite("none", "api_search",
+                    name="ldb.python.api_search.tr",
+                    extra_path=['lib/ldb/tests/python'],
+                    environ={'LC_ALL': 'tr_TR.UTF-8',
+                             'HAVE_LMDB': str(int(have_lmdb))})
 planpythontestsuite("none", "index",
                     name="ldb.python.index.tr",
                     extra_path=['lib/ldb/tests/python'],