mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-06 08:44:23 +01:00
Handle issues with nested arguments
This commit is contained in:
@@ -54,5 +54,12 @@ macro_rules! bail_corrupt_error {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bail_constraint_error {
|
||||
($($arg:tt)*) => {
|
||||
return Err($crate::error::LimboError::Constraint(format!($($arg)*)))
|
||||
};
|
||||
}
|
||||
|
||||
pub const SQLITE_CONSTRAINT: usize = 19;
|
||||
pub const SQLITE_CONSTRAINT_PRIMARYKEY: usize = SQLITE_CONSTRAINT | (6 << 8);
|
||||
|
||||
@@ -50,13 +50,13 @@ pub fn get_json(json_value: &OwnedValue) -> crate::Result<OwnedValue> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn json_array(values: Vec<OwnedValue>) -> crate::Result<OwnedValue> {
|
||||
pub fn json_array(values: Vec<&OwnedValue>) -> crate::Result<OwnedValue> {
|
||||
let mut s = String::new();
|
||||
s.push('[');
|
||||
|
||||
for (idx, value) in values.iter().enumerate() {
|
||||
match value {
|
||||
OwnedValue::Blob(_) => crate::bail_parse_error!("JSON cannot hold BLOB values"),
|
||||
OwnedValue::Blob(_) => crate::bail_constraint_error!("JSON cannot hold BLOB values"),
|
||||
OwnedValue::Text(t) => {
|
||||
if t.subtype == TextSubtype::Json {
|
||||
s.push_str(&t.value);
|
||||
@@ -67,8 +67,15 @@ pub fn json_array(values: Vec<OwnedValue>) -> crate::Result<OwnedValue> {
|
||||
}
|
||||
}
|
||||
}
|
||||
OwnedValue::Integer(i) => s.push_str(&i.to_string()),
|
||||
OwnedValue::Float(f) => s.push_str(&f.to_string()),
|
||||
OwnedValue::Integer(i) => match crate::json::to_string(&i) {
|
||||
Ok(json) => s.push_str(&json),
|
||||
Err(_) => crate::bail_parse_error!("malformed JSON"),
|
||||
},
|
||||
OwnedValue::Float(f) => match crate::json::to_string(&f) {
|
||||
Ok(json) => s.push_str(&json),
|
||||
Err(_) => crate::bail_parse_error!("malformed JSON"),
|
||||
},
|
||||
OwnedValue::Null => s.push_str("null"),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
@@ -78,7 +85,7 @@ pub fn json_array(values: Vec<OwnedValue>) -> crate::Result<OwnedValue> {
|
||||
}
|
||||
|
||||
s.push(']');
|
||||
Ok(OwnedValue::build_text(Rc::new(s)))
|
||||
Ok(OwnedValue::Text(LimboText::json(Rc::new(s))))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -92,6 +99,7 @@ mod tests {
|
||||
let result = get_json(&input).unwrap();
|
||||
if let OwnedValue::Text(result_str) = result {
|
||||
assert!(result_str.value.contains("\"key\":\"value\""));
|
||||
assert_eq!(result_str.subtype, TextSubtype::Json);
|
||||
} else {
|
||||
panic!("Expected OwnedValue::Text");
|
||||
}
|
||||
@@ -103,6 +111,7 @@ mod tests {
|
||||
let result = get_json(&input).unwrap();
|
||||
if let OwnedValue::Text(result_str) = result {
|
||||
assert!(result_str.value.contains("\"key\":\"value\""));
|
||||
assert_eq!(result_str.subtype, TextSubtype::Json);
|
||||
} else {
|
||||
panic!("Expected OwnedValue::Text");
|
||||
}
|
||||
@@ -114,6 +123,7 @@ mod tests {
|
||||
let result = get_json(&input).unwrap();
|
||||
if let OwnedValue::Text(result_str) = result {
|
||||
assert!(result_str.value.contains("{\"key\":9e999}"));
|
||||
assert_eq!(result_str.subtype, TextSubtype::Json);
|
||||
} else {
|
||||
panic!("Expected OwnedValue::Text");
|
||||
}
|
||||
@@ -125,6 +135,7 @@ mod tests {
|
||||
let result = get_json(&input).unwrap();
|
||||
if let OwnedValue::Text(result_str) = result {
|
||||
assert!(result_str.value.contains("{\"key\":-9e999}"));
|
||||
assert_eq!(result_str.subtype, TextSubtype::Json);
|
||||
} else {
|
||||
panic!("Expected OwnedValue::Text");
|
||||
}
|
||||
@@ -136,6 +147,7 @@ mod tests {
|
||||
let result = get_json(&input).unwrap();
|
||||
if let OwnedValue::Text(result_str) = result {
|
||||
assert!(result_str.value.contains("{\"key\":null}"));
|
||||
assert_eq!(result_str.subtype, TextSubtype::Json);
|
||||
} else {
|
||||
panic!("Expected OwnedValue::Text");
|
||||
}
|
||||
@@ -157,6 +169,7 @@ mod tests {
|
||||
let result = get_json(&input).unwrap();
|
||||
if let OwnedValue::Text(result_str) = result {
|
||||
assert!(result_str.value.contains("\"key\":\"value\""));
|
||||
assert_eq!(result_str.subtype, TextSubtype::Json);
|
||||
} else {
|
||||
panic!("Expected OwnedValue::Text");
|
||||
}
|
||||
@@ -179,6 +192,7 @@ mod tests {
|
||||
let result = get_json(&input).unwrap();
|
||||
if let OwnedValue::Text(result_str) = result {
|
||||
assert!(result_str.value.contains("\"asd\":\"adf\""));
|
||||
assert_eq!(result_str.subtype, TextSubtype::Json);
|
||||
} else {
|
||||
panic!("Expected OwnedValue::Text");
|
||||
}
|
||||
@@ -210,11 +224,17 @@ mod tests {
|
||||
fn test_json_array_simple() {
|
||||
let text = OwnedValue::build_text(Rc::new("value1".to_string()));
|
||||
let json = OwnedValue::Text(LimboText::json(Rc::new("\"value2\"".to_string())));
|
||||
let input = vec![text, json, OwnedValue::Integer(1), OwnedValue::Float(1.1)];
|
||||
let input = vec![
|
||||
&text,
|
||||
&json,
|
||||
&OwnedValue::Integer(1),
|
||||
&OwnedValue::Float(1.1),
|
||||
];
|
||||
|
||||
let result = json_array(input).unwrap();
|
||||
if let OwnedValue::Text(res) = result {
|
||||
assert_eq!(res.value.as_str(), "[\"value1\",\"value2\",1,1.1]");
|
||||
assert_eq!(res.subtype, TextSubtype::Json);
|
||||
} else {
|
||||
panic!("Expected OwnedValue::Text");
|
||||
}
|
||||
@@ -227,6 +247,7 @@ mod tests {
|
||||
let result = json_array(input).unwrap();
|
||||
if let OwnedValue::Text(res) = result {
|
||||
assert_eq!(res.value.as_str(), "[]");
|
||||
assert_eq!(res.subtype, TextSubtype::Json);
|
||||
} else {
|
||||
panic!("Expected OwnedValue::Text");
|
||||
}
|
||||
@@ -236,7 +257,7 @@ mod tests {
|
||||
fn test_json_array_blob_invalid() {
|
||||
let blob = OwnedValue::Blob(Rc::new("1".as_bytes().to_vec()));
|
||||
|
||||
let input = vec![blob];
|
||||
let input = vec![&blob];
|
||||
|
||||
let result = json_array(input);
|
||||
|
||||
|
||||
@@ -899,7 +899,7 @@ pub fn translate_expr(
|
||||
Ok(target_register)
|
||||
}
|
||||
JsonFunc::JsonArray => {
|
||||
allocate_registers(
|
||||
let start_reg = translate_variable_sized_function_parameter_list(
|
||||
program,
|
||||
args,
|
||||
referenced_tables,
|
||||
@@ -908,7 +908,7 @@ pub fn translate_expr(
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: target_register + 1,
|
||||
start_reg,
|
||||
dest: target_register,
|
||||
func: func_ctx,
|
||||
});
|
||||
@@ -921,7 +921,7 @@ pub fn translate_expr(
|
||||
unreachable!("this is always ast::Expr::Cast")
|
||||
}
|
||||
ScalarFunc::Char => {
|
||||
allocate_registers(
|
||||
let start_reg = translate_variable_sized_function_parameter_list(
|
||||
program,
|
||||
args,
|
||||
referenced_tables,
|
||||
@@ -930,7 +930,7 @@ pub fn translate_expr(
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: target_register + 1,
|
||||
start_reg,
|
||||
dest: target_register,
|
||||
func: func_ctx,
|
||||
});
|
||||
@@ -1952,26 +1952,32 @@ pub fn translate_expr(
|
||||
}
|
||||
}
|
||||
|
||||
fn allocate_registers(
|
||||
// Returns the starting register for the function.
|
||||
// TODO: Use this function for all functions with variable number of parameters in `translate_expr`
|
||||
fn translate_variable_sized_function_parameter_list(
|
||||
program: &mut ProgramBuilder,
|
||||
args: &Option<Vec<ast::Expr>>,
|
||||
referenced_tables: Option<&[BTreeTableReference]>,
|
||||
precomputed_exprs_to_registers: Option<&Vec<(&ast::Expr, usize)>>,
|
||||
) -> Result<()> {
|
||||
let args = args.clone().unwrap_or_else(Vec::new);
|
||||
) -> Result<usize> {
|
||||
let args = args.as_deref().unwrap_or_default();
|
||||
|
||||
let reg = program.alloc_registers(args.len());
|
||||
let mut current_reg = reg;
|
||||
|
||||
for arg in args.iter() {
|
||||
let reg = program.alloc_register();
|
||||
translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
arg,
|
||||
reg,
|
||||
current_reg,
|
||||
precomputed_exprs_to_registers,
|
||||
)?;
|
||||
|
||||
current_reg += 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(reg)
|
||||
}
|
||||
|
||||
fn wrap_eval_jump_expr(
|
||||
|
||||
@@ -2267,8 +2267,9 @@ impl Program {
|
||||
}
|
||||
#[cfg(feature = "json")]
|
||||
crate::function::Func::Json(JsonFunc::JsonArray) => {
|
||||
let reg_values =
|
||||
state.registers[*start_reg..*start_reg + arg_count].to_vec();
|
||||
let reg_values = state.registers[*start_reg..*start_reg + arg_count]
|
||||
.iter()
|
||||
.collect();
|
||||
|
||||
let json_array = json_array(reg_values);
|
||||
|
||||
|
||||
@@ -60,10 +60,26 @@ do_execsql_test json_array_str {
|
||||
SELECT json_array('a')
|
||||
} {{["a"]}}
|
||||
|
||||
do_execsql_test json_array_numbers {
|
||||
SELECT json_array(1, 1.5)
|
||||
} {{[1,1.5]}}
|
||||
|
||||
do_execsql_test json_array_numbers_2 {
|
||||
SELECT json_array(1., +2., -2.)
|
||||
} {{[1.0,2.0,-2.0]}}
|
||||
|
||||
do_execsql_test json_array_null {
|
||||
SELECT json_array(null)
|
||||
} {{[null]}}
|
||||
|
||||
do_execsql_test json_array_not_json {
|
||||
SELECT json_array('{"a":1}');
|
||||
SELECT json_array('{"a":1}')
|
||||
} {{["{\"a\":1}"]}}
|
||||
|
||||
do_execsql_test json_array_json {
|
||||
SELECT json_array(json('{"a":1}'));
|
||||
SELECT json_array(json('{"a":1}'))
|
||||
} {{[{"a":1}]}}
|
||||
|
||||
do_execsql_test json_array_nested {
|
||||
SELECT json_array(json_array(1,2,3), json('[1,2,3]'), '[1,2,3]')
|
||||
} {{[[1,2,3],[1,2,3],"[1,2,3]"]}}
|
||||
|
||||
@@ -39,6 +39,10 @@ do_execsql_test char {
|
||||
select char(108, 105)
|
||||
} {li}
|
||||
|
||||
do_execsql_test char-nested {
|
||||
select char(106 + 2, 105)
|
||||
} {li}
|
||||
|
||||
do_execsql_test char-empty {
|
||||
select char()
|
||||
} {}
|
||||
|
||||
Reference in New Issue
Block a user