1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//! SQL column references for JSON document rows (`id` + `body`).
use crate::safe_ident::assert_safe_ident;
/// Map a Valence field name to a SQL expression against document storage.
///
/// Callers that accept untrusted field names must use [`sql_doc_column_checked`].
pub fn sql_doc_column(field: &str) -> String {
sql_doc_column_checked(field)
.unwrap_or_else(|_| "json_extract(body, '$.__valence_rejected_ident')".to_string())
}
/// Map a field name to a SQL document column expression after charset validation.
///
/// # Errors
///
/// Returns [`crate::Error::Validation`] when `field` is not a safe identifier.
pub fn sql_doc_column_checked(field: &str) -> crate::error::Result<String> {
if field == "id" {
return Ok("id".to_string());
}
assert_safe_ident(field)?;
Ok(format!("json_extract(body, '$.{field}')"))
}
/// Rewrite `SELECT *` / field lists for document tables.
pub fn sql_select_clause(projection: Option<&Vec<String>>) -> String {
match projection {
None => "id, body".to_string(),
Some(fields) if fields.len() == 1 && fields[0].trim() == "*" => "id, body".to_string(),
Some(fields) => fields
.iter()
.map(|f| {
let trimmed = f.trim();
if trimmed.starts_with("VALUE ") {
let inner = trimmed.trim_start_matches("VALUE ").trim();
if inner == "id" {
"id".to_string()
} else {
sql_doc_column(inner)
}
} else if trimmed == "id" {
"id".to_string()
} else if trimmed == "*" {
"id, body".to_string()
} else {
sql_doc_column(trimmed)
}
})
.collect::<Vec<_>>()
.join(", "),
}
}