Skip to main content

nodedb_types/value/
sql_literal.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Convert `Value` to SQL literal string for substitution into SQL text.
4
5use super::core::Value;
6
7impl Value {
8    /// Convert to a SQL literal string for substitution into SQL text.
9    pub fn to_sql_literal(&self) -> String {
10        match self {
11            Value::Null => "NULL".into(),
12            Value::Bool(b) => if *b { "TRUE" } else { "FALSE" }.into(),
13            Value::Integer(i) => i.to_string(),
14            Value::Float(f) => f.to_string(),
15            Value::String(s) => format!("'{}'", s.replace('\'', "''")),
16            Value::Uuid(s) | Value::Ulid(s) | Value::Regex(s) => {
17                format!("'{}'", s.replace('\'', "''"))
18            }
19            Value::Bytes(b) => {
20                let hex: String = b.iter().map(|byte| format!("{byte:02x}")).collect();
21                format!("'\\x{hex}'")
22            }
23            Value::Array(arr) | Value::Set(arr) => {
24                let elements: Vec<String> = arr.iter().map(|v| v.to_sql_literal()).collect();
25                format!("ARRAY[{}]", elements.join(", "))
26            }
27            Value::Object(map) => {
28                let json_str = sonic_rs::to_string(&serde_json::Value::Object(
29                    map.iter()
30                        .map(|(k, v)| (k.clone(), value_to_json(v)))
31                        .collect(),
32                ))
33                .unwrap_or_default();
34                format!("'{}'", json_str.replace('\'', "''"))
35            }
36            Value::DateTime(dt) | Value::NaiveDateTime(dt) => format!("'{dt}'"),
37            Value::Duration(d) => format!("'{d}'"),
38            Value::Decimal(d) => d.to_string(),
39            Value::Geometry(g) => format!("'{}'", sonic_rs::to_string(g).unwrap_or_default()),
40            Value::Range { .. } | Value::Record { .. } => "NULL".into(),
41            Value::Vector(v) => {
42                let elements: Vec<String> = v.iter().map(|f| f.to_string()).collect();
43                format!("ARRAY[{}]", elements.join(", "))
44            }
45            Value::ArrayCell(cell) => {
46                let coords: Vec<String> = cell.coords.iter().map(|v| v.to_sql_literal()).collect();
47                let attrs: Vec<String> = cell.attrs.iter().map(|v| v.to_sql_literal()).collect();
48                format!(
49                    "ARRAY_CELL(coords=[{}], attrs=[{}])",
50                    coords.join(", "),
51                    attrs.join(", ")
52                )
53            }
54        }
55    }
56}
57
58/// Convert nodedb_types::Value back to serde_json::Value (for object serialization).
59fn value_to_json(v: &Value) -> serde_json::Value {
60    match v {
61        Value::Null => serde_json::Value::Null,
62        Value::Bool(b) => serde_json::Value::Bool(*b),
63        Value::Integer(i) => serde_json::json!(*i),
64        Value::Float(f) => serde_json::json!(*f),
65        Value::String(s) => serde_json::Value::String(s.clone()),
66        Value::Array(arr) | Value::Set(arr) => {
67            serde_json::Value::Array(arr.iter().map(value_to_json).collect())
68        }
69        Value::Object(map) => serde_json::Value::Object(
70            map.iter()
71                .map(|(k, v)| (k.clone(), value_to_json(v)))
72                .collect(),
73        ),
74        other => serde_json::Value::String(other.to_sql_literal()),
75    }
76}