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::Datum(value) => return core_value_to_json(&super::datums::read(value)?),
48        Value::Array(array) => serde_json::Value::Array(
49            array
50                .elements()
51                .iter()
52                .map(core_value_to_json)
53                .collect::<crate::error::Result<_>>()?,
54        ),
55        Value::LegacyVector(vector) => serde_json::Value::Array(
56            vector
57                .elements()
58                .iter()
59                .map(core_value_to_json)
60                .collect::<crate::error::Result<_>>()?,
61        ),
62        Value::List(items) => serde_json::Value::Array(
63            items
64                .iter()
65                .map(core_value_to_json)
66                .collect::<crate::error::Result<_>>()?,
67        ),
68        Value::Row(values) => serde_json::Value::Object(
69            values
70                .iter()
71                .enumerate()
72                .map(|(index, value)| Ok((format!("f{}", index + 1), core_value_to_json(value)?)))
73                .collect::<crate::error::Result<_>>()?,
74        ),
75        Value::Record(fields) => serde_json::Value::Object(
76            fields
77                .iter()
78                .map(|(name, value)| Ok((name.clone(), core_value_to_json(value)?)))
79                .collect::<crate::error::Result<_>>()?,
80        ),
81        Value::Map(map) => serde_json::Value::Object(
82            map.iter()
83                .map(|(k, v)| Ok((k.clone(), core_value_to_json(v)?)))
84                .collect::<crate::error::Result<_>>()?,
85        ),
86    })
87}
88
89pub fn value_to_text(value: &Value) -> crate::error::Result<String> {
90    Ok(
91        value_to_text_with_control(value, &uqa_core::memory::ProductionControl::uncontrolled())?
92            .into_uncontrolled()
93            .expect("ordinary carrier text"),
94    )
95}
96
97pub fn value_to_text_with_control(
98    value: &Value,
99    control: &uqa_core::memory::ProductionControl<'_>,
100) -> crate::error::Result<uqa_core::memory::Produced<String>> {
101    control.check()?;
102    Ok(match value {
103        Value::Null | Value::Void => control.copy_text("")?,
104        Value::Enum(value) => return Err(super::catalog_output_required(value)),
105        Value::Datum(value) => {
106            return value_to_text_with_control(
107                &*super::datums::read_with_control(value, control)?,
108                control,
109            )
110        }
111        Value::Bool(value) => control.format(format_args!("{value}"))?,
112        Value::Int(value) => control.format(format_args!("{value}"))?,
113        Value::Float(value) => control.format(format_args!("{value}"))?,
114        Value::Decimal(value) => value.to_sql_string_with_control(control)?,
115        Value::Str(value) | Value::Json(value) | Value::JsonB(value) => control.copy_text(value)?,
116        Value::FixedChar(value) => control.copy_text(value.trim_end_matches(' '))?,
117        Value::Bytes(value) => super::json::utf8_lossy_with_control(value, control)?,
118        Value::Temporal(value) => value.to_sql_string_with_control(control)?,
119        Value::Array(array) => {
120            super::conversion::array_value_to_string_with_control(array, control)?
121        }
122        Value::List(_) | Value::Map(_) => {
123            super::json::format_core_value_as_json_with_control(value, control)?
124        }
125        Value::Row(_) | Value::Record(_) | Value::LegacyVector(_) => {
126            super::conversion::value_to_string_with_control(value, control)?
127        }
128    })
129}