Skip to main content

uqa_sql/expr/
json_carrier.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Convert SQL values into the JSON carrier used by table and aggregate execution.
8
9use uqa_core::Value;
10
11/// Enum carriers need catalog label output and are rejected rather than rendered from their storage key.
12pub fn core_value_to_json(value: &Value) -> crate::error::Result<serde_json::Value> {
13    Ok(match value {
14        Value::Null => serde_json::Value::Null,
15        Value::Void => serde_json::Value::String(String::new()),
16        Value::Bool(b) => serde_json::Value::Bool(*b),
17        Value::Int(i) => serde_json::Value::Number((*i).into()),
18        Value::Float(f) => serde_json::Number::from_f64(*f).map_or_else(
19            || {
20                let label = if f.is_nan() {
21                    "NaN"
22                } else if f.is_sign_positive() {
23                    "Infinity"
24                } else {
25                    "-Infinity"
26                };
27                serde_json::Value::String(label.to_string())
28            },
29            serde_json::Value::Number,
30        ),
31        Value::Decimal(d) => d
32            .to_f64()
33            .and_then(serde_json::Number::from_f64)
34            .map_or_else(
35                || serde_json::Value::String(d.to_sql_string()),
36                serde_json::Value::Number,
37            ),
38        Value::Str(s) => serde_json::from_str::<serde_json::Value>(s)
39            .unwrap_or_else(|_| serde_json::Value::String(s.clone())),
40        Value::FixedChar(s) => serde_json::Value::String(s.trim_end_matches(' ').to_string()),
41        Value::Bytes(bytes) => serde_json::Value::String(String::from_utf8_lossy(bytes).into()),
42        Value::Temporal(t) => serde_json::Value::String(t.to_sql_string()),
43        Value::Json(text) | Value::JsonB(text) => {
44            serde_json::from_str(text).unwrap_or_else(|_| serde_json::Value::String(text.clone()))
45        }
46        Value::Enum(value) => return Err(super::catalog_output_required(value)),
47        Value::Array(array) => serde_json::Value::Array(
48            array
49                .elements()
50                .iter()
51                .map(core_value_to_json)
52                .collect::<crate::error::Result<_>>()?,
53        ),
54        Value::LegacyVector(vector) => serde_json::Value::Array(
55            vector
56                .elements()
57                .iter()
58                .map(core_value_to_json)
59                .collect::<crate::error::Result<_>>()?,
60        ),
61        Value::List(items) => serde_json::Value::Array(
62            items
63                .iter()
64                .map(core_value_to_json)
65                .collect::<crate::error::Result<_>>()?,
66        ),
67        Value::Row(values) => serde_json::Value::Object(
68            values
69                .iter()
70                .enumerate()
71                .map(|(index, value)| Ok((format!("f{}", index + 1), core_value_to_json(value)?)))
72                .collect::<crate::error::Result<_>>()?,
73        ),
74        Value::Record(fields) => serde_json::Value::Object(
75            fields
76                .iter()
77                .map(|(name, value)| Ok((name.clone(), core_value_to_json(value)?)))
78                .collect::<crate::error::Result<_>>()?,
79        ),
80        Value::Map(map) => serde_json::Value::Object(
81            map.iter()
82                .map(|(k, v)| Ok((k.clone(), core_value_to_json(v)?)))
83                .collect::<crate::error::Result<_>>()?,
84        ),
85    })
86}
87
88pub fn value_to_text(value: &Value) -> crate::error::Result<String> {
89    Ok(
90        value_to_text_with_control(value, &uqa_core::memory::ProductionControl::uncontrolled())?
91            .into_uncontrolled()
92            .expect("ordinary carrier text"),
93    )
94}
95
96pub fn value_to_text_with_control(
97    value: &Value,
98    control: &uqa_core::memory::ProductionControl<'_>,
99) -> crate::error::Result<uqa_core::memory::Produced<String>> {
100    control.check()?;
101    Ok(match value {
102        Value::Null | Value::Void => control.copy_text("")?,
103        Value::Enum(value) => return Err(super::catalog_output_required(value)),
104        Value::Bool(value) => control.format(format_args!("{value}"))?,
105        Value::Int(value) => control.format(format_args!("{value}"))?,
106        Value::Float(value) => control.format(format_args!("{value}"))?,
107        Value::Decimal(value) => value.to_sql_string_with_control(control)?,
108        Value::Str(value) | Value::Json(value) | Value::JsonB(value) => control.copy_text(value)?,
109        Value::FixedChar(value) => control.copy_text(value.trim_end_matches(' '))?,
110        Value::Bytes(value) => super::json::utf8_lossy_with_control(value, control)?,
111        Value::Temporal(value) => value.to_sql_string_with_control(control)?,
112        Value::Array(array) => {
113            super::conversion::array_value_to_string_with_control(array, control)?
114        }
115        Value::List(_) | Value::Map(_) => {
116            super::json::format_core_value_as_json_with_control(value, control)?
117        }
118        Value::Row(_) | Value::Record(_) | Value::LegacyVector(_) => {
119            super::conversion::value_to_string_with_control(value, control)?
120        }
121    })
122}