mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-23 08:55:40 +01:00
Add char function support
This commit is contained in:
@@ -63,7 +63,7 @@ This document describes the SQLite compatibility status of Limbo:
|
||||
|------------------------------|--------|---------|
|
||||
| abs(X) | Yes | |
|
||||
| changes() | No | |
|
||||
| char(X1,X2,...,XN) | No | |
|
||||
| char(X1,X2,...,XN) | Yes | |
|
||||
| coalesce(X,Y,...) | Yes | |
|
||||
| concat(X,...) | No | |
|
||||
| concat_ws(SEP,X,...) | No | |
|
||||
|
||||
@@ -40,6 +40,7 @@ impl AggFunc {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ScalarFunc {
|
||||
Char,
|
||||
Coalesce,
|
||||
Like,
|
||||
Abs,
|
||||
@@ -62,6 +63,7 @@ pub enum ScalarFunc {
|
||||
impl ToString for ScalarFunc {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
ScalarFunc::Char => "char".to_string(),
|
||||
ScalarFunc::Coalesce => "coalesce".to_string(),
|
||||
ScalarFunc::Like => "like(2)".to_string(),
|
||||
ScalarFunc::Abs => "abs".to_string(),
|
||||
@@ -103,6 +105,7 @@ impl Func {
|
||||
"string_agg" => Ok(Func::Agg(AggFunc::StringAgg)),
|
||||
"sum" => Ok(Func::Agg(AggFunc::Sum)),
|
||||
"total" => Ok(Func::Agg(AggFunc::Total)),
|
||||
"char" => Ok(Func::Scalar(ScalarFunc::Char)),
|
||||
"coalesce" => Ok(Func::Scalar(ScalarFunc::Coalesce)),
|
||||
"like" => Ok(Func::Scalar(ScalarFunc::Like)),
|
||||
"abs" => Ok(Func::Scalar(ScalarFunc::Abs)),
|
||||
|
||||
@@ -164,6 +164,28 @@ pub fn translate_expr(
|
||||
},
|
||||
Some(Func::Scalar(srf)) => {
|
||||
match srf {
|
||||
ScalarFunc::Char => {
|
||||
let args = if let Some(args) = args {
|
||||
args
|
||||
} else {
|
||||
crate::bail_parse_error!(
|
||||
"{} function with no arguments",
|
||||
srf.to_string()
|
||||
);
|
||||
};
|
||||
|
||||
for arg in args.iter() {
|
||||
let reg = program.alloc_register();
|
||||
translate_expr(program, select, arg, reg, cursor_hint)?;
|
||||
}
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
start_reg: target_register + 1,
|
||||
dest: target_register,
|
||||
func: crate::vdbe::Func::Scalar(srf),
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
ScalarFunc::Coalesce => {
|
||||
let args = if let Some(args) = args {
|
||||
if args.len() < 2 {
|
||||
|
||||
@@ -1185,6 +1185,12 @@ impl Program {
|
||||
}
|
||||
state.pc += 1;
|
||||
}
|
||||
Func::Scalar(ScalarFunc::Char) => {
|
||||
let start_reg = *start_reg;
|
||||
let reg_values = state.registers[start_reg..state.registers.len()].to_vec();
|
||||
state.registers[*dest] = exec_char(reg_values);
|
||||
state.pc += 1;
|
||||
}
|
||||
Func::Scalar(ScalarFunc::Coalesce) => {}
|
||||
Func::Scalar(ScalarFunc::Like) => {
|
||||
let start_reg = *start_reg;
|
||||
@@ -1636,6 +1642,20 @@ fn exec_random() -> OwnedValue {
|
||||
OwnedValue::Integer(random_number)
|
||||
}
|
||||
|
||||
fn exec_char(values: Vec<OwnedValue>) -> OwnedValue {
|
||||
let result: String = values
|
||||
.iter()
|
||||
.filter_map(|x| {
|
||||
if let OwnedValue::Integer(i) = x {
|
||||
Some(*i as u8 as char)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
OwnedValue::Text(Rc::new(result))
|
||||
}
|
||||
|
||||
// Implements LIKE pattern matching.
|
||||
fn exec_like(pattern: &str, text: &str) -> bool {
|
||||
let re = Regex::new(&pattern.replace('%', ".*").replace('_', ".").to_string()).unwrap();
|
||||
@@ -1798,12 +1818,14 @@ fn exec_if(reg: &OwnedValue, null_reg: &OwnedValue, not: bool) -> bool {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
exec_abs, exec_if, exec_length, exec_like, exec_lower, exec_ltrim, exec_minmax,
|
||||
exec_abs, exec_char, exec_if, exec_length, exec_like, exec_lower, exec_ltrim, exec_minmax,
|
||||
exec_random, exec_round, exec_rtrim, exec_substring, exec_trim, exec_unicode, exec_upper,
|
||||
get_new_rowid, Cursor, CursorResult, LimboError, OwnedRecord, OwnedValue, Result,
|
||||
};
|
||||
use mockall::{mock, predicate, predicate::*};
|
||||
use mockall::{mock, predicate};
|
||||
use rand::{rngs::mock::StepRng, thread_rng};
|
||||
use rusqlite::types::ToSqlOutput::Owned;
|
||||
use rustix::path::Arg;
|
||||
use std::{cell::Ref, rc::Rc};
|
||||
|
||||
mock! {
|
||||
@@ -2118,6 +2140,24 @@ mod tests {
|
||||
);
|
||||
assert_eq!(exec_abs(&OwnedValue::Null).unwrap(), OwnedValue::Null);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char() {
|
||||
assert_eq!(
|
||||
exec_char(vec![OwnedValue::Integer(108), OwnedValue::Integer(105)]),
|
||||
OwnedValue::Text(Rc::new("li".to_string()))
|
||||
);
|
||||
assert_eq!(exec_char(vec![]), OwnedValue::Text(Rc::new("".to_string())));
|
||||
assert_eq!(
|
||||
exec_char(vec![OwnedValue::Null]),
|
||||
OwnedValue::Text(Rc::new("".to_string()))
|
||||
);
|
||||
assert_eq!(
|
||||
exec_char(vec![OwnedValue::Text(Rc::new("a".to_string()))]),
|
||||
OwnedValue::Text(Rc::new("".to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_like() {
|
||||
assert!(exec_like("a%", "aaaa"));
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_execsql_test char {
|
||||
select char(108, 105)
|
||||
} {li}
|
||||
|
||||
do_execsql_test char-empty {
|
||||
select char()
|
||||
} {}
|
||||
|
||||
do_execsql_test char-null {
|
||||
select char(null)
|
||||
} {}
|
||||
|
||||
do_execsql_test char-non-integer {
|
||||
select char('a')
|
||||
} {}
|
||||
|
||||
do_execsql_test abs {
|
||||
select abs(1);
|
||||
} {1}
|
||||
|
||||
Reference in New Issue
Block a user