Skip to main content

uqa_sql/expr/
json.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! JSON scalar-function helpers for the expression evaluator.
8
9use uqa_core::{jsonb_equality_key, DecimalValue, TemporalValue, Value};
10
11use crate::error::{Result, SQLError};
12
13use super::{hex_encode, out_of_range, value_to_string};
14
15mod path;
16
17pub(super) use path::{jsonpath_candidate, jsonpath_exists, jsonpath_match};
18
19pub(super) fn parse_json(s: &str) -> Result<serde_json::Value> {
20    serde_json::from_str::<serde_json::Value>(s)
21        .map_err(|_| super::json_strip::invalid_json_input(s))
22}
23
24/// Render a parsed JSON value in `PostgreSQL`'s compact result format. JSONB
25/// objects use `PostgreSQL`'s length-then-bytewise key ordering.
26pub(super) fn format_json(value: &serde_json::Value, jsonb: bool) -> String {
27    if !jsonb {
28        return serde_json::to_string(value).expect("serializing a JSON value cannot fail");
29    }
30    match value {
31        serde_json::Value::Null => "null".to_string(),
32        serde_json::Value::Bool(value) => value.to_string(),
33        serde_json::Value::Number(value) => {
34            let text = value.to_string();
35            DecimalValue::parse(&text).map_or(text, |value| value.to_sql_string())
36        }
37        serde_json::Value::String(value) => serde_json::Value::String(value.clone()).to_string(),
38        serde_json::Value::Array(values) => {
39            let values = values
40                .iter()
41                .map(|value| format_json(value, true))
42                .collect::<Vec<_>>();
43            format!("[{}]", values.join(", "))
44        }
45        serde_json::Value::Object(values) => {
46            let mut values = values.iter().collect::<Vec<_>>();
47            values.sort_by(|(left, _), (right, _)| {
48                left.len()
49                    .cmp(&right.len())
50                    .then_with(|| left.as_bytes().cmp(right.as_bytes()))
51            });
52            let values = values
53                .into_iter()
54                .map(|(key, value)| {
55                    let key = serde_json::Value::String(key.clone()).to_string();
56                    format!("{key}: {}", format_json(value, true))
57                })
58                .collect::<Vec<_>>();
59            format!("{{{}}}", values.join(", "))
60        }
61    }
62}
63
64pub(super) fn format_jsonb_pretty(value: &serde_json::Value) -> String {
65    format_jsonb_pretty_at_depth(value, 0)
66}
67
68fn format_jsonb_pretty_at_depth(value: &serde_json::Value, depth: usize) -> String {
69    let indent = " ".repeat(depth * 4);
70    let child_indent = " ".repeat((depth + 1) * 4);
71    match value {
72        serde_json::Value::Array(values) => {
73            if values.is_empty() {
74                return format!("[\n{indent}]");
75            }
76            let values = values
77                .iter()
78                .map(|value| {
79                    format!(
80                        "{child_indent}{}",
81                        format_jsonb_pretty_at_depth(value, depth + 1)
82                    )
83                })
84                .collect::<Vec<_>>();
85            format!("[\n{}\n{indent}]", values.join(",\n"))
86        }
87        serde_json::Value::Object(values) => {
88            if values.is_empty() {
89                return format!("{{\n{indent}}}");
90            }
91            let mut values = values.iter().collect::<Vec<_>>();
92            values.sort_by(|(left, _), (right, _)| {
93                left.len()
94                    .cmp(&right.len())
95                    .then_with(|| left.as_bytes().cmp(right.as_bytes()))
96            });
97            let values = values
98                .into_iter()
99                .map(|(key, value)| {
100                    let key = serde_json::Value::String(key.clone()).to_string();
101                    format!(
102                        "{child_indent}{key}: {}",
103                        format_jsonb_pretty_at_depth(value, depth + 1)
104                    )
105                })
106                .collect::<Vec<_>>();
107            format!("{{\n{}\n{indent}}}", values.join(",\n"))
108        }
109        _ => format_json(value, true),
110    }
111}
112
113pub(super) fn typed_json_value(value: &serde_json::Value, jsonb: bool) -> Result<Value> {
114    if jsonb {
115        validate_jsonb_numbers(value)?;
116    }
117    let text = format_json(value, jsonb);
118    if jsonb {
119        Ok(Value::JsonB(text))
120    } else {
121        Ok(Value::Json(text))
122    }
123}
124
125fn validate_jsonb_numbers(value: &serde_json::Value) -> Result<()> {
126    match value {
127        serde_json::Value::Number(value) => DecimalValue::parse(&value.to_string())
128            .map(|_| ())
129            .ok_or_else(|| out_of_range("numeric")),
130        serde_json::Value::Array(values) => values.iter().try_for_each(validate_jsonb_numbers),
131        serde_json::Value::Object(values) => values.values().try_for_each(validate_jsonb_numbers),
132        _ => Ok(()),
133    }
134}
135
136/// Render an engine value as `PostgreSQL` JSON input text without losing the
137/// lexical representation of values already typed as `json` or `jsonb`.
138pub fn value_to_json_text(value: &Value) -> String {
139    match value {
140        Value::Null => "null".to_string(),
141        Value::Void => "\"\"".to_string(),
142        Value::Bool(value) => value.to_string(),
143        Value::Int(value) => value.to_string(),
144        Value::Float(value) => match serde_json::Number::from_f64(*value) {
145            Some(number) => number.to_string(),
146            None if value.is_nan() => "\"NaN\"".to_string(),
147            None if value.is_sign_positive() => "\"Infinity\"".to_string(),
148            None => "\"-Infinity\"".to_string(),
149        },
150        Value::Decimal(value) => value.to_sql_string(),
151        Value::Str(value) => serde_json::Value::String(value.clone()).to_string(),
152        Value::FixedChar(value) => {
153            serde_json::Value::String(value.trim_end_matches(' ').to_string()).to_string()
154        }
155        Value::Bytes(value) => {
156            serde_json::Value::String(format!("0x{}", hex_encode(value))).to_string()
157        }
158        Value::Temporal(value) => serde_json::Value::String(value.to_sql_string()).to_string(),
159        Value::Json(text) | Value::JsonB(text) => text.clone(),
160        Value::Array(array) => {
161            let values = array
162                .elements()
163                .iter()
164                .map(value_to_json_text)
165                .collect::<Vec<_>>();
166            format!("[{}]", values.join(","))
167        }
168        Value::List(values) => {
169            let values = values.iter().map(value_to_json_text).collect::<Vec<_>>();
170            format!("[{}]", values.join(","))
171        }
172        Value::Row(values) => record_json_text(
173            values
174                .iter()
175                .enumerate()
176                .map(|(index, value)| (format!("f{}", index + 1), value)),
177        ),
178        Value::Record(fields) => {
179            record_json_text(fields.iter().map(|(name, value)| (name.clone(), value)))
180        }
181        Value::Map(values) => {
182            let values = values
183                .iter()
184                .map(|(key, value)| {
185                    let key = serde_json::Value::String(key.clone()).to_string();
186                    format!("{key}:{}", value_to_json_text(value))
187                })
188                .collect::<Vec<_>>();
189            format!("{{{}}}", values.join(","))
190        }
191    }
192}
193
194fn record_json_text<'a>(fields: impl IntoIterator<Item = (String, &'a Value)>) -> String {
195    let fields = fields
196        .into_iter()
197        .map(|(name, value)| {
198            let name = serde_json::Value::String(name).to_string();
199            format!("{name}:{}", value_to_json_text(value))
200        })
201        .collect::<Vec<_>>();
202    format!("{{{}}}", fields.join(","))
203}
204
205pub(super) fn json_build_array_value(args: &[Value], jsonb: bool) -> Result<Value> {
206    let text = format!(
207        "[{}]",
208        args.iter()
209            .map(value_to_json_text)
210            .collect::<Vec<_>>()
211            .join(", ")
212    );
213    if jsonb {
214        typed_json_value(&parse_json(&text)?, true)
215    } else {
216        Ok(Value::Json(text))
217    }
218}
219
220pub(super) fn json_build_object_value(args: &[Value], jsonb: bool) -> Result<Value> {
221    if !args.len().is_multiple_of(2) {
222        return Err(SQLError::TypeMismatch(
223            "json_build_object requires an even number of args".into(),
224        ));
225    }
226    let mut fields = Vec::with_capacity(args.len() / 2);
227    for pair in args.chunks_exact(2) {
228        if matches!(pair[0], Value::Null) {
229            return Err(SQLError::TypeMismatch(
230                "json_build_object key must not be NULL".into(),
231            ));
232        }
233        let key = serde_json::Value::String(value_to_string(&pair[0])).to_string();
234        fields.push(format!("{key} : {}", value_to_json_text(&pair[1])));
235    }
236    let text = format!("{{{}}}", fields.join(", "));
237    if jsonb {
238        typed_json_value(&parse_json(&text)?, true)
239    } else {
240        Ok(Value::Json(text))
241    }
242}
243
244pub(super) fn value_to_json(v: &Value) -> serde_json::Value {
245    match v {
246        Value::Null => serde_json::Value::Null,
247        Value::Void => serde_json::Value::String(String::new()),
248        Value::Bool(b) => serde_json::Value::Bool(*b),
249        Value::Int(i) => serde_json::Value::Number((*i).into()),
250        Value::Float(f) => serde_json::Number::from_f64(*f).map_or_else(
251            || {
252                let label = if f.is_nan() {
253                    "NaN"
254                } else if f.is_sign_positive() {
255                    "Infinity"
256                } else {
257                    "-Infinity"
258                };
259                serde_json::Value::String(label.to_string())
260            },
261            serde_json::Value::Number,
262        ),
263        Value::Decimal(d) => {
264            if d.is_nan() || d.is_infinite() {
265                serde_json::Value::String(d.to_sql_string())
266            } else {
267                d.to_sql_string()
268                    .parse::<serde_json::Number>()
269                    .map(serde_json::Value::Number)
270                    .unwrap_or_else(|_| serde_json::Value::String(d.to_sql_string()))
271            }
272        }
273        Value::Str(s) => serde_json::Value::String(s.clone()),
274        Value::FixedChar(s) => serde_json::Value::String(s.trim_end_matches(' ').to_string()),
275        Value::Bytes(b) => serde_json::Value::String(format!("0x{}", hex_encode(b))),
276        Value::Temporal(t) => serde_json::Value::String(t.to_sql_string()),
277        Value::Json(text) | Value::JsonB(text) => {
278            serde_json::from_str(text).unwrap_or_else(|_| serde_json::Value::String(text.clone()))
279        }
280        Value::Array(array) => {
281            serde_json::Value::Array(array.elements().iter().map(value_to_json).collect())
282        }
283        Value::List(items) => serde_json::Value::Array(items.iter().map(value_to_json).collect()),
284        Value::Row(values) => serde_json::Value::Object(
285            values
286                .iter()
287                .enumerate()
288                .map(|(index, value)| (format!("f{}", index + 1), value_to_json(value)))
289                .collect(),
290        ),
291        Value::Record(fields) => serde_json::Value::Object(
292            fields
293                .iter()
294                .map(|(name, value)| (name.clone(), value_to_json(value)))
295                .collect(),
296        ),
297        Value::Map(map) => {
298            let mut obj = serde_json::Map::new();
299            for (k, v) in map {
300                obj.insert(k.clone(), value_to_json(v));
301            }
302            serde_json::Value::Object(obj)
303        }
304    }
305}
306
307#[allow(dead_code)]
308pub(super) fn json_to_value(json: &serde_json::Value) -> Value {
309    match json {
310        serde_json::Value::Null => Value::Null,
311        serde_json::Value::Bool(b) => Value::Bool(*b),
312        serde_json::Value::Number(n) => {
313            if let Some(i) = n.as_i64() {
314                Value::Int(i)
315            } else if let Some(d) = DecimalValue::parse(&n.to_string()) {
316                Value::Decimal(d)
317            } else if let Some(f) = n.as_f64() {
318                Value::Float(f)
319            } else {
320                Value::Null
321            }
322        }
323        serde_json::Value::String(s) => Value::Str(s.clone()),
324        serde_json::Value::Array(arr) => Value::List(arr.iter().map(json_to_value).collect()),
325        serde_json::Value::Object(obj) => {
326            if let Ok(temporal) =
327                serde_json::from_value::<TemporalValue>(serde_json::Value::Object(obj.clone()))
328            {
329                return Value::Temporal(temporal);
330            }
331            let mut map = std::collections::BTreeMap::new();
332            for (k, v) in obj {
333                map.insert(k.clone(), json_to_value(v));
334            }
335            Value::Map(map)
336        }
337    }
338}
339
340pub(super) fn json_typeof(v: &serde_json::Value) -> &'static str {
341    match v {
342        serde_json::Value::Null => "null",
343        serde_json::Value::Bool(_) => "boolean",
344        serde_json::Value::Number(_) => "number",
345        serde_json::Value::String(_) => "string",
346        serde_json::Value::Array(_) => "array",
347        serde_json::Value::Object(_) => "object",
348    }
349}
350
351pub(super) fn json_extract_path(args: &[Value], as_text: bool, jsonb: bool) -> Result<Value> {
352    if args.len() < 2 {
353        return Err(SQLError::TypeMismatch(
354            "json_extract_path takes 2+ args".into(),
355        ));
356    }
357    let jsonb = jsonb || matches!(args[0], Value::JsonB(_));
358    let mut current = parse_json(&value_to_string(&args[0]))?;
359    for key in &args[1..] {
360        let key_str = value_to_string(key);
361        current = match current {
362            serde_json::Value::Object(mut obj) => {
363                obj.remove(&key_str).unwrap_or(serde_json::Value::Null)
364            }
365            serde_json::Value::Array(arr) => json_array_index(arr.len(), &key_str)
366                .and_then(|idx| arr.into_iter().nth(idx))
367                .unwrap_or(serde_json::Value::Null),
368            _ => serde_json::Value::Null,
369        };
370    }
371    if as_text {
372        Ok(Value::Str(match current {
373            serde_json::Value::String(s) => s,
374            serde_json::Value::Null => return Ok(Value::Null),
375            other => format_json(&other, jsonb),
376        }))
377    } else if matches!(current, serde_json::Value::Null) {
378        Ok(Value::Null)
379    } else {
380        typed_json_value(&current, jsonb)
381    }
382}
383
384fn json_array_index(len: usize, key: &str) -> Option<usize> {
385    let index = key.parse::<i64>().ok()?;
386    let normalized = if index < 0 { len as i64 + index } else { index };
387    usize::try_from(normalized).ok().filter(|idx| *idx < len)
388}
389
390pub(super) fn json_contains(args: &[Value]) -> Result<Value> {
391    if args.len() != 2 {
392        return Err(SQLError::TypeMismatch("json_contains takes 2 args".into()));
393    }
394    let lhs = parse_json(&value_to_string(&args[0]))?;
395    let rhs = parse_json(&value_to_string(&args[1]))?;
396    Ok(Value::Bool(json_contains_value(&lhs, &rhs)))
397}
398
399pub(super) fn json_contained_by(args: &[Value]) -> Result<Value> {
400    if args.len() != 2 {
401        return Err(SQLError::TypeMismatch(
402            "json_contained_by takes 2 args".into(),
403        ));
404    }
405    let lhs = parse_json(&value_to_string(&args[0]))?;
406    let rhs = parse_json(&value_to_string(&args[1]))?;
407    Ok(Value::Bool(json_contains_value(&rhs, &lhs)))
408}
409
410fn json_contains_value(lhs: &serde_json::Value, rhs: &serde_json::Value) -> bool {
411    json_contains_value_at_depth(lhs, rhs, true)
412}
413
414fn json_contains_value_at_depth(
415    lhs: &serde_json::Value,
416    rhs: &serde_json::Value,
417    top_level: bool,
418) -> bool {
419    match (lhs, rhs) {
420        (serde_json::Value::Object(l), serde_json::Value::Object(r)) => r.iter().all(|(k, rv)| {
421            l.get(k)
422                .is_some_and(|lv| json_contains_value_at_depth(lv, rv, false))
423        }),
424        (serde_json::Value::Array(l), serde_json::Value::Array(r)) => r.iter().all(|rv| {
425            l.iter()
426                .any(|lv| json_contains_value_at_depth(lv, rv, false))
427        }),
428        (serde_json::Value::Array(l), r) if top_level && jsonb_is_primitive(r) => l
429            .iter()
430            .any(|lv| json_contains_value_at_depth(lv, r, false)),
431        _ => jsonb_values_equal(lhs, rhs),
432    }
433}
434
435fn jsonb_is_primitive(value: &serde_json::Value) -> bool {
436    !matches!(
437        value,
438        serde_json::Value::Array(_) | serde_json::Value::Object(_)
439    )
440}
441
442fn jsonb_values_equal(lhs: &serde_json::Value, rhs: &serde_json::Value) -> bool {
443    let lhs = serde_json::to_string(lhs).expect("serializing parsed JSON cannot fail");
444    let rhs = serde_json::to_string(rhs).expect("serializing parsed JSON cannot fail");
445    match (jsonb_equality_key(&lhs), jsonb_equality_key(&rhs)) {
446        (Some(lhs), Some(rhs)) => lhs == rhs,
447        _ => false,
448    }
449}
450
451pub(super) fn json_has_key(args: &[Value]) -> Result<Value> {
452    if args.len() != 2 {
453        return Err(SQLError::TypeMismatch("json_has_key takes 2 args".into()));
454    }
455    let obj = parse_json(&value_to_string(&args[0]))?;
456    let key = value_to_string(&args[1]);
457    Ok(Value::Bool(match obj {
458        serde_json::Value::Object(map) => map.contains_key(&key),
459        serde_json::Value::Array(items) => items
460            .iter()
461            .any(|item| matches!(item, serde_json::Value::String(value) if value == &key)),
462        _ => false,
463    }))
464}
465
466pub(super) fn json_has_keys(args: &[Value], require_all: bool) -> Result<Value> {
467    if args.len() != 2 {
468        return Err(SQLError::TypeMismatch("json_has_keys takes 2 args".into()));
469    }
470    let obj = parse_json(&value_to_string(&args[0]))?;
471    let keys = match &args[1] {
472        Value::Array(array) => array_strings(array.elements()),
473        Value::List(items) => array_strings(items),
474        other => {
475            return Err(SQLError::TypeMismatch(format!(
476                "json key list must be array, got {other:?}"
477            )));
478        }
479    };
480    let found = |key: &String| match &obj {
481        serde_json::Value::Object(map) => map.contains_key(key),
482        serde_json::Value::Array(items) => items
483            .iter()
484            .any(|item| matches!(item, serde_json::Value::String(value) if value == key)),
485        _ => false,
486    };
487    Ok(Value::Bool(if require_all {
488        keys.iter().all(found)
489    } else {
490        keys.iter().any(found)
491    }))
492}
493
494pub(super) fn json_concat(args: &[Value]) -> Result<Option<Value>> {
495    if args.len() != 2 {
496        return Err(SQLError::TypeMismatch("json_concat takes 2 args".into()));
497    }
498    if !args.iter().any(|arg| matches!(arg, Value::JsonB(_))) {
499        return Ok(None);
500    }
501    let lhs = value_to_json(&args[0]);
502    let rhs = value_to_json(&args[1]);
503    let out = match (lhs, rhs) {
504        (serde_json::Value::Object(mut left), serde_json::Value::Object(right)) => {
505            for (key, value) in right {
506                left.insert(key, value);
507            }
508            serde_json::Value::Object(left)
509        }
510        (serde_json::Value::Array(mut left), serde_json::Value::Array(right)) => {
511            left.extend(right);
512            serde_json::Value::Array(left)
513        }
514        (serde_json::Value::Array(mut left), right) => {
515            left.push(right);
516            serde_json::Value::Array(left)
517        }
518        (left, serde_json::Value::Array(mut right)) => {
519            let mut out = vec![left];
520            out.append(&mut right);
521            serde_json::Value::Array(out)
522        }
523        (left, right) => serde_json::Value::Array(vec![left, right]),
524    };
525    typed_json_value(&out, true).map(Some)
526}
527
528pub(super) fn json_delete(args: &[Value]) -> Result<Option<Value>> {
529    if args.len() != 2 {
530        return Err(SQLError::TypeMismatch("json_delete takes 2 args".into()));
531    }
532    if !matches!(args[0], Value::JsonB(_) | Value::Map(_) | Value::List(_)) {
533        return Ok(None);
534    }
535    let mut target = value_to_json(&args[0]);
536    match &args[1] {
537        Value::Int(index) => delete_array_index(&mut target, *index),
538        Value::Array(array) => {
539            for key in array_strings(array.elements()) {
540                delete_key_or_string(&mut target, &key);
541            }
542        }
543        Value::List(keys) => {
544            for key in array_strings(keys) {
545                delete_key_or_string(&mut target, &key);
546            }
547        }
548        key => delete_key_or_string(&mut target, &value_to_string(key)),
549    }
550    typed_json_value(&target, true).map(Some)
551}
552
553pub(super) fn json_delete_path(args: &[Value]) -> Result<Value> {
554    if args.len() != 2 {
555        return Err(SQLError::TypeMismatch(
556            "json_delete_path takes 2 args".into(),
557        ));
558    }
559    let mut target = value_to_json(&args[0]);
560    let path = path_arg(&args[1])?;
561    delete_path(&mut target, &path);
562    typed_json_value(&target, true)
563}
564
565fn path_arg(value: &Value) -> Result<Vec<String>> {
566    match value {
567        Value::Array(array) => Ok(array_strings(array.elements())),
568        Value::List(items) => Ok(array_strings(items)),
569        Value::Str(s) => Ok(s
570            .trim_matches(|c| c == '{' || c == '}')
571            .split(',')
572            .filter(|part| !part.is_empty())
573            .map(|part| part.trim().to_string())
574            .collect()),
575        other => Err(SQLError::TypeMismatch(format!(
576            "JSON path must be an array, got {other:?}"
577        ))),
578    }
579}
580
581fn array_strings(values: &[Value]) -> Vec<String> {
582    fn append(values: &[Value], output: &mut Vec<String>) {
583        for value in values {
584            if let Value::List(nested) = value {
585                append(nested, output);
586            } else {
587                output.push(value_to_string(value));
588            }
589        }
590    }
591
592    let mut output = Vec::new();
593    append(values, &mut output);
594    output
595}
596
597fn delete_key_or_string(target: &mut serde_json::Value, key: &str) {
598    match target {
599        serde_json::Value::Object(map) => {
600            map.remove(key);
601        }
602        serde_json::Value::Array(items) => {
603            items.retain(|item| !matches!(item, serde_json::Value::String(value) if value == key));
604        }
605        _ => {}
606    }
607}
608
609fn delete_array_index(target: &mut serde_json::Value, index: i64) {
610    let serde_json::Value::Array(items) = target else {
611        return;
612    };
613    let normalized = if index < 0 {
614        items.len() as i64 + index
615    } else {
616        index
617    };
618    if let Ok(index) = usize::try_from(normalized) {
619        if index < items.len() {
620            items.remove(index);
621        }
622    }
623}
624
625fn delete_path(target: &mut serde_json::Value, path: &[String]) {
626    let Some((head, rest)) = path.split_first() else {
627        return;
628    };
629    if rest.is_empty() {
630        match target {
631            serde_json::Value::Object(map) => {
632                map.remove(head);
633            }
634            serde_json::Value::Array(items) => {
635                if let Some(index) = json_array_index(items.len(), head) {
636                    items.remove(index);
637                }
638            }
639            _ => {}
640        }
641        return;
642    }
643    match target {
644        serde_json::Value::Object(map) => {
645            if let Some(next) = map.get_mut(head) {
646                delete_path(next, rest);
647            }
648        }
649        serde_json::Value::Array(items) => {
650            if let Some(index) = json_array_index(items.len(), head) {
651                delete_path(&mut items[index], rest);
652            }
653        }
654        _ => {}
655    }
656}
657
658pub(super) fn jsonb_set(args: &[Value]) -> Result<Value> {
659    if !(3..=4).contains(&args.len()) {
660        return Err(SQLError::TypeMismatch("jsonb_set takes 3-4 args".into()));
661    }
662    let mut current = parse_json(&value_to_string(&args[0]))?;
663    let path = path_arg(&args[1])?;
664    let new_val = parse_json(&value_to_string(&args[2]))
665        .unwrap_or_else(|_| serde_json::Value::String(value_to_string(&args[2])));
666    let create_missing = args.get(3).is_none_or(|value| match value {
667        Value::Bool(value) => *value,
668        Value::Null => false,
669        other => value_to_string(other).eq_ignore_ascii_case("true"),
670    });
671    json_set_path(&mut current, &path, new_val, create_missing);
672    typed_json_value(&current, true)
673}
674
675pub(super) fn jsonb_insert(args: &[Value]) -> Result<Value> {
676    if !(3..=4).contains(&args.len()) {
677        return Err(SQLError::TypeMismatch("jsonb_insert takes 3-4 args".into()));
678    }
679    let mut current = parse_json(&value_to_string(&args[0]))?;
680    let path = path_arg(&args[1])?;
681    let new_val = parse_json(&value_to_string(&args[2]))
682        .unwrap_or_else(|_| serde_json::Value::String(value_to_string(&args[2])));
683    let insert_after = args.get(3).is_some_and(|value| match value {
684        Value::Bool(value) => *value,
685        other => value_to_string(other).eq_ignore_ascii_case("true"),
686    });
687    json_insert_path(&mut current, &path, new_val, insert_after);
688    typed_json_value(&current, true)
689}
690
691fn json_insert_path(
692    current: &mut serde_json::Value,
693    path: &[String],
694    new_val: serde_json::Value,
695    insert_after: bool,
696) -> bool {
697    let Some((head, rest)) = path.split_first() else {
698        return false;
699    };
700    if rest.is_empty() {
701        return match current {
702            serde_json::Value::Object(map) => {
703                if map.contains_key(head) {
704                    false
705                } else {
706                    map.insert(head.clone(), new_val);
707                    true
708                }
709            }
710            serde_json::Value::Array(items) => {
711                let Some(index) = json_insert_index(items.len(), head, insert_after) else {
712                    return false;
713                };
714                items.insert(index, new_val);
715                true
716            }
717            _ => false,
718        };
719    }
720    match current {
721        serde_json::Value::Object(map) => map
722            .get_mut(head)
723            .is_some_and(|next| json_insert_path(next, rest, new_val, insert_after)),
724        serde_json::Value::Array(items) => json_array_index(items.len(), head)
725            .is_some_and(|index| json_insert_path(&mut items[index], rest, new_val, insert_after)),
726        _ => false,
727    }
728}
729
730fn json_insert_index(len: usize, key: &str, insert_after: bool) -> Option<usize> {
731    let raw = key.parse::<i64>().ok()?;
732    let len_i64 = len as i64;
733    let index = if raw >= 0 {
734        if raw >= len_i64 {
735            len_i64
736        } else if insert_after {
737            raw + 1
738        } else {
739            raw
740        }
741    } else {
742        let normalized = len_i64 + raw;
743        if normalized < 0 {
744            0
745        } else if insert_after {
746            normalized + 1
747        } else {
748            normalized
749        }
750    };
751    usize::try_from(index.clamp(0, len_i64)).ok()
752}
753
754fn json_set_path(
755    current: &mut serde_json::Value,
756    path: &[String],
757    new_val: serde_json::Value,
758    create_missing: bool,
759) -> bool {
760    if path.is_empty() {
761        *current = new_val;
762        return true;
763    }
764    let head = &path[0];
765    let rest = &path[1..];
766    match current {
767        serde_json::Value::Object(obj) => {
768            if !obj.contains_key(head) && !create_missing {
769                return false;
770            }
771            let entry = obj.entry(head.clone()).or_insert(serde_json::Value::Null);
772            json_set_path(entry, rest, new_val, create_missing)
773        }
774        serde_json::Value::Array(arr) => {
775            if let Some(idx) = json_array_index(arr.len(), head) {
776                json_set_path(&mut arr[idx], rest, new_val, create_missing)
777            } else if create_missing && rest.is_empty() {
778                if let Ok(idx) = head.parse::<usize>() {
779                    while arr.len() <= idx {
780                        arr.push(serde_json::Value::Null);
781                    }
782                    arr[idx] = new_val;
783                    true
784                } else {
785                    false
786                }
787            } else {
788                false
789            }
790        }
791        _ if create_missing => {
792            let mut new_obj = serde_json::Map::new();
793            new_obj.insert(head.clone(), serde_json::Value::Null);
794            let mut wrapper = serde_json::Value::Object(new_obj);
795            let changed = json_set_path(&mut wrapper, path, new_val, create_missing);
796            if changed {
797                *current = wrapper;
798            }
799            changed
800        }
801        _ => false,
802    }
803}
804pub(super) fn strip_nulls(value: &mut serde_json::Value, strip_in_arrays: bool) {
805    match value {
806        serde_json::Value::Object(obj) => {
807            obj.retain(|_, v| !v.is_null());
808            for v in obj.values_mut() {
809                strip_nulls(v, strip_in_arrays);
810            }
811        }
812        serde_json::Value::Array(arr) => {
813            if strip_in_arrays {
814                arr.retain(|value| !value.is_null());
815            }
816            for v in arr.iter_mut() {
817                strip_nulls(v, strip_in_arrays);
818            }
819        }
820        _ => {}
821    }
822}
823
824#[cfg(test)]
825mod pretty_tests {
826    use super::{format_jsonb_pretty, parse_json, typed_json_value, DecimalValue};
827
828    #[test]
829    fn jsonb_pretty_uses_postgresql_layout_and_key_order() {
830        let value = parse_json(r#"{"zz":1,"b":[],"aa":{"long":3,"x":2}}"#).unwrap();
831        assert_eq!(
832            format_jsonb_pretty(&value),
833            "{\n    \"b\": [\n    ],\n    \"aa\": {\n        \"x\": 2,\n        \"long\": 3\n    },\n    \"zz\": 1\n}"
834        );
835        assert_eq!(format_jsonb_pretty(&parse_json("[]").unwrap()), "[\n]");
836        assert_eq!(format_jsonb_pretty(&parse_json("{}").unwrap()), "{\n}");
837        assert_eq!(
838            format_jsonb_pretty(&parse_json("1e-1000").unwrap()),
839            DecimalValue::parse("1e-1000").unwrap().to_sql_string()
840        );
841        assert_eq!(format_jsonb_pretty(&parse_json("1.00").unwrap()), "1.00");
842        assert_eq!(format_jsonb_pretty(&parse_json("-0").unwrap()), "0");
843    }
844
845    #[test]
846    fn jsonb_rejects_numbers_outside_postgresql_numeric_range() {
847        let maximum = parse_json("1e131071").unwrap();
848        assert!(typed_json_value(&maximum, true).is_ok());
849
850        for text in ["1e131072", "1e-16384", "[1e131072]", r#"{"n":1e131072}"#] {
851            let error = typed_json_value(&parse_json(text).unwrap(), true).unwrap_err();
852            assert_eq!(error.sqlstate(), Some("22003"));
853        }
854
855        assert!(typed_json_value(&parse_json("1e200000").unwrap(), false).is_ok());
856        assert!(typed_json_value(&parse_json("0e200000").unwrap(), true).is_ok());
857    }
858}