Handle issues with nested arguments

This commit is contained in:
Kacper Madej
2024-12-18 15:35:10 +01:00
parent 19ae42dfa3
commit cdb24d3de1
6 changed files with 76 additions and 21 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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()
} {}