Merge 'Fix updating single value' from Pedro Muniz

Closes #1482. I needed to change the `key_exists_in_index` function
because it zips the values from the records it is comparing, but if one
of the records is empty or not of the same length, the `all` function
could return true incorrectly.

Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #1514
This commit is contained in:
Jussi Saurio
2025-05-18 22:51:11 +03:00
2 changed files with 31 additions and 17 deletions

View File

@@ -3863,21 +3863,24 @@ impl BTreeCursor {
// Existing record found — compare prefix
let existing_key = &record.get_values()[..record.count().saturating_sub(1)];
let inserted_key_vals = &key.get_values();
if existing_key
.iter()
.zip(inserted_key_vals.iter())
.all(|(a, b)| a == b)
{
return Ok(CursorResult::Ok(true)); // duplicate
// Need this check because .all returns True on an empty iterator,
// So when record_opt is invalidated, it would always indicate show up as a duplicate key
if existing_key.len() != inserted_key_vals.len() {
return Ok(CursorResult::Ok(false));
}
Ok(CursorResult::Ok(
existing_key
.iter()
.zip(inserted_key_vals.iter())
.all(|(a, b)| a == b),
))
}
None => {
// Cursor not pointing at a record — table is empty or past last
return Ok(CursorResult::Ok(false));
Ok(CursorResult::Ok(false))
}
}
Ok(CursorResult::Ok(false)) // not a duplicate
}
pub fn exists(&mut self, key: &Value) -> Result<CursorResult<bool>> {

View File

@@ -347,6 +347,15 @@ def custom_test_2(limbo: TestLimboShell):
)
# Issue #1482
def regression_test_update_single_key(limbo: TestLimboShell):
create = "CREATE TABLE t(a unique);"
first_insert = "INSERT INTO t VALUES (1);"
limbo.run_test("Create simple table with 1 unique value", create + first_insert, "")
update_single = "UPDATE t SET a=1 WHERE a=1;"
limbo.run_test("Update one single key to the same value", update_single, "")
def all_tests() -> list[ConstraintTest]:
tests: list[ConstraintTest] = []
max_cols = 10
@@ -390,15 +399,17 @@ def main():
cleanup(db_path)
db_path = "testing/constraint.db"
try:
with TestLimboShell("") as limbo:
limbo.execute_dot(f".open {db_path}")
custom_test_2(limbo)
except Exception as e:
console.error(f"Test FAILED: {e}")
tests = [custom_test_2, regression_test_update_single_key]
for test in tests:
try:
with TestLimboShell("") as limbo:
limbo.execute_dot(f".open {db_path}")
test(limbo)
except Exception as e:
console.error(f"Test FAILED: {e}")
cleanup(db_path)
exit(1)
cleanup(db_path)
exit(1)
cleanup(db_path)
console.info("All tests passed successfully.")