Skip to main content

rupg_func/
jsonbuild.rs

1//! The functions of `json.c` and `jsonb.c` that make a `json` or a `jsonb` value from SQL values: `to_json`, `array_to_json`, `row_to_json`, `json_build_object`, `json_build_array` and `json_object`, and the same functions of `jsonb`.
2//!
3//! A value goes to the text of `json` as `datum_to_json_internal` writes it, by the category that `json_categorize_type` gives its type. A function of `jsonb` gives that text to `jsonb_in`, which puts the keys of each object in their order and keeps only the last field of each key, as `pushJsonbValue` does. The text of `json` and the value of `jsonb` agree in the other parts: a number that is not finite is a string, and a key is always a string.
4
5use rupg_analyze::JsonItemType;
6use rupg_common::{Error, Result, SqlState};
7use rupg_pgcatalog::builtin;
8use rupg_types::{
9    DateFormat, DateOrder, DateStyle, JsonItem, JsonTree, Record, Value, date_out, json_escape,
10    json_in, jsonb_in, oid, timestamp_out, timestamptz_out,
11};
12
13use crate::deparse::row_names;
14use crate::io::{array_of, base_type, is_row_type, output};
15use crate::json::invalid;
16use crate::text::variadic_values;
17use crate::{Call, Kernel, Session, bad_value, type_error};
18
19/// The kernel of a function that makes a `json` or a `jsonb` value, by its `prosrc`.
20pub(crate) fn by_src(src: &str) -> Option<Kernel> {
21    Some(match src {
22        "to_json" => to_json,
23        "to_jsonb" => to_jsonb,
24        "array_to_json" => array_to_json,
25        "array_to_json_pretty" => array_to_json_pretty,
26        "row_to_json" => row_to_json,
27        "row_to_json_pretty" => row_to_json_pretty,
28        "json_build_object" => json_build_object,
29        "jsonb_build_object" => jsonb_build_object,
30        "json_build_array" => json_build_array,
31        "jsonb_build_array" => jsonb_build_array,
32        "json_build_object_noargs" => |_, _| Ok(Value::text("{}")),
33        "jsonb_build_object_noargs" => |_, _| Ok(Value::text("{}")),
34        "json_build_array_noargs" => |_, _| Ok(Value::text("[]")),
35        "jsonb_build_array_noargs" => |_, _| Ok(Value::text("[]")),
36        "json_object" => json_object,
37        "jsonb_object" => jsonb_object,
38        "json_object_two_arg" => json_object_two_arg,
39        "jsonb_object_two_arg" => jsonb_object_two_arg,
40        _ => return None,
41    })
42}
43
44/// The `jsonb` value of the text of a `json` value.
45fn jsonb(text: &str) -> Result<Value> {
46    jsonb_in(text).map(Value::Text).map_err(type_error)
47}
48
49/// True when the category of the type is an array or a composite, after which `json_agg` puts a new line between the values.
50pub fn json_structured(ty: u32) -> bool {
51    let ty = base_type(ty);
52    array_of(ty).is_some() || is_row_type(ty) || ty == oid::RECORD_ARRAY || ty == oid::ANYARRAY
53}
54
55/// The text of a value, as `datum_to_json_internal` writes it. A key is always a string, and a key of an array, a composite or a `json` type is an error.
56pub fn json_value(
57    ty: u32,
58    value: &Value,
59    key: bool,
60    session: &dyn Session,
61    out: &mut String,
62) -> Result<()> {
63    if value.is_null() {
64        out.push_str("null");
65        return Ok(());
66    }
67    let ty = base_type(ty);
68    let composite = matches!(value, Value::Record(_));
69    let array = array_of(ty).is_some() || matches!(value, Value::Array(_));
70    if key && (composite || array || ty == oid::JSON || ty == oid::JSONB) {
71        return Err(invalid("key value must be scalar, not array, composite, or json"));
72    }
73    match (ty, value) {
74        (_, Value::Record(record)) => put_record(ty, record, false, session, out),
75        (_, Value::Array(_)) => put_array(ty, value, false, session, out),
76        (oid::BOOL, Value::Bool(b)) => {
77            out.push_str(match (key, b) {
78                (false, true) => "true",
79                (false, false) => "false",
80                (true, true) => "\"true\"",
81                (true, false) => "\"false\"",
82            });
83            Ok(())
84        }
85        (oid::INT2 | oid::INT4 | oid::INT8 | oid::FLOAT4 | oid::FLOAT8 | oid::NUMERIC, _) => {
86            let text = output_text(ty, value, session)?;
87            let number = matches!(text.as_bytes(), [b'0'..=b'9', ..] | [b'-', b'0'..=b'9', ..]);
88            if number && !key {
89                out.push_str(&text);
90            } else {
91                out.push('"');
92                out.push_str(&text);
93                out.push('"');
94            }
95            Ok(())
96        }
97        (oid::DATE | oid::TIMESTAMP | oid::TIMESTAMPTZ, _) => {
98            let mut buf = Vec::new();
99            let format = DateFormat { style: DateStyle::Xsd, order: DateOrder::Mdy };
100            match value {
101                Value::Date(v) => date_out(*v, format, &mut buf),
102                Value::Timestamp(v) => timestamp_out(*v, format, &mut buf).map_err(type_error)?,
103                Value::TimestampTz(v) => {
104                    timestamptz_out(*v, format, &*session.zone()?, &mut buf).map_err(type_error)?;
105                }
106                _ => return Err(bad_value()),
107            }
108            out.push('"');
109            out.push_str(&String::from_utf8_lossy(&buf));
110            out.push('"');
111            Ok(())
112        }
113        (oid::JSON | oid::JSONB, _) => {
114            out.push_str(value.as_str().ok_or_else(bad_value)?);
115            Ok(())
116        }
117        _ => {
118            json_escape(&output_text(ty, value, session)?, out);
119            Ok(())
120        }
121    }
122}
123
124/// The text output of a value.
125fn output_text(ty: u32, value: &Value, session: &dyn Session) -> Result<String> {
126    if let Some(s) = value.as_str() {
127        return Ok(s.to_owned());
128    }
129    let mut buf = Vec::new();
130    output(ty, value, session, &mut buf)?;
131    String::from_utf8(buf).map_err(|_| bad_value())
132}
133
134/// `composite_to_json`: an object with a field for each field of the record. The names come from the record, then from its type, then they are `f1`, `f2` and so on.
135fn put_record(
136    ty: u32,
137    record: &Record,
138    lines: bool,
139    session: &dyn Session,
140    out: &mut String,
141) -> Result<()> {
142    let from_type = if record.names.is_none() { row_names(ty, session) } else { None };
143    let names: Option<&[String]> = record.names.as_deref().or(from_type.as_deref());
144    out.push('{');
145    for (i, (&field_ty, value)) in record.types.iter().zip(&record.values).enumerate() {
146        if i > 0 {
147            out.push_str(if lines { ",\n " } else { "," });
148        }
149        match names.and_then(|n| n.get(i)) {
150            Some(name) => json_escape(name, out),
151            None => json_escape(&format!("f{}", i + 1), out),
152        }
153        out.push(':');
154        json_value(field_ty, value, false, session, out)?;
155    }
156    out.push('}');
157    Ok(())
158}
159
160/// `array_to_json_internal`: nested arrays for the dimensions. Only the elements of the first dimension are on new lines when `lines` is true.
161fn put_array(
162    ty: u32,
163    value: &Value,
164    lines: bool,
165    session: &dyn Session,
166    out: &mut String,
167) -> Result<()> {
168    let Value::Array(array) = value else { return Err(bad_value()) };
169    if array.values.is_empty() {
170        out.push_str("[]");
171        return Ok(());
172    }
173    let element = array_of(base_type(ty))
174        .map(|(e, _)| e)
175        .or_else(|| builtin::type_by_oid(base_type(ty)).map(|row| row.elem))
176        .unwrap_or(oid::UNKNOWN);
177    let lens: Vec<usize> = array.dims.iter().map(|d| usize::try_from(d.len).unwrap_or(0)).collect();
178    let mut next = 0;
179    put_dim(element, &array.values, &lens, lines, &mut next, session, out)
180}
181
182/// `array_dim_to_json`: the elements of one dimension.
183fn put_dim(
184    element: u32,
185    values: &[Option<Value>],
186    lens: &[usize],
187    lines: bool,
188    next: &mut usize,
189    session: &dyn Session,
190    out: &mut String,
191) -> Result<()> {
192    let Some((&len, inner)) = lens.split_first() else { return Ok(()) };
193    out.push('[');
194    for i in 0..len {
195        if i > 0 {
196            out.push_str(if lines { ",\n " } else { "," });
197        }
198        if inner.is_empty() {
199            let value = values.get(*next).ok_or_else(bad_value)?;
200            json_value(element, value.as_ref().unwrap_or(&Value::Null), false, session, out)?;
201            *next += 1;
202        } else {
203            put_dim(element, values, inner, false, next, session, out)?;
204        }
205    }
206    out.push(']');
207    Ok(())
208}
209
210/// The text of the first argument.
211fn value_text(call: &Call<'_>, args: &[Value]) -> Result<String> {
212    let ty = call.args.first().copied().ok_or_else(bad_value)?;
213    let mut out = String::new();
214    json_value(ty, args.first().ok_or_else(bad_value)?, false, call.session, &mut out)?;
215    Ok(out)
216}
217
218fn to_json(call: &Call<'_>, args: &[Value]) -> Result<Value> {
219    Ok(Value::Text(value_text(call, args)?))
220}
221
222fn to_jsonb(call: &Call<'_>, args: &[Value]) -> Result<Value> {
223    jsonb(&value_text(call, args)?)
224}
225
226/// `array_to_json` with the pretty flag.
227fn array_text(call: &Call<'_>, args: &[Value], lines: bool) -> Result<Value> {
228    let ty = call.args.first().copied().ok_or_else(bad_value)?;
229    let mut out = String::new();
230    put_array(ty, args.first().ok_or_else(bad_value)?, lines, call.session, &mut out)?;
231    Ok(Value::Text(out))
232}
233
234fn array_to_json(call: &Call<'_>, args: &[Value]) -> Result<Value> {
235    array_text(call, args, false)
236}
237
238fn array_to_json_pretty(call: &Call<'_>, args: &[Value]) -> Result<Value> {
239    array_text(call, args, matches!(args.get(1), Some(Value::Bool(true))))
240}
241
242/// `row_to_json` with the pretty flag.
243fn row_text(call: &Call<'_>, args: &[Value], lines: bool) -> Result<Value> {
244    let ty = call.args.first().copied().ok_or_else(bad_value)?;
245    let Some(Value::Record(record)) = args.first() else { return Err(bad_value()) };
246    let mut out = String::new();
247    put_record(base_type(ty), record, lines, call.session, &mut out)?;
248    Ok(Value::Text(out))
249}
250
251fn row_to_json(call: &Call<'_>, args: &[Value]) -> Result<Value> {
252    row_text(call, args, false)
253}
254
255fn row_to_json_pretty(call: &Call<'_>, args: &[Value]) -> Result<Value> {
256    row_text(call, args, matches!(args.get(1), Some(Value::Bool(true))))
257}
258
259/// `json_build_object_worker` and `jsonb_build_object_worker`: the text of the object, or `None` for a null `VARIADIC` array.
260fn build_object(call: &Call<'_>, args: &[Value], is_jsonb: bool) -> Result<Option<String>> {
261    let Some(values) = variadic_values(call, args, 0)? else { return Ok(None) };
262    if values.len() % 2 != 0 {
263        let name = if is_jsonb { "jsonb_build_object()" } else { "json_build_object()" };
264        return Err(invalid("argument list must have even number of elements").with_hint(format!(
265            "The arguments of {name} must consist of alternating keys and values."
266        )));
267    }
268    let mut out = String::from("{");
269    for (i, pair) in values.chunks(2).enumerate() {
270        let [(key_ty, key), (ty, value)] = pair else { return Err(bad_value()) };
271        if i > 0 {
272            out.push_str(", ");
273        }
274        if key.is_null() {
275            return Err(if is_jsonb {
276                invalid(&format!("argument {}: key must not be null", 2 * i + 1))
277            } else {
278                Error::new(
279                    SqlState::NULL_VALUE_NOT_ALLOWED,
280                    "null value not allowed for object key",
281                )
282            });
283        }
284        json_value(*key_ty, key, true, call.session, &mut out)?;
285        out.push_str(" : ");
286        json_value(*ty, value, false, call.session, &mut out)?;
287    }
288    out.push('}');
289    Ok(Some(out))
290}
291
292fn json_build_object(call: &Call<'_>, args: &[Value]) -> Result<Value> {
293    Ok(build_object(call, args, false)?.map_or(Value::Null, Value::Text))
294}
295
296fn jsonb_build_object(call: &Call<'_>, args: &[Value]) -> Result<Value> {
297    build_object(call, args, true)?.map_or(Ok(Value::Null), |text| jsonb(&text))
298}
299
300/// `json_build_array_worker`: the text of the array, or `None` for a null `VARIADIC` array.
301fn build_array(call: &Call<'_>, args: &[Value]) -> Result<Option<String>> {
302    let Some(values) = variadic_values(call, args, 0)? else { return Ok(None) };
303    let mut out = String::from("[");
304    for (i, (ty, value)) in values.iter().enumerate() {
305        if i > 0 {
306            out.push_str(", ");
307        }
308        json_value(*ty, value, false, call.session, &mut out)?;
309    }
310    out.push(']');
311    Ok(Some(out))
312}
313
314fn json_build_array(call: &Call<'_>, args: &[Value]) -> Result<Value> {
315    Ok(build_array(call, args)?.map_or(Value::Null, Value::Text))
316}
317
318fn jsonb_build_array(call: &Call<'_>, args: &[Value]) -> Result<Value> {
319    build_array(call, args)?.map_or(Ok(Value::Null), |text| jsonb(&text))
320}
321
322fn subscript_error(message: &str) -> Error {
323    Error::new(SqlState::ARRAY_SUBSCRIPT_ERROR, message)
324}
325
326/// The lengths of the dimensions of an array and its elements.
327type TextArray<'a> = (Vec<i32>, Vec<Option<&'a str>>);
328
329/// The lengths of the dimensions of a `text[]` argument and its elements.
330fn text_array(args: &[Value], at: usize) -> Result<TextArray<'_>> {
331    let Some(Value::Array(array)) = args.get(at) else { return Err(bad_value()) };
332    let values = array
333        .values
334        .iter()
335        .map(|v| v.as_ref().map(|v| v.as_str().ok_or_else(bad_value)).transpose())
336        .collect::<Result<_>>()?;
337    Ok((array.dims.iter().map(|d| d.len).collect(), values))
338}
339
340/// The text of an object with a string or a null for each key.
341fn object_text<'a>(
342    pairs: impl Iterator<Item = (Option<&'a str>, Option<&'a str>)>,
343) -> Result<String> {
344    let mut out = String::from("{");
345    for (i, (key, value)) in pairs.enumerate() {
346        let Some(key) = key else {
347            return Err(Error::new(
348                SqlState::NULL_VALUE_NOT_ALLOWED,
349                "null value not allowed for object key",
350            ));
351        };
352        if i > 0 {
353            out.push_str(", ");
354        }
355        json_escape(key, &mut out);
356        out.push_str(" : ");
357        match value {
358            Some(value) => json_escape(value, &mut out),
359            None => out.push_str("null"),
360        }
361    }
362    out.push('}');
363    Ok(out)
364}
365
366/// `json_object(text[])`: the keys and the values in turn, in one dimension or in two dimensions of two columns.
367fn object_one_arg(args: &[Value]) -> Result<String> {
368    let (lens, values) = text_array(args, 0)?;
369    match lens.len() {
370        0 => return Ok("{}".to_owned()),
371        1 if lens[0] % 2 != 0 => {
372            return Err(subscript_error("array must have even number of elements"));
373        }
374        1 => {}
375        2 if lens[1] != 2 => return Err(subscript_error("array must have two columns")),
376        2 => {}
377        _ => return Err(subscript_error("wrong number of array subscripts")),
378    }
379    object_text(values.chunks(2).map(|pair| (pair[0], pair.get(1).copied().flatten())))
380}
381
382/// `json_object(text[], text[])`: the keys in one array and the values in the other.
383fn object_two_arg(args: &[Value]) -> Result<String> {
384    let (key_dims, keys) = text_array(args, 0)?;
385    let (value_dims, values) = text_array(args, 1)?;
386    let (key_dims, value_dims) = (key_dims.len(), value_dims.len());
387    if key_dims > 1 || key_dims != value_dims {
388        return Err(subscript_error("wrong number of array subscripts"));
389    }
390    if key_dims == 0 {
391        return Ok("{}".to_owned());
392    }
393    if keys.len() != values.len() {
394        return Err(subscript_error("mismatched array dimensions"));
395    }
396    object_text(keys.into_iter().zip(values))
397}
398
399fn json_object(_: &Call<'_>, args: &[Value]) -> Result<Value> {
400    Ok(Value::Text(object_one_arg(args)?))
401}
402
403fn jsonb_object(_: &Call<'_>, args: &[Value]) -> Result<Value> {
404    jsonb(&object_one_arg(args)?)
405}
406
407fn json_object_two_arg(_: &Call<'_>, args: &[Value]) -> Result<Value> {
408    Ok(Value::Text(object_two_arg(args)?))
409}
410
411fn jsonb_object_two_arg(_: &Call<'_>, args: &[Value]) -> Result<Value> {
412    jsonb(&object_two_arg(args)?)
413}
414
415/// The JSON whitespace that the lexer skips.
416const JSON_SPACE: [char; 4] = [' ', '\t', '\n', '\r'];
417
418/// The error of a key that is not unique. A `jsonb` value does not show the key.
419fn duplicate_key(key: Option<&str>) -> Error {
420    let message = match key {
421        Some(key) => format!("duplicate JSON object key value: {key}"),
422        None => "duplicate JSON object key value".to_owned(),
423    };
424    Error::new(SqlState::DUPLICATE_JSON_OBJECT_KEY_VALUE, message)
425}
426
427/// `json_build_object_worker` and `jsonb_build_object_worker` for `JSON_OBJECT`: an object of the keys and the values, one after the other.
428///
429/// A `json` value keeps each field in its order. A `jsonb` value keeps the last field of each key, and then `ABSENT ON NULL` removes each field with a JSON null value, as `uniqueifyJsonbObject` does. A key of a field that `ABSENT ON NULL` skips still counts for `WITH UNIQUE KEYS`.
430///
431/// # Errors
432///
433/// The error of a null key, of a key that is not a scalar, or of a key that is not unique.
434pub fn json_ctor_object(
435    args: &[(u32, Value)],
436    jsonb: bool,
437    absent_on_null: bool,
438    unique: bool,
439    session: &dyn Session,
440) -> Result<Value> {
441    let mut fields: Vec<(String, String)> = Vec::new();
442    let mut keys = std::collections::HashSet::new();
443    for (i, pair) in args.chunks(2).enumerate() {
444        let [(key_ty, key), (ty, value)] = pair else { return Err(bad_value()) };
445        let skip = absent_on_null && value.is_null();
446        if jsonb && key.is_null() {
447            return Err(invalid(&format!("argument {}: key must not be null", 2 * i + 1)));
448        }
449        if skip && !unique {
450            continue;
451        }
452        if key.is_null() {
453            return Err(Error::new(
454                SqlState::NULL_VALUE_NOT_ALLOWED,
455                "null value not allowed for object key",
456            ));
457        }
458        let mut name = String::new();
459        json_value(*key_ty, key, true, session, &mut name)?;
460        if unique && !keys.insert(name.clone()) {
461            return Err(duplicate_key((!jsonb).then_some(&*name)));
462        }
463        if skip && !jsonb {
464            continue;
465        }
466        let mut text = String::new();
467        json_value(*ty, value, false, session, &mut text)?;
468        fields.push((name, text));
469    }
470    if jsonb {
471        // The last field of each key, then the fields with a value that is not a JSON null.
472        let mut last: Vec<(String, String)> = Vec::new();
473        for (name, text) in fields.into_iter().rev() {
474            if !last.iter().any(|(n, _)| *n == name) {
475                last.push((name, text));
476            }
477        }
478        last.reverse();
479        if absent_on_null {
480            last.retain(|(_, text)| text.trim_matches(JSON_SPACE) != "null");
481        }
482        fields = last;
483    }
484    let mut out = String::from("{");
485    for (i, (name, text)) in fields.iter().enumerate() {
486        if i > 0 {
487            out.push_str(", ");
488        }
489        out.push_str(name);
490        out.push_str(" : ");
491        out.push_str(text);
492    }
493    out.push('}');
494    if jsonb { self::jsonb(&out) } else { Ok(Value::Text(out)) }
495}
496
497/// `json_build_array_worker` and `jsonb_build_array_worker` for `JSON_ARRAY`: an array of the values. `ABSENT ON NULL` skips the null values.
498///
499/// # Errors
500///
501/// The error of the output of a value.
502pub fn json_ctor_array(
503    args: &[(u32, Value)],
504    jsonb: bool,
505    absent_on_null: bool,
506    session: &dyn Session,
507) -> Result<Value> {
508    let mut out = String::from("[");
509    let mut first = true;
510    for (ty, value) in args {
511        if absent_on_null && value.is_null() {
512            continue;
513        }
514        if !first {
515            out.push_str(", ");
516        }
517        first = false;
518        json_value(*ty, value, false, session, &mut out)?;
519    }
520    out.push(']');
521    if jsonb { self::jsonb(&out) } else { Ok(Value::Text(out)) }
522}
523
524/// `datum_to_json` and `datum_to_jsonb` for `JSON_SCALAR`: the JSON value of a SQL value, or null for null.
525///
526/// # Errors
527///
528/// The error of the output of the value.
529pub fn json_ctor_scalar(
530    ty: u32,
531    value: &Value,
532    jsonb: bool,
533    session: &dyn Session,
534) -> Result<Value> {
535    if value.is_null() {
536        return Ok(Value::Null);
537    }
538    let mut out = String::new();
539    json_value(ty, value, false, session, &mut out)?;
540    if jsonb { self::jsonb(&out) } else { Ok(Value::Text(out)) }
541}
542
543/// True when no object of the parsed value has a key two times.
544fn unique_keys(tree: JsonTree<'_>) -> bool {
545    tree.into_nodes().iter().all(|node| match &node.item {
546        JsonItem::Object(fields) => {
547            let mut keys = std::collections::HashSet::new();
548            fields.iter().all(|(key, _)| keys.insert(key.as_str()))
549        }
550        _ => true,
551    })
552}
553
554/// `JSON` with `WITH UNIQUE KEYS`: the `json` or `jsonb` value of a text that has no object with a key two times, as `json_validate` and `jsonb_from_text` check it.
555///
556/// # Errors
557///
558/// The error of the input of the type, or of a key that is not unique.
559pub fn json_ctor_parse(text: &str, jsonb: bool) -> Result<Value> {
560    let tree = JsonTree::parse(text).map_err(type_error)?;
561    if !unique_keys(tree) {
562        return Err(duplicate_key(None));
563    }
564    if jsonb { self::jsonb(text) } else { Ok(Value::text(text)) }
565}
566
567/// `ExecEvalJsonIsPredicate`: true when the value is JSON of the kind of the predicate. `base_type` is `text`, `json` or `jsonb`. The first token of a `text` or a `json` value gives its kind, and only a `text` value or a check of the keys parses all of the value.
568pub fn is_json(value: &str, base_type: u32, item: JsonItemType, unique: bool) -> bool {
569    let first = value.trim_start_matches(JSON_SPACE).chars().next();
570    let kind = match first {
571        Some('{') => JsonItemType::Object,
572        Some('[') => JsonItemType::Array,
573        Some(_) => JsonItemType::Scalar,
574        None => return false,
575    };
576    if base_type == oid::JSONB {
577        return item == JsonItemType::Any || item == kind;
578    }
579    if item != JsonItemType::Any && item != kind {
580        return false;
581    }
582    if unique {
583        return JsonTree::parse(value).is_ok_and(unique_keys);
584    }
585    base_type != oid::TEXT || json_in(value).is_ok()
586}