Merge 'Add support for hex scalar function' from baishen

Add support for hex scalar function

```
limbo> SELECT hex('abc');
616263

limbo> EXPLAIN SELECT hex('abc');
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     5     0                    0   Start at 5
1     String8            0     2     0     abc            0   r[2]='abc'
2     Function           0     2     1     hex            0   r[1]=func(r[2])
3     ResultRow          1     1     0                    0   output=r[1]
4     Halt               0     0     0                    0
5     Transaction        0     0     0                    0
6     Goto               0     1     0                    0
```

Related issue #144

Closes #342
This commit is contained in:
Pekka Enberg
2024-09-22 08:50:26 -04:00
7 changed files with 86 additions and 2 deletions

View File

@@ -69,7 +69,7 @@ This document describes the SQLite compatibility status of Limbo:
| concat_ws(SEP,X,...) | Yes | |
| format(FORMAT,...) | No | |
| glob(X,Y) | Yes | |
| hex(X) | No | |
| hex(X) | Yes | |
| ifnull(X,Y) | Yes | |
| iif(X,Y,Z) | No | |
| instr(X,Y) | No | |

7
Cargo.lock generated
View File

@@ -893,6 +893,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "home"
version = "0.5.9"
@@ -1117,6 +1123,7 @@ dependencies = [
"criterion",
"fallible-iterator 0.3.0",
"getrandom",
"hex",
"indexmap",
"io-uring",
"jsonb",

View File

@@ -30,6 +30,7 @@ mimalloc = { version = "*", default-features = false }
[dependencies]
cfg_block = "0.1.1"
fallible-iterator = "0.3.0"
hex = "0.4.3"
libc = "0.2.155"
log = "0.4.20"
nix = { version = "0.29.0", features = ["fs"] }

View File

@@ -76,6 +76,7 @@ pub enum ScalarFunc {
Quote,
SqliteVersion,
UnixEpoch,
Hex,
}
impl Display for ScalarFunc {
@@ -110,6 +111,7 @@ impl Display for ScalarFunc {
ScalarFunc::Quote => "quote".to_string(),
ScalarFunc::SqliteVersion => "sqlite_version".to_string(),
ScalarFunc::UnixEpoch => "unixepoch".to_string(),
ScalarFunc::Hex => "hex".to_string(),
};
write!(f, "{}", str)
}
@@ -179,6 +181,7 @@ impl Func {
"sqlite_version" => Ok(Func::Scalar(ScalarFunc::SqliteVersion)),
"json" => Ok(Func::Json(JsonFunc::Json)),
"unixepoch" => Ok(Func::Scalar(ScalarFunc::UnixEpoch)),
"hex" => Ok(Func::Scalar(ScalarFunc::Hex)),
_ => Err(()),
}
}

View File

@@ -1133,6 +1133,34 @@ pub fn translate_expr(
});
Ok(target_register)
}
ScalarFunc::Hex => {
let args = if let Some(args) = args {
if args.len() != 1 {
crate::bail_parse_error!(
"hex function must have exactly 1 argument",
);
}
args
} else {
crate::bail_parse_error!("hex function with no arguments",);
};
let regs = program.alloc_register();
translate_expr(
program,
referenced_tables,
&args[0],
regs,
cursor_hint,
cached_results,
)?;
program.emit_insn(Insn::Function {
constant_mask: 0,
start_reg: regs,
dest: target_register,
func: func_ctx,
});
Ok(target_register)
}
ScalarFunc::UnixEpoch => {
let mut start_reg = 0;
match args {

View File

@@ -1536,6 +1536,11 @@ impl Program {
};
state.registers[*dest] = result.unwrap_or(OwnedValue::Null);
}
ScalarFunc::Hex => {
let reg_value = state.registers[*start_reg].borrow_mut();
let result = exec_hex(reg_value);
state.registers[*dest] = result;
}
ScalarFunc::Random => {
state.registers[*dest] = exec_random();
}
@@ -2146,6 +2151,19 @@ fn exec_typeof(reg: &OwnedValue) -> OwnedValue {
}
}
fn exec_hex(reg: &OwnedValue) -> OwnedValue {
match reg {
OwnedValue::Text(_)
| OwnedValue::Integer(_)
| OwnedValue::Float(_)
| OwnedValue::Blob(_) => {
let text = reg.to_string();
OwnedValue::Text(Rc::new(hex::encode_upper(text)))
}
_ => OwnedValue::Null,
}
}
fn exec_unicode(reg: &OwnedValue) -> OwnedValue {
match reg {
OwnedValue::Text(_)
@@ -2265,7 +2283,7 @@ fn execute_sqlite_version(version_integer: i64) -> String {
mod tests {
use super::{
exec_abs, exec_char, exec_if, exec_length, exec_like, exec_lower, exec_ltrim, exec_minmax,
exec_abs, exec_char, exec_hex, exec_if, exec_length, exec_like, exec_lower, exec_ltrim, exec_minmax,
exec_nullif, exec_quote, exec_random, exec_round, exec_rtrim, exec_sign, exec_substring,
exec_trim, exec_typeof, exec_unicode, exec_upper, execute_sqlite_version, get_new_rowid,
AggContext, Cursor, CursorResult, LimboError, OwnedRecord, OwnedValue, Result,
@@ -2610,6 +2628,21 @@ mod tests {
assert_eq!(exec_lower(&OwnedValue::Null).unwrap(), OwnedValue::Null)
}
#[test]
fn test_hex() {
let input_str = OwnedValue::Text(Rc::new("limbo".to_string()));
let expected_val = OwnedValue::Text(Rc::new(String::from("6C696D626F")));
assert_eq!(exec_hex(&input_str), expected_val);
let input_int = OwnedValue::Integer(100);
let expected_val = OwnedValue::Text(Rc::new(String::from("313030")));
assert_eq!(exec_hex(&input_int), expected_val);
let input_float = OwnedValue::Float(12.34);
let expected_val = OwnedValue::Text(Rc::new(String::from("31322E3334")));
assert_eq!(exec_hex(&input_float), expected_val);
}
#[test]
fn test_abs() {
let int_positive_reg = OwnedValue::Integer(10);

View File

@@ -107,6 +107,18 @@ do_execsql_test lower-null {
select lower(null)
} {}
do_execsql_test hex {
select hex('limbo')
} {6C696D626F}
do_execsql_test hex-number {
select hex(100)
} {313030}
do_execsql_test hex-null {
select hex(null)
} {}
do_execsql_test trim {
SELECT trim(' Limbo ');
} {Limbo}