Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add clear() #310

Merged
merged 4 commits into from
Aug 18, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,19 @@ In addition to range options, `iterator()` takes the following options:

Lastly, an implementation is free to add its own options.

### `db.clear([options, ]callback)`

**This method is experimental. Not all implementations support it yet.**

Delete all entries or a range. Not guaranteed to be atomic. Accepts the following range options (with the same rules as on iterators):

- `gt` (greater than), `gte` (greater than or equal) define the lower bound of the range to be deleted. Only entries where the key is greater than (or equal to) this option will be included in the range. When `reverse=true` the order will be reversed, but the entries deleted will be the same.
- `lt` (less than), `lte` (less than or equal) define the higher bound of the range to be deleted. Only entries where the key is less than (or equal to) this option will be included in the range. When `reverse=true` the order will be reversed, but the entries deleted will be the same.
- `reverse` _(boolean, default: `false`)_: delete entries in reverse order. Only effective in combination with `limit`, to remove the last N records.
- `limit` _(number, default: `-1`)_: limit the number of entries to be deleted. This number represents a _maximum_ number of entries and may not be reached if you get to the end of the range first. A value of `-1` means there is no limit. When `reverse=true` the entries with the highest keys will be deleted instead of the lowest keys.

If no options are provided, all entries will be deleted. The `callback` function will be called with no arguments if the operation was successful or with an `Error` if it failed for any reason.

### `chainedBatch`

#### `chainedBatch.put(key, value)`
Expand Down Expand Up @@ -356,6 +369,18 @@ The default `_iterator()` returns a noop `AbstractIterator` instance. The protot

The `options` object will always have the following properties: `reverse`, `keys`, `values`, `limit`, `keyAsBuffer` and `valueAsBuffer`.

### `db._clear(options, callback)`

**This method is experimental and optional for the time being. To enable its tests, set the [`clear` option of the test suite](#excluding-tests) to `true`.**

Delete all entries or a range. Does not have to be atomic. It is recommended (and possibly mandatory in the future) to operate on a snapshot so that writes scheduled after a call to `clear()` will not be affected.

The default `_clear()` uses `_iterator()` and `_del()` to provide a reasonable fallback, but requires binary key support. It is _recommended_ to implement `_clear()` with more performant primitives than `_iterator()` and `_del()` if the underlying storage has such primitives. Implementations that don't support binary keys _must_ implement their own `_clear()`.

Implementations that wrap another `db` can typically forward the `_clear()` call to that `db`, having transformed range options if necessary.

The `options` object will always have the following properties: `reverse` and `limit`.

### `iterator = AbstractIterator(db)`

The first argument to this constructor must be an instance of your `AbstractLevelDOWN` implementation. The constructor will set `iterator.db` which is used to access `db._serialize*` and ensures that `db` will not be garbage collected in case there are no other references to it.
Expand Down Expand Up @@ -442,6 +467,7 @@ This also serves as a signal to users of your implementation. The following opti

- `bufferKeys`: set to `false` if binary keys are not supported by the underlying storage
- `seek`: set to `false` if your `iterator` does not implement `_seek`
- `clear`: defaults to `false` until a next major release. Set to `true` if your implementation either implements `_clear()` itself or is suitable to use the default implementation of `_clear()` (which requires binary key support).
- `snapshots`: set to `false` if any of the following is true:
- Reads don't operate on a [snapshot](#iterator)
- Snapshots are created asynchronously
Expand Down
46 changes: 46 additions & 0 deletions abstract-leveldown.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,52 @@ AbstractLevelDOWN.prototype._batch = function (array, options, callback) {
process.nextTick(callback)
}

AbstractLevelDOWN.prototype.clear = function (options, callback) {
if (typeof options === 'function') {
callback = options
} else if (typeof callback !== 'function') {
throw new Error('clear() requires a callback argument')
}

options = cleanRangeOptions(this, options)
options.reverse = !!options.reverse
options.limit = 'limit' in options ? options.limit : -1

this._clear(options, callback)
}

AbstractLevelDOWN.prototype._clear = function (options, callback) {
vweevers marked this conversation as resolved.
Show resolved Hide resolved
// Avoid setupIteratorOptions, would serialize range options a second time.
options.keys = true
options.values = false
options.keyAsBuffer = true
options.valueAsBuffer = true

var iterator = this._iterator(options)
var emptyOptions = {}
var self = this

var next = function (err) {
if (err) {
return iterator.end(function () {
callback(err)
})
}

iterator.next(function (err, key) {
if (err) return next(err)
if (key === undefined) return iterator.end(callback)

// This could be optimized by using a batch, but the default _clear
// is not meant to be fast. Implementations have more room to optimize
// if they override _clear. Note: using _del bypasses key serialization.
self._del(key, emptyOptions, next)
})
}

next()
}

AbstractLevelDOWN.prototype._setupIteratorOptions = function (options) {
options = cleanRangeOptions(this, options)

Expand Down
23 changes: 22 additions & 1 deletion test/common.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
var warned = false

function testCommon (options) {
var factory = options.factory
var test = options.test
var clear = !!options.clear

if (typeof factory !== 'function') {
throw new TypeError('factory must be a function')
Expand All @@ -10,6 +13,15 @@ function testCommon (options) {
throw new TypeError('test must be a function')
}

if (!clear && !warned) {
warned = true
warn(
'A next major release of abstract-leveldown will make support of ' +
'clear() mandatory. Prepare by enabling the tests and implementing a ' +
'custom _clear() if necessary. See the README for details.'
)
}

return {
test: test,
factory: factory,
Expand All @@ -19,7 +31,16 @@ function testCommon (options) {
createIfMissing: options.createIfMissing !== false,
errorIfExists: options.errorIfExists !== false,
snapshots: options.snapshots !== false,
seek: options.seek !== false
seek: options.seek !== false,
clear: clear
}
}

function warn (msg) {
if (typeof process !== 'undefined' && process && process.emitWarning) {
process.emitWarning(msg)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL!

} else if (typeof console !== 'undefined' && console && console.warn) {
console.warn('Warning: ' + msg)
}
}

Expand Down
4 changes: 4 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ function suite (options) {
} else {
require('./iterator-no-snapshot-test').all(test, testCommon)
}

if (testCommon.clear) {
// TODO
}
}

suite.common = common
Expand Down