Add char function support

This commit is contained in:
김선우
2024-08-16 06:26:06 +09:00
parent 39e77bac9f
commit 77c3d130f3
5 changed files with 84 additions and 3 deletions

View File

@@ -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 | |

View File

@@ -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)),

View File

@@ -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 {

View File

@@ -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"));

View File

@@ -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}