From 533e33131f1d697375f35fb6b7e1be5b9d84d4bf Mon Sep 17 00:00:00 2001 From: Heyang Zhou Date: Thu, 23 Mar 2023 04:53:16 +0800 Subject: [PATCH] fix(ext/kv): reverse mapping between `AnyValue::Bool` and `KeyPart::Bool` (#18365) Previously the mapping between `AnyValue::Bool` and `KeyPart::Bool` was inverted. This patch also allows using the empty key `[]` as range start/end to `snapshot_read`. --- cli/tests/unit/kv_test.ts | 28 ++++++++++++++++++++++++++++ ext/kv/codec.rs | 16 ---------------- ext/kv/lib.rs | 18 ++++++++++++++---- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/cli/tests/unit/kv_test.ts b/cli/tests/unit/kv_test.ts index 256060ccc7..1c0eed0021 100644 --- a/cli/tests/unit/kv_test.ts +++ b/cli/tests/unit/kv_test.ts @@ -600,6 +600,14 @@ dbTest("list prefix with end empty", async (db) => { assertEquals(entries.length, 0); }); +dbTest("list prefix with empty prefix", async (db) => { + await db.set(["a"], 1); + const entries = await collect(db.list({ prefix: [] })); + assertEquals(entries, [ + { key: ["a"], value: 1, versionstamp: "00000000000000010000" }, + ]); +}); + dbTest("list prefix reverse", async (db) => { await setupData(db); @@ -966,3 +974,23 @@ dbTest("invalid mutation type rejects", async (db) => { .commit(); }, TypeError); }); + +dbTest("key ordering", async (db) => { + await db.atomic() + .set([new Uint8Array(0x1)], 0) + .set(["a"], 0) + .set([1n], 0) + .set([3.14], 0) + .set([false], 0) + .set([true], 0) + .commit(); + + assertEquals((await collect(db.list({ prefix: [] }))).map((x) => x.key), [ + [new Uint8Array(0x1)], + ["a"], + [1n], + [3.14], + [false], + [true], + ]); +}); diff --git a/ext/kv/codec.rs b/ext/kv/codec.rs index b2acfdbc20..522c2e9bc0 100644 --- a/ext/kv/codec.rs +++ b/ext/kv/codec.rs @@ -34,14 +34,6 @@ pub fn canonicalize_f64(n: f64) -> f64 { } pub fn encode_key(key: &Key) -> std::io::Result> { - // Disallow empty key - if key.0.is_empty() { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "key should not be empty", - )); - } - let mut output: Vec = vec![]; for part in &key.0 { match part { @@ -73,14 +65,6 @@ pub fn encode_key(key: &Key) -> std::io::Result> { } pub fn decode_key(mut bytes: &[u8]) -> std::io::Result { - // Disallow empty key - if bytes.is_empty() { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "key should not be empty", - )); - } - let mut key = Key(vec![]); while !bytes.is_empty() { let tag = bytes[0]; diff --git a/ext/kv/lib.rs b/ext/kv/lib.rs index 49a59af747..2d4bae6fcf 100644 --- a/ext/kv/lib.rs +++ b/ext/kv/lib.rs @@ -102,8 +102,8 @@ type KvKey = Vec; impl From for KeyPart { fn from(value: AnyValue) -> Self { match value { - AnyValue::Bool(false) => KeyPart::True, - AnyValue::Bool(true) => KeyPart::False, + AnyValue::Bool(false) => KeyPart::False, + AnyValue::Bool(true) => KeyPart::True, AnyValue::Number(n) => KeyPart::Float(n), AnyValue::BigInt(n) => KeyPart::Int(n), AnyValue::String(s) => KeyPart::String(s), @@ -115,8 +115,8 @@ impl From for KeyPart { impl From for AnyValue { fn from(value: KeyPart) -> Self { match value { - KeyPart::True => AnyValue::Bool(false), - KeyPart::False => AnyValue::Bool(true), + KeyPart::False => AnyValue::Bool(false), + KeyPart::True => AnyValue::Bool(true), KeyPart::Float(n) => AnyValue::Number(n), KeyPart::Int(n) => AnyValue::BigInt(n), KeyPart::String(s) => AnyValue::String(s), @@ -499,6 +499,16 @@ where resource.db.clone() }; + for key in checks + .iter() + .map(|c| &c.0) + .chain(mutations.iter().map(|m| &m.0)) + { + if key.is_empty() { + return Err(type_error("key cannot be empty")); + } + } + let checks = checks .into_iter() .map(TryInto::try_into)