From 10ee4d9652c906626f49d4e91b813911c4a34203 Mon Sep 17 00:00:00 2001 From: Vincent Weevers Date: Tue, 17 Sep 2019 22:14:17 +0300 Subject: [PATCH] Use reachdown package (Level/community#82) (#75) --- leveldown.js | 21 ++++---------- matchdown.js | 8 ++++++ package.json | 4 ++- test/index.js | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 matchdown.js diff --git a/leveldown.js b/leveldown.js index a07988e..cffa5c2 100644 --- a/leveldown.js +++ b/leveldown.js @@ -1,6 +1,8 @@ var inherits = require('inherits') var abstract = require('abstract-leveldown') var wrap = require('level-option-wrap') +var reachdown = require('reachdown') +var matchdown = require('./matchdown') var rangeOptions = 'start end gt gte lt lte'.split(' ') var defaultClear = abstract.AbstractLevelDOWN.prototype._clear @@ -83,13 +85,13 @@ SubDown.prototype._open = function (opts, cb) { this.db.open(function (err) { if (err) return cb(err) - var subdb = down(self.db, 'subleveldown') + var subdb = reachdown(self.db, 'subleveldown') if (subdb && subdb.prefix) { self.prefix = subdb.prefix + self.prefix - self.leveldown = down(subdb.db) + self.leveldown = reachdown(subdb.db, matchdown, false) } else { - self.leveldown = down(self.db) + self.leveldown = reachdown(self.db, matchdown, false) } if (self._beforeOpen) self._beforeOpen(cb) @@ -179,16 +181,3 @@ SubDown.prototype._iterator = function (opts) { } module.exports = SubDown - -function down (db, type) { - if (typeof db.down === 'function') return db.down(type) - if (type && db.type === type) return db - if (isLooseAbstract(db.db)) return down(db.db, type) - if (isLooseAbstract(db._db)) return down(db._db, type) - return type ? null : db -} - -function isLooseAbstract (db) { - if (!db || typeof db !== 'object') { return false } - return typeof db._batch === 'function' && typeof db._iterator === 'function' -} diff --git a/matchdown.js b/matchdown.js new file mode 100644 index 0000000..089ce39 --- /dev/null +++ b/matchdown.js @@ -0,0 +1,8 @@ +module.exports = function matchdown (db, type) { + // Skip layers that we handle ourselves + if (type === 'levelup') return false + if (type === 'encoding-down') return false + if (type === 'deferred-leveldown') return false + + return true +} diff --git a/package.json b/package.json index 4f18f5d..a00a235 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "encoding-down": "^6.2.0", "inherits": "^2.0.3", "level-option-wrap": "^1.1.0", - "levelup": "^4.2.0" + "levelup": "^4.2.0", + "reachdown": "^1.0.0" }, "devDependencies": { "after": "^0.8.2", @@ -30,6 +31,7 @@ "hallmark": "^2.0.0", "level-community": "^3.0.0", "level-concat-iterator": "^2.0.1", + "memdb": "^1.3.1", "memdown": "^5.0.0", "nyc": "^14.0.0", "standard": "^14.0.0", diff --git a/test/index.js b/test/index.js index a59feb1..e3503e9 100644 --- a/test/index.js +++ b/test/index.js @@ -7,6 +7,10 @@ var after = require('after') var subdown = require('../leveldown') var subdb = require('..') var levelup = require('levelup') +var reachdown = require('reachdown') +var memdb = require('memdb') +var abstract = require('abstract-leveldown') +var inherits = require('util').inherits // Test abstract-leveldown compliance suite({ @@ -359,6 +363,78 @@ test('SubDb main function', function (t) { } }) +// Test that we peel off the levelup, deferred-leveldown and encoding-down +// layers from db, but stop at any other intermediate layer like encrypt-down, +// cachedown, etc. +test('subleveldown on intermediate layer', function (t) { + t.plan(7) + + function Intermediate (db) { + abstract.AbstractLevelDOWN.call(this) + this.db = db + } + + inherits(Intermediate, abstract.AbstractLevelDOWN) + + Intermediate.prototype._put = function (key, value, options, callback) { + t.pass('got _put call') + this.db._put('mitm' + key, value, options, callback) + } + + Intermediate.prototype._get = function (key, options, callback) { + t.pass('got _get call') + this.db._get('mitm' + key, options, callback) + } + + var db = levelup(encoding(new Intermediate(memdown()))) + var sub = subdb(db, 'test') + + sub.put('key', 'value', function (err) { + t.error(err, 'no err') + + db.get('!test!key', function (err, value) { + t.ifError(err, 'no levelup get error') + t.is(value, 'value') + }) + + reachdown(db).get('mitm!test!key', { asBuffer: false }, function (err, value) { + t.ifError(err, 'no memdown get error') + t.is(value, 'value') + }) + }) +}) + +test('legacy memdb (old levelup)', function (t) { + t.plan(7) + + // Should not result in double json encoding + var db = memdb({ valueEncoding: 'json' }) + var sub = subdb(db, 'test', { valueEncoding: 'json' }) + + // Integration with memdb still works because subleveldown waits to reachdown + // until the (old levelup) db is open. Reaching down then correctly lands on + // the memdown db. If subleveldown were to reachdown immediately it'd land on + // the old deferred-leveldown (which when unopened doesn't have a reference to + // the memdown db yet) so we'd be unable to persist anything. + t.is(Object.getPrototypeOf(reachdown(db)).constructor.name, 'DeferredLevelDOWN') + + sub.put('key', { a: 1 }, function (err) { + t.ifError(err, 'no put error') + + sub.get('key', function (err, value) { + t.ifError(err, 'no get error') + t.same(value, { a: 1 }) + }) + + t.is(Object.getPrototypeOf(reachdown(db)).constructor.name, 'MemDOWN') + + reachdown(db).get('!test!key', { asBuffer: false }, function (err, value) { + t.ifError(err, 'no get error') + t.is(value, '{"a":1}') + }) + }) +}) + function getKey (entry) { return entry.key }