1use 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
19pub(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
44fn jsonb(text: &str) -> Result<Value> {
46 jsonb_in(text).map(Value::Text).map_err(type_error)
47}
48
49pub 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
55pub 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
124fn 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
134fn 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
160fn 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
182fn 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
210fn 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
226fn 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
242fn 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
259fn 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
300fn 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
326type TextArray<'a> = (Vec<i32>, Vec<Option<&'a str>>);
328
329fn 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
340fn 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
366fn 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
382fn 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
415const JSON_SPACE: [char; 4] = [' ', '\t', '\n', '\r'];
417
418fn 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
427pub 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 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
497pub 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
524pub 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
543fn 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
554pub 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
567pub 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}