Skip to content

Commit 1c5fb16

Browse files
committed
add a native sqlpage implementation of json_array, which allows passing json arrays to nested sqlpage functions
1 parent 9fe908e commit 1c5fb16

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

src/webserver/database/sql.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,10 @@ fn expr_to_stmt_param(arg: &mut Expr) -> Option<StmtParam> {
609609
let right = expr_to_stmt_param(right)?;
610610
Some(StmtParam::Concat(vec![left, right]))
611611
}
612+
// SQLPage can evaluate some functions natively without sending them to the database:
612613
// CONCAT('str1', 'str2', ...)
614+
// json_object('key1', 'value1', 'key2', 'value2', ...)
615+
// json_array('value1', 'value2', ...)
613616
Expr::Function(Function {
614617
name: ObjectName(func_name_parts),
615618
args:
@@ -628,13 +631,25 @@ fn expr_to_stmt_param(arg: &mut Expr) -> Option<StmtParam> {
628631
}
629632
Some(StmtParam::Concat(concat_args))
630633
} else if func_name.eq_ignore_ascii_case("json_object")
634+
|| func_name.eq_ignore_ascii_case("jsonb_object")
631635
|| func_name.eq_ignore_ascii_case("json_build_object")
636+
|| func_name.eq_ignore_ascii_case("jsonb_build_object")
632637
{
633638
let mut json_obj_args = Vec::with_capacity(args.len());
634639
for arg in args {
635640
json_obj_args.push(function_arg_to_stmt_param(arg)?);
636641
}
637642
Some(StmtParam::JsonObject(json_obj_args))
643+
} else if func_name.eq_ignore_ascii_case("json_array")
644+
|| func_name.eq_ignore_ascii_case("jsonb_array")
645+
|| func_name.eq_ignore_ascii_case("json_build_array")
646+
|| func_name.eq_ignore_ascii_case("jsonb_build_array")
647+
{
648+
let mut json_obj_args = Vec::with_capacity(args.len());
649+
for arg in args {
650+
json_obj_args.push(function_arg_to_stmt_param(arg)?);
651+
}
652+
Some(StmtParam::JsonArray(json_obj_args))
638653
} else {
639654
log::warn!("SQLPage cannot emulate the following function: {func_name}");
640655
None

src/webserver/database/syntax_tree.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub(crate) enum StmtParam {
3939
Null,
4040
Concat(Vec<StmtParam>),
4141
JsonObject(Vec<StmtParam>),
42+
JsonArray(Vec<StmtParam>),
4243
FunctionCall(SqlPageFunctionCall),
4344
}
4445

@@ -64,6 +65,13 @@ impl std::fmt::Display for StmtParam {
6465
}
6566
write!(f, ")")
6667
}
68+
StmtParam::JsonArray(items) => {
69+
write!(f, "JSON_ARRAY(")?;
70+
for item in items {
71+
write!(f, "{item}, ")?;
72+
}
73+
write!(f, ")")
74+
}
6775
StmtParam::FunctionCall(call) => write!(f, "{call}"),
6876
StmtParam::Error(x) => {
6977
if let Some((i, _)) = x.char_indices().nth(21) {
@@ -154,6 +162,7 @@ pub(super) async fn extract_req_param<'a, 'b>(
154162
StmtParam::Null => None,
155163
StmtParam::Concat(args) => concat_params(&args[..], request, db_connection).await?,
156164
StmtParam::JsonObject(args) => json_object_params(&args[..], request, db_connection).await?,
165+
StmtParam::JsonArray(args) => json_array_params(&args[..], request, db_connection).await?,
157166
StmtParam::FunctionCall(func) => func.evaluate(request, db_connection).await.with_context(|| {
158167
format!(
159168
"Error in function call {func}.\nExpected {:#}",
@@ -200,3 +209,20 @@ async fn json_object_params<'a, 'b>(
200209
map_ser.end()?;
201210
Ok(Some(Cow::Owned(String::from_utf8(result)?)))
202211
}
212+
213+
async fn json_array_params<'a, 'b>(
214+
args: &[StmtParam],
215+
request: &'a RequestInfo,
216+
db_connection: &'b mut DbConn,
217+
) -> anyhow::Result<Option<Cow<'a, str>>> {
218+
use serde::{ser::SerializeSeq, Serializer};
219+
let mut result = Vec::new();
220+
let mut ser = serde_json::Serializer::new(&mut result);
221+
let mut seq_ser = ser.serialize_seq(Some(args.len()))?;
222+
for element in args {
223+
let element = Box::pin(extract_req_param(element, request, db_connection)).await?;
224+
seq_ser.serialize_element(&element)?;
225+
}
226+
seq_ser.end()?;
227+
Ok(Some(Cow::Owned(String::from_utf8(result)?)))
228+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
set res = sqlpage.fetch(json_object(
2+
'method', 'POST',
3+
'url', 'http://localhost:62802/post',
4+
'headers', json_object('x-custom', '1'),
5+
'body', json_array('hello', 'world')
6+
));
7+
set expected = 'POST /post|accept-encoding: br, gzip, deflate, zstd|content-length: 18|content-type: application/json|host: localhost:62802|user-agent: sqlpage|x-custom: 1|["hello", "world"]';
8+
select 'text' as component,
9+
case $res
10+
when $expected then 'It works !'
11+
else 'It failed ! Expected:
12+
' || $expected || '
13+
Got:
14+
' || $res
15+
end as contents;

0 commit comments

Comments
 (0)