Merge 'Add zeroblob(N) scalar function' from Lauri Virtanen

Relates to issue #144

Closes #352
This commit is contained in:
Pekka Enberg
2024-10-02 10:59:22 +03:00
5 changed files with 79 additions and 4 deletions

View File

@@ -117,7 +117,7 @@ This document describes the SQLite compatibility status of Limbo:
| unicode(X) | Yes | |
| unlikely(X) | No | |
| upper(X) | Yes | |
| zeroblob(N) | No | |
| zeroblob(N) | Yes | |
### Aggregate functions

View File

@@ -77,6 +77,7 @@ pub enum ScalarFunc {
SqliteVersion,
UnixEpoch,
Hex,
ZeroBlob,
}
impl Display for ScalarFunc {
@@ -112,6 +113,7 @@ impl Display for ScalarFunc {
ScalarFunc::SqliteVersion => "sqlite_version".to_string(),
ScalarFunc::UnixEpoch => "unixepoch".to_string(),
ScalarFunc::Hex => "hex".to_string(),
ScalarFunc::ZeroBlob => "zeroblob".to_string(),
};
write!(f, "{}", str)
}
@@ -182,6 +184,7 @@ impl Func {
"json" => Ok(Func::Json(JsonFunc::Json)),
"unixepoch" => Ok(Func::Scalar(ScalarFunc::UnixEpoch)),
"hex" => Ok(Func::Scalar(ScalarFunc::Hex)),
"zeroblob" => Ok(Func::Scalar(ScalarFunc::ZeroBlob)),
_ => Err(()),
}
}

View File

@@ -1006,7 +1006,8 @@ pub fn translate_expr(
| ScalarFunc::Typeof
| ScalarFunc::Unicode
| ScalarFunc::Quote
| ScalarFunc::Sign => {
| ScalarFunc::Sign
| ScalarFunc::ZeroBlob => {
let args = if let Some(args) = args {
if args.len() != 1 {
crate::bail_parse_error!(

View File

@@ -1531,7 +1531,8 @@ impl Program {
| ScalarFunc::Typeof
| ScalarFunc::Unicode
| ScalarFunc::Quote
| ScalarFunc::Sign => {
| ScalarFunc::Sign
| ScalarFunc::ZeroBlob => {
let reg_value = state.registers[*start_reg].borrow_mut();
let result = match scalar_func {
ScalarFunc::Sign => exec_sign(reg_value),
@@ -1542,6 +1543,7 @@ impl Program {
ScalarFunc::Typeof => Some(exec_typeof(reg_value)),
ScalarFunc::Unicode => Some(exec_unicode(reg_value)),
ScalarFunc::Quote => Some(exec_quote(reg_value)),
ScalarFunc::ZeroBlob => Some(exec_zeroblob(reg_value)),
_ => unreachable!(),
};
state.registers[*dest] = result.unwrap_or(OwnedValue::Null);
@@ -2264,6 +2266,16 @@ fn exec_rtrim(reg: &OwnedValue, pattern: Option<OwnedValue>) -> OwnedValue {
}
}
fn exec_zeroblob(req: &OwnedValue) -> OwnedValue {
let length: i64 = match req {
OwnedValue::Integer(i) => *i,
OwnedValue::Float(f) => *f as i64,
OwnedValue::Text(s) => s.parse().unwrap_or(0),
_ => 0,
};
OwnedValue::Blob(Rc::new(vec![0; length.max(0) as usize]))
}
// exec_if returns whether you should jump
fn exec_if(reg: &OwnedValue, null_reg: &OwnedValue, not: bool) -> bool {
match reg {
@@ -2292,7 +2304,7 @@ mod tests {
use super::{
exec_abs, exec_char, exec_hex, exec_if, exec_length, exec_like, exec_lower, exec_ltrim,
exec_max, exec_min, exec_nullif, exec_quote, exec_random, exec_round, exec_rtrim,
exec_sign, exec_substring, exec_trim, exec_typeof, exec_unicode, exec_upper,
exec_sign, exec_substring, exec_trim, exec_typeof, exec_unicode, exec_upper, exec_zeroblob,
execute_sqlite_version, get_new_rowid, AggContext, Cursor, CursorResult, LimboError,
OwnedRecord, OwnedValue, Result,
};
@@ -2925,6 +2937,45 @@ mod tests {
assert_eq!(exec_sign(&input), expected);
}
#[test]
fn test_exec_zeroblob() {
let input = OwnedValue::Integer(0);
let expected = OwnedValue::Blob(Rc::new(vec![]));
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Null;
let expected = OwnedValue::Blob(Rc::new(vec![]));
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Integer(4);
let expected = OwnedValue::Blob(Rc::new(vec![0; 4]));
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Integer(-1);
let expected = OwnedValue::Blob(Rc::new(vec![]));
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Text(Rc::new("5".to_string()));
let expected = OwnedValue::Blob(Rc::new(vec![0; 5]));
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Text(Rc::new("-5".to_string()));
let expected = OwnedValue::Blob(Rc::new(vec![]));
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Text(Rc::new("text".to_string()));
let expected = OwnedValue::Blob(Rc::new(vec![]));
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Float(2.6);
let expected = OwnedValue::Blob(Rc::new(vec![0; 2]));
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Blob(Rc::new(vec![1]));
let expected = OwnedValue::Blob(Rc::new(vec![]));
assert_eq!(exec_zeroblob(&input), expected);
}
#[test]
fn test_execute_sqlite_version() {
let version_integer = 3046001;

View File

@@ -514,3 +514,23 @@ do_execsql_test sign-text-non-numeric {
do_execsql_test sign-null {
SELECT sign(NULL);
} {}
do_execsql_test zeroblob-int-0 {
SELECT zeroblob(0) = x'';
} {1}
do_execsql_test zeroblob-int-1 {
SELECT zeroblob(1) = x'00';
} {1}
do_execsql_test zeroblob-str-3 {
SELECT zeroblob('3') = x'000000';
} {1}
do_execsql_test zeroblob-str-a {
SELECT zeroblob('a') = x'';
} {1}
do_execsql_test zeroblob-blob {
SELECT zeroblob(x'01') = x'';
} {1}