1use uqa_core::{DecimalValue, TemporalValue, Value};
10
11use crate::error::{Result, SQLError};
12
13use super::{hex_encode, out_of_range};
14
15mod path;
16mod production;
17#[cfg(test)]
18pub(super) use production::json_delete_with_control;
19pub(super) use production::{
20 cast_json_value_with_control, evaluate, format_core_value_as_json_with_control,
21 format_value_as_json_with_control, json_concat_with_control, json_delete_values_with_control,
22 json_extract_operator_with_control, quote_with_control, utf8_lossy_with_control,
23};
24
25pub(super) use path::{jsonpath_candidate, jsonpath_match};
26
27pub(super) fn parse_json(s: &str) -> Result<serde_json::Value> {
28 serde_json::from_str::<serde_json::Value>(s)
29 .map_err(|_| super::json_strip::invalid_json_input(s))
30}
31
32pub(super) fn format_json(value: &serde_json::Value, jsonb: bool) -> String {
35 if !jsonb {
36 return serde_json::to_string(value).expect("serializing a JSON value cannot fail");
37 }
38 match value {
39 serde_json::Value::Null => "null".to_string(),
40 serde_json::Value::Bool(value) => value.to_string(),
41 serde_json::Value::Number(value) => {
42 let text = value.to_string();
43 DecimalValue::parse(&text).map_or(text, |value| value.to_sql_string())
44 }
45 serde_json::Value::String(value) => serde_json::Value::String(value.clone()).to_string(),
46 serde_json::Value::Array(values) => {
47 let values = values
48 .iter()
49 .map(|value| format_json(value, true))
50 .collect::<Vec<_>>();
51 format!("[{}]", values.join(", "))
52 }
53 serde_json::Value::Object(values) => {
54 let mut values = values.iter().collect::<Vec<_>>();
55 values.sort_by(|(left, _), (right, _)| {
56 left.len()
57 .cmp(&right.len())
58 .then_with(|| left.as_bytes().cmp(right.as_bytes()))
59 });
60 let values = values
61 .into_iter()
62 .map(|(key, value)| {
63 let key = serde_json::Value::String(key.clone()).to_string();
64 format!("{key}: {}", format_json(value, true))
65 })
66 .collect::<Vec<_>>();
67 format!("{{{}}}", values.join(", "))
68 }
69 }
70}
71
72#[cfg(test)]
73fn format_jsonb_pretty(value: &serde_json::Value) -> String {
74 let Value::Str(text) =
75 ordinary("jsonb_pretty", &[Value::Json(value.to_string())]).expect("valid parsed JSON")
76 else {
77 unreachable!("JSON pretty text")
78 };
79 text
80}
81
82pub(super) fn typed_json_value(value: &serde_json::Value, jsonb: bool) -> Result<Value> {
83 if jsonb {
84 validate_jsonb_numbers(value)?;
85 }
86 let text = format_json(value, jsonb);
87 if jsonb {
88 Ok(Value::JsonB(text))
89 } else {
90 Ok(Value::Json(text))
91 }
92}
93
94fn validate_jsonb_numbers(value: &serde_json::Value) -> Result<()> {
95 match value {
96 serde_json::Value::Number(value) => DecimalValue::parse(&value.to_string())
97 .map(|_| ())
98 .ok_or_else(|| out_of_range("numeric")),
99 serde_json::Value::Array(values) => values.iter().try_for_each(validate_jsonb_numbers),
100 serde_json::Value::Object(values) => values.values().try_for_each(validate_jsonb_numbers),
101 _ => Ok(()),
102 }
103}
104
105pub fn value_to_json_text(value: &Value) -> String {
108 match value {
109 Value::Null => "null".to_string(),
110 Value::Void => "\"\"".to_string(),
111 Value::Bool(value) => value.to_string(),
112 Value::Int(value) => value.to_string(),
113 Value::Float(value) => match serde_json::Number::from_f64(*value) {
114 Some(number) => number.to_string(),
115 None if value.is_nan() => "\"NaN\"".to_string(),
116 None if value.is_sign_positive() => "\"Infinity\"".to_string(),
117 None => "\"-Infinity\"".to_string(),
118 },
119 Value::Decimal(value) => value.to_sql_string(),
120 Value::Str(value) => serde_json::Value::String(value.clone()).to_string(),
121 Value::FixedChar(value) => {
122 serde_json::Value::String(value.trim_end_matches(' ').to_string()).to_string()
123 }
124 Value::Bytes(value) => {
125 serde_json::Value::String(format!("0x{}", hex_encode(value))).to_string()
126 }
127 Value::Temporal(value) => serde_json::Value::String(value.to_sql_string()).to_string(),
128 Value::Json(text) | Value::JsonB(text) => text.clone(),
129 Value::LegacyVector(vector) => legacy_vector_json(vector).to_string(),
130 Value::Array(array) => {
131 let values = array
132 .elements()
133 .iter()
134 .map(value_to_json_text)
135 .collect::<Vec<_>>();
136 format!("[{}]", values.join(","))
137 }
138 Value::List(values) => {
139 let values = values.iter().map(value_to_json_text).collect::<Vec<_>>();
140 format!("[{}]", values.join(","))
141 }
142 Value::Row(values) => record_json_text(
143 values
144 .iter()
145 .enumerate()
146 .map(|(index, value)| (format!("f{}", index + 1), value)),
147 ),
148 Value::Record(fields) => {
149 record_json_text(fields.iter().map(|(name, value)| (name.clone(), value)))
150 }
151 Value::Map(values) => {
152 let values = values
153 .iter()
154 .map(|(key, value)| {
155 let key = serde_json::Value::String(key.clone()).to_string();
156 format!("{key}:{}", value_to_json_text(value))
157 })
158 .collect::<Vec<_>>();
159 format!("{{{}}}", values.join(","))
160 }
161 }
162}
163
164fn legacy_vector_json(vector: &uqa_core::LegacyVectorValue) -> serde_json::Value {
165 serde_json::Value::Array(
166 vector
167 .elements()
168 .iter()
169 .map(|value| {
170 let Value::Int(value) = value else {
171 unreachable!("validated legacy vector element");
172 };
173 match vector.kind() {
174 uqa_core::LegacyVectorKind::SmallInteger => (*value).into(),
175 uqa_core::LegacyVectorKind::Oid => value.to_string().into(),
176 }
177 })
178 .collect(),
179 )
180}
181
182fn record_json_text<'a>(fields: impl IntoIterator<Item = (String, &'a Value)>) -> String {
183 let fields = fields
184 .into_iter()
185 .map(|(name, value)| {
186 let name = serde_json::Value::String(name).to_string();
187 format!("{name}:{}", value_to_json_text(value))
188 })
189 .collect::<Vec<_>>();
190 format!("{{{}}}", fields.join(","))
191}
192
193pub(super) fn json_build_array_value(args: &[Value], jsonb: bool) -> Result<Value> {
194 let text = format!(
195 "[{}]",
196 args.iter()
197 .map(value_to_json_text)
198 .collect::<Vec<_>>()
199 .join(", ")
200 );
201 if jsonb {
202 typed_json_value(&parse_json(&text)?, true)
203 } else {
204 Ok(Value::Json(text))
205 }
206}
207
208pub fn validate_json_object_key_type(value: &Value) -> Result<()> {
210 if matches!(
211 value,
212 Value::Array(_)
213 | Value::LegacyVector(_)
214 | Value::List(_)
215 | Value::Row(_)
216 | Value::Record(_)
217 | Value::Map(_)
218 | Value::Json(_)
219 | Value::JsonB(_)
220 ) {
221 return Err(SQLError::Routine {
222 sqlstate: "22023".into(),
223 message: "key value must be scalar, not array, composite, or json".into(),
224 });
225 }
226 Ok(())
227}
228
229pub(super) fn json_build_object_value(args: &[Value], jsonb: bool) -> Result<Value> {
230 if !args.len().is_multiple_of(2) {
231 return Err(SQLError::TypeMismatch(
232 "json_build_object requires an even number of args".into(),
233 ));
234 }
235 let mut fields = Vec::with_capacity(args.len() / 2);
236 for pair in args.chunks_exact(2) {
237 if matches!(pair[0], Value::Null) {
238 return Err(SQLError::TypeMismatch(
239 "json_build_object key must not be NULL".into(),
240 ));
241 }
242 validate_json_object_key_type(&pair[0])?;
243 let key = serde_json::Value::String(super::value_to_string(&pair[0])?).to_string();
244 fields.push(format!("{key} : {}", value_to_json_text(&pair[1])));
245 }
246 let text = format!("{{{}}}", fields.join(", "));
247 if jsonb {
248 typed_json_value(&parse_json(&text)?, true)
249 } else {
250 Ok(Value::Json(text))
251 }
252}
253
254#[cfg(test)]
255pub(super) fn value_to_json(v: &Value) -> serde_json::Value {
256 match v {
257 Value::Null => serde_json::Value::Null,
258 Value::Void => serde_json::Value::String(String::new()),
259 Value::Bool(b) => serde_json::Value::Bool(*b),
260 Value::Int(i) => serde_json::Value::Number((*i).into()),
261 Value::Float(f) => serde_json::Number::from_f64(*f).map_or_else(
262 || {
263 let label = if f.is_nan() {
264 "NaN"
265 } else if f.is_sign_positive() {
266 "Infinity"
267 } else {
268 "-Infinity"
269 };
270 serde_json::Value::String(label.to_string())
271 },
272 serde_json::Value::Number,
273 ),
274 Value::Decimal(d) => {
275 if d.is_nan() || d.is_infinite() {
276 serde_json::Value::String(d.to_sql_string())
277 } else {
278 d.to_sql_string()
279 .parse::<serde_json::Number>()
280 .map(serde_json::Value::Number)
281 .unwrap_or_else(|_| serde_json::Value::String(d.to_sql_string()))
282 }
283 }
284 Value::Str(s) => serde_json::Value::String(s.clone()),
285 Value::FixedChar(s) => serde_json::Value::String(s.trim_end_matches(' ').to_string()),
286 Value::Bytes(b) => serde_json::Value::String(format!("0x{}", hex_encode(b))),
287 Value::Temporal(t) => serde_json::Value::String(t.to_sql_string()),
288 Value::Json(text) | Value::JsonB(text) => {
289 serde_json::from_str(text).unwrap_or_else(|_| serde_json::Value::String(text.clone()))
290 }
291 Value::LegacyVector(vector) => legacy_vector_json(vector),
292 Value::Array(array) => {
293 serde_json::Value::Array(array.elements().iter().map(value_to_json).collect())
294 }
295 Value::List(items) => serde_json::Value::Array(items.iter().map(value_to_json).collect()),
296 Value::Row(values) => serde_json::Value::Object(
297 values
298 .iter()
299 .enumerate()
300 .map(|(index, value)| (format!("f{}", index + 1), value_to_json(value)))
301 .collect(),
302 ),
303 Value::Record(fields) => serde_json::Value::Object(
304 fields
305 .iter()
306 .map(|(name, value)| (name.clone(), value_to_json(value)))
307 .collect(),
308 ),
309 Value::Map(map) => {
310 let mut obj = serde_json::Map::new();
311 for (k, v) in map {
312 obj.insert(k.clone(), value_to_json(v));
313 }
314 serde_json::Value::Object(obj)
315 }
316 }
317}
318
319#[allow(dead_code)]
320pub(super) fn json_to_value(json: &serde_json::Value) -> Value {
321 match json {
322 serde_json::Value::Null => Value::Null,
323 serde_json::Value::Bool(b) => Value::Bool(*b),
324 serde_json::Value::Number(n) => {
325 if let Some(i) = n.as_i64() {
326 Value::Int(i)
327 } else if let Some(d) = DecimalValue::parse(&n.to_string()) {
328 Value::Decimal(d)
329 } else if let Some(f) = n.as_f64() {
330 Value::Float(f)
331 } else {
332 Value::Null
333 }
334 }
335 serde_json::Value::String(s) => Value::Str(s.clone()),
336 serde_json::Value::Array(arr) => Value::List(arr.iter().map(json_to_value).collect()),
337 serde_json::Value::Object(obj) => {
338 if let Ok(temporal) =
339 serde_json::from_value::<TemporalValue>(serde_json::Value::Object(obj.clone()))
340 {
341 return Value::Temporal(temporal);
342 }
343 let mut map = std::collections::BTreeMap::new();
344 for (k, v) in obj {
345 map.insert(k.clone(), json_to_value(v));
346 }
347 Value::Map(map)
348 }
349 }
350}
351
352fn ordinary(name: &str, args: &[Value]) -> Result<Value> {
353 Ok(production::evaluate(
354 name,
355 args,
356 &uqa_core::memory::ProductionControl::uncontrolled(),
357 )
358 .expect("known JSON builtin")?
359 .into_uncontrolled()
360 .expect("ordinary JSON has no lease"))
361}
362
363fn json_array_index(len: usize, key: &str) -> Option<usize> {
364 let index = key.parse::<i64>().ok()?;
365 let normalized = if index < 0 { len as i64 + index } else { index };
366 usize::try_from(normalized).ok().filter(|idx| *idx < len)
367}
368
369fn json_insert_index(len: usize, key: &str, insert_after: bool) -> Option<usize> {
370 let raw = key.parse::<i64>().ok()?;
371 let len_i64 = len as i64;
372 let index = if raw >= 0 {
373 if raw >= len_i64 {
374 len_i64
375 } else if insert_after {
376 raw + 1
377 } else {
378 raw
379 }
380 } else {
381 let normalized = len_i64 + raw;
382 if normalized < 0 {
383 0
384 } else if insert_after {
385 normalized + 1
386 } else {
387 normalized
388 }
389 };
390 usize::try_from(index.clamp(0, len_i64)).ok()
391}
392
393#[cfg(test)]
394mod pretty_tests {
395 use super::{format_jsonb_pretty, parse_json, typed_json_value, DecimalValue};
396
397 #[test]
398 fn jsonb_pretty_uses_postgresql_layout_and_key_order() {
399 let value = parse_json(r#"{"zz":1,"b":[],"aa":{"long":3,"x":2}}"#).unwrap();
400 assert_eq!(
401 format_jsonb_pretty(&value),
402 "{\n \"b\": [\n ],\n \"aa\": {\n \"x\": 2,\n \"long\": 3\n },\n \"zz\": 1\n}"
403 );
404 assert_eq!(format_jsonb_pretty(&parse_json("[]").unwrap()), "[\n]");
405 assert_eq!(format_jsonb_pretty(&parse_json("{}").unwrap()), "{\n}");
406 assert_eq!(
407 format_jsonb_pretty(&parse_json("1e-1000").unwrap()),
408 DecimalValue::parse("1e-1000").unwrap().to_sql_string()
409 );
410 assert_eq!(format_jsonb_pretty(&parse_json("1.00").unwrap()), "1.00");
411 assert_eq!(format_jsonb_pretty(&parse_json("-0").unwrap()), "0");
412 }
413
414 #[test]
415 fn jsonb_rejects_numbers_outside_postgresql_numeric_range() {
416 let maximum = parse_json("1e131071").unwrap();
417 assert!(typed_json_value(&maximum, true).is_ok());
418
419 for text in ["1e131072", "1e-16384", "[1e131072]", r#"{"n":1e131072}"#] {
420 let error = typed_json_value(&parse_json(text).unwrap(), true).unwrap_err();
421 assert_eq!(error.sqlstate(), Some("22003"));
422 }
423
424 assert!(typed_json_value(&parse_json("1e200000").unwrap(), false).is_ok());
425 assert!(typed_json_value(&parse_json("0e200000").unwrap(), true).is_ok());
426 }
427}