Skip to main content

nodedb_sql/parser/preprocess/
literal.rs

1//! SQL literal canonicalization for `nodedb_types::Value`.
2
3/// Convert a `nodedb_types::Value` to a SQL literal string.
4///
5/// Used by pre-processing and by Origin's pgwire handlers to build SQL
6/// from parsed field maps. Handles all Value variants.
7pub fn value_to_sql_literal(value: &nodedb_types::Value) -> String {
8    match value {
9        nodedb_types::Value::String(s) => format!("'{}'", s.replace('\'', "''")),
10        nodedb_types::Value::Integer(n) => n.to_string(),
11        nodedb_types::Value::Float(f) => {
12            if f.is_finite() {
13                format!("{f}")
14            } else {
15                // NaN / ±inf have no canonical SQL literal form. Emitting
16                // `NaN` or `inf` would be parsed as an identifier
17                // reference by the planner and either bind to an unrelated
18                // column or fail with an opaque error. Canonicalize to
19                // NULL instead; the column value becomes unknown, which
20                // matches the semantic of a non-finite numeric.
21                "NULL".to_string()
22            }
23        }
24        nodedb_types::Value::Bool(b) => if *b { "TRUE" } else { "FALSE" }.to_string(),
25        nodedb_types::Value::Null => "NULL".to_string(),
26        nodedb_types::Value::Array(items) => {
27            let inner: Vec<String> = items.iter().map(value_to_sql_literal).collect();
28            format!("ARRAY[{}]", inner.join(", "))
29        }
30        nodedb_types::Value::Bytes(b) => {
31            let hex: String = b.iter().map(|byte| format!("{byte:02x}")).collect();
32            format!("'\\x{hex}'")
33        }
34        nodedb_types::Value::Object(_) => "NULL".to_string(),
35        nodedb_types::Value::Uuid(u) => format!("'{u}'"),
36        nodedb_types::Value::Ulid(u) => format!("'{u}'"),
37        nodedb_types::Value::DateTime(dt) => format!("'{dt}'"),
38        nodedb_types::Value::Duration(d) => format!("'{d}'"),
39        nodedb_types::Value::Decimal(d) => d.to_string(),
40        other => format!("'{}'", format!("{other:?}").replace('\'', "''")),
41    }
42}