From: Miod Vallat Date: Fri, 11 Jul 2025 16:02:47 +0000 (+0200) Subject: Add record search capability to the LMDB backend. X-Git-Tag: rec-5.4.0-alpha0~30^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4e82abdec8e9df1fc058317d76bd579234ca90c9;p=thirdparty%2Fpdns.git Add record search capability to the LMDB backend. Fixes: #14079 Signed-off-by: Miod Vallat --- diff --git a/docs/backends/lmdb.rst b/docs/backends/lmdb.rst index a0dac24656..9c5e8fd671 100644 --- a/docs/backends/lmdb.rst +++ b/docs/backends/lmdb.rst @@ -11,7 +11,7 @@ LMDB backend * DNSSEC: Yes * Disabled data: Yes * Comments: No -* Search: No +* Search: since version 5.0.0 * Views: Yes * API: Read-Write * Multiple instances: No diff --git a/docs/upgrading.rst b/docs/upgrading.rst index 2b6cfdafe0..9c458a82cc 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -11,8 +11,8 @@ upgrade notes if your version is older than 3.4.2. 4.9.0 to 5.0.0/master --------------------- -LMDB backend, views and DNS Update support -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LMDB backend, views +^^^^^^^^^^^^^^^^^^^ Version 5.0.0-alpha1 ships a new version of the LMDB database schema (called version 6), in support of the new views feature. There is no downgrade process. @@ -25,8 +25,17 @@ While many things have been thoroughly tested, some loose ends likely remain. Specifically, catalog zones have not been updated for views support at all. Most other things are expected to work; if you find something wrong, please :ref:`let us know `. +LMDB backend, DNS Update support +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + The LMDB backend also now supports :doc:`DNS Update ` (RFC2136). +LMDB backend, search support +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The record search from the :doc:`HTTP API ` functionality has +been implemented in the LMDB backend. + LOC record parsing ^^^^^^^^^^^^^^^^^^ diff --git a/modules/lmdbbackend/lmdbbackend.cc b/modules/lmdbbackend/lmdbbackend.cc index 2f78de9f86..bc7ba9da26 100644 --- a/modules/lmdbbackend/lmdbbackend.cc +++ b/modules/lmdbbackend/lmdbbackend.cc @@ -1426,6 +1426,32 @@ bool LMDBBackend::replaceComments([[maybe_unused]] domainid_t domain_id, [[maybe return comments.empty(); } +bool LMDBBackend::searchRecords(const string& pattern, size_t maxResults, vector& result) +{ + SimpleMatch simpleMatch(pattern, true); + std::vector domains; + getAllDomains(&domains, false, true); + for (const auto& info : domains) { + if (!list(info.zone, info.id, true)) { + return false; + } + DNSResourceRecord rec; + while (get(rec)) { + if (maxResults == 0) { + continue; + } + if (simpleMatch.match(rec.qname.toStringNoDot()) || simpleMatch.match(rec.content)) { + result.emplace_back(rec); + --maxResults; + } + } + if (maxResults == 0) { + break; + } + } + return true; +} + // FIXME: this is not very efficient static DNSName keyUnconv(std::string& instr) { diff --git a/modules/lmdbbackend/lmdbbackend.hh b/modules/lmdbbackend/lmdbbackend.hh index c3997e9dfb..1071a50731 100644 --- a/modules/lmdbbackend/lmdbbackend.hh +++ b/modules/lmdbbackend/lmdbbackend.hh @@ -88,6 +88,7 @@ public: bool feedEnts3(domainid_t domain_id, const DNSName& domain, map& nonterm, const NSEC3PARAMRecordContent& ns3prc, bool narrow) override; bool replaceRRSet(domainid_t domain_id, const DNSName& qname, const QType& qt, const vector& rrset) override; bool replaceComments(domainid_t domain_id, const DNSName& qname, const QType& qt, const vector& comments) override; + bool searchRecords(const string& pattern, size_t maxResults, vector& result) override; void viewList(vector& /* result */) override; void viewListZones(const string& /* view */, vector& /* result */) override; diff --git a/regression-tests.api/test_Zones.py b/regression-tests.api/test_Zones.py index 8a4871a988..d85f1e3663 100644 --- a/regression-tests.api/test_Zones.py +++ b/regression-tests.api/test_Zones.py @@ -2118,7 +2118,6 @@ $NAME$ 1D IN SOA ns1.example.org. hostmaster.example.org. ( self.assertEqual(serverset['records'], rrset2['records']) self.assertEqual(serverset['comments'], rrset['comments']) - @unittest.skipIf(is_auth_lmdb(), "No search in LMDB") def test_search_rr_exact_zone(self): name = unique_zone_name() self.create_zone(name=name, serial=22, soa_edit_api='') @@ -2138,7 +2137,6 @@ $NAME$ 1D IN SOA ns1.example.org. hostmaster.example.org. ( u'ttl': 3600, u'type': u'SOA', u'name': name}, ]) - @unittest.skipIf(is_auth_lmdb(), "No search in LMDB") def test_search_rr_exact_zone_filter_type_zone(self): name = unique_zone_name() data_type = "zone" @@ -2150,7 +2148,6 @@ $NAME$ 1D IN SOA ns1.example.org. hostmaster.example.org. ( {u'object_type': u'zone', u'name': name, u'zone_id': name}, ]) - @unittest.skipIf(is_auth_lmdb(), "No search in LMDB") def test_search_rr_exact_zone_filter_type_record(self): name = unique_zone_name() data_type = "record" @@ -2170,7 +2167,6 @@ $NAME$ 1D IN SOA ns1.example.org. hostmaster.example.org. ( u'ttl': 3600, u'type': u'SOA', u'name': name}, ]) - @unittest.skipIf(is_auth_lmdb(), "No search in LMDB") def test_search_rr_substring(self): name = unique_zone_name() search = name[5:-5] @@ -2181,7 +2177,6 @@ $NAME$ 1D IN SOA ns1.example.org. hostmaster.example.org. ( # should return zone, SOA, ns1, ns2 self.assertEqual(len(r.json()), 4) - @unittest.skipIf(is_auth_lmdb(), "No search in LMDB") def test_search_rr_case_insensitive(self): name = unique_zone_name()+'testsuffix.' self.create_zone(name=name) @@ -2191,7 +2186,7 @@ $NAME$ 1D IN SOA ns1.example.org. hostmaster.example.org. ( # should return zone, SOA, ns1, ns2 self.assertEqual(len(r.json()), 4) - @unittest.skipIf(is_auth_lmdb(), "No search or comments in LMDB") + @unittest.skipIf(is_auth_lmdb(), "No comments in LMDB") def test_search_rr_comment(self): name = unique_zone_name() rrsets = [{ @@ -2219,7 +2214,6 @@ $NAME$ 1D IN SOA ns1.example.org. hostmaster.example.org. ( self.assertEqual(data[0]['name'], name) self.assertEqual(data[0]['content'], rrsets[0]['comments'][0]['content']) - @unittest.skipIf(is_auth_lmdb(), "No search in LMDB") def test_search_after_rectify_with_ent(self): name = unique_zone_name() search = name.split('.')[0]