Skip to content

Commit e73787e

Browse files
cursoragentlovasoa
andcommitted
Refactor: Improve run_sql variable parsing
Co-authored-by: contact <[email protected]>
1 parent 3276b5a commit e73787e

File tree

1 file changed

+58
-1
lines changed

1 file changed

+58
-1
lines changed

src/webserver/database/sqlpage_functions/functions.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,63 @@ async fn request_method(request: &RequestInfo) -> String {
548548
request.method.to_string()
549549
}
550550

551+
fn parse_run_sql_variables(raw: &str) -> anyhow::Result<ParamMap> {
552+
let value: serde_json::Value =
553+
serde_json::from_str(raw).with_context(|| "run_sql: unable to parse variables as JSON")?;
554+
let object = value.as_object().ok_or_else(|| {
555+
anyhow!(
556+
"run_sql: the second argument must be a JSON object whose values are strings or arrays of strings. Example: {{\"name\": \"Alice\", \"store_ids\": [\"1\", \"2\"]}}"
557+
)
558+
})?;
559+
let mut parsed = ParamMap::with_capacity(object.len());
560+
for (key, value) in object {
561+
let entry = match value {
562+
serde_json::Value::String(s) => SingleOrVec::Single(s.clone()),
563+
serde_json::Value::Array(values) => {
564+
let mut strings = Vec::with_capacity(values.len());
565+
for (idx, item) in values.iter().enumerate() {
566+
let Some(string_value) = item.as_str() else {
567+
anyhow::bail!(
568+
"run_sql: variable {key:?} must be an array of strings. Item at index {idx} is {item}"
569+
);
570+
};
571+
strings.push(string_value.to_owned());
572+
}
573+
SingleOrVec::Vec(strings)
574+
}
575+
_ => {
576+
anyhow::bail!(
577+
"run_sql: variable {key:?} must be a string or an array of strings, but found {value}"
578+
);
579+
}
580+
};
581+
parsed.insert(key.clone(), entry);
582+
}
583+
Ok(parsed)
584+
}
585+
586+
#[test]
587+
fn parse_run_sql_variables_accepts_strings_and_arrays() {
588+
let vars =
589+
parse_run_sql_variables(r#"{"city":"Paris","ids":["1","2"]}"#).expect("valid variables");
590+
assert_eq!(
591+
vars.get("city"),
592+
Some(&SingleOrVec::Single("Paris".to_string()))
593+
);
594+
assert_eq!(
595+
vars.get("ids"),
596+
Some(&SingleOrVec::Vec(vec!["1".to_string(), "2".to_string()]))
597+
);
598+
}
599+
600+
#[test]
601+
fn parse_run_sql_variables_rejects_invalid_values() {
602+
let err = parse_run_sql_variables(r#"{"city":1}"#).expect_err("should fail");
603+
let err_string = err.to_string();
604+
assert!(err_string.contains(r#"variable "city""#));
605+
assert!(err_string.contains("string or an array of strings"));
606+
}
607+
551608
async fn run_sql<'a>(
552609
request: &'a RequestInfo,
553610
db_connection: &mut DbConn,
@@ -571,7 +628,7 @@ async fn run_sql<'a>(
571628
.with_context(|| format!("run_sql: invalid path {sql_file_path:?}"))?;
572629
let mut tmp_req = if let Some(variables) = variables {
573630
let mut tmp_req = request.clone_without_variables();
574-
let variables: ParamMap = serde_json::from_str(&variables)?;
631+
let variables = parse_run_sql_variables(&variables)?;
575632
tmp_req.get_variables = variables;
576633
tmp_req
577634
} else {

0 commit comments

Comments
 (0)