1use std::cell::OnceCell;
10use std::cmp::Ordering;
11use std::io::Write;
12use std::rc::Rc;
13
14use rupg_common::{Error, Result, SqlState};
15use rupg_types::{
16 DateFormat, DateOrder, DateStyle, FixedZone, JSONPATH_LAST, JsonItem, JsonNode, JsonPath,
17 JsonTree, Numeric, NumericSign, PathItem, PathOp, UNIX_TO_POSTGRES_USECS, USECS_PER_SEC, Value,
18 adjust_time, adjust_timestamp, bool_in, date_out, float8_in, int4_in, int8_in, json_escape,
19 jsonpath_regex_flags, local_offset, numeric_in, numeric_out, oid, time_out, timestamp_out,
20 timestamptz_out,
21};
22
23use crate::cast::midnight;
24use crate::compare::{compare, datetime_order};
25use crate::formatting::parse_datetime;
26use crate::json::text;
27use crate::{Call, Kernel, bad_value, type_error};
28
29pub(crate) fn by_src(src: &str) -> Option<Kernel> {
31 Some(match src {
32 "jsonb_path_exists" | "jsonb_path_exists_opr" => |call, args| exists(call, args, false),
33 "jsonb_path_exists_tz" => |call, args| exists(call, args, true),
34 "jsonb_path_match" | "jsonb_path_match_opr" => |call, args| path_match(call, args, false),
35 "jsonb_path_match_tz" => |call, args| path_match(call, args, true),
36 "jsonb_path_query_array" => |call, args| query_array(call, args, false),
37 "jsonb_path_query_array_tz" => |call, args| query_array(call, args, true),
38 "jsonb_path_query_first" => |call, args| query_first(call, args, false),
39 "jsonb_path_query_first_tz" => |call, args| query_first(call, args, true),
40 _ => return None,
41 })
42}
43
44const ISO_FORMATS: [&str; 13] = [
46 "yyyy-mm-dd",
47 "HH24:MI:SS.USTZ",
48 "HH24:MI:SSTZ",
49 "HH24:MI:SS.US",
50 "HH24:MI:SS",
51 "yyyy-mm-dd HH24:MI:SS.USTZ",
52 "yyyy-mm-dd HH24:MI:SSTZ",
53 "yyyy-mm-dd\"T\"HH24:MI:SS.USTZ",
54 "yyyy-mm-dd\"T\"HH24:MI:SSTZ",
55 "yyyy-mm-dd HH24:MI:SS.US",
56 "yyyy-mm-dd HH24:MI:SS",
57 "yyyy-mm-dd\"T\"HH24:MI:SS.US",
58 "yyyy-mm-dd\"T\"HH24:MI:SS",
59];
60
61const MAX_DEPTH: usize = 2_000;
63
64const BASE_ID_FACTOR: i64 = 10_000_000_000;
66
67struct Doc {
69 text: String,
70 nodes: Vec<JsonNode>,
71 offsets: OnceCell<Vec<i64>>,
73}
74
75impl Doc {
76 fn parse(text: String) -> Result<Rc<Doc>> {
78 let nodes = JsonTree::parse(&text).map_err(type_error)?.into_nodes();
79 Ok(Rc::new(Doc { text, nodes, offsets: OnceCell::new() }))
80 }
81
82 fn item(&self, at: usize) -> &JsonItem {
83 &self.nodes[at].item
84 }
85
86 fn text(&self, at: usize) -> &str {
88 let node = &self.nodes[at];
89 &self.text[node.start..node.end]
90 }
91
92 fn get(self: &Rc<Self>, at: usize) -> Result<Item> {
94 Ok(match self.item(at) {
95 JsonItem::Null => Item::Null,
96 JsonItem::Bool(b) => Item::Bool(*b),
97 JsonItem::Number => Item::Numeric(numeric_in(self.text(at), -1).map_err(type_error)?),
98 JsonItem::String(s) => Item::String(s.clone()),
99 JsonItem::Array(_) | JsonItem::Object(_) => Item::Node(Rc::clone(self), at),
100 })
101 }
102
103 fn field(&self, at: usize, name: &str) -> Option<usize> {
105 match self.item(at) {
106 JsonItem::Object(fields) => fields.iter().find(|(k, _)| k == name).map(|f| f.1),
107 _ => None,
108 }
109 }
110
111 fn children(&self, at: usize) -> Vec<usize> {
113 match self.item(at) {
114 JsonItem::Array(items) => items.clone(),
115 JsonItem::Object(fields) => fields.iter().map(|f| f.1).collect(),
116 _ => Vec::new(),
117 }
118 }
119
120 fn offset(&self, at: usize) -> i64 {
122 self.offsets.get_or_init(|| self.layout())[at]
123 }
124
125 fn layout(&self) -> Vec<i64> {
127 let mut offsets = vec![0; self.nodes.len()];
128 self.place(0, 4, &mut offsets);
129 offsets
130 }
131
132 fn place(&self, at: usize, pos: usize, offsets: &mut [i64]) -> usize {
134 let mut start = pos.next_multiple_of(4);
135 let (header, children) = match self.item(at) {
136 JsonItem::Null | JsonItem::Bool(_) => return pos,
137 JsonItem::String(s) => return pos + s.len(),
138 JsonItem::Number => {
139 return start + numeric_in(self.text(at), -1).map_or(8, |n| numeric_size(&n));
140 }
141 JsonItem::Array(items) => (4 + 4 * items.len(), items.clone()),
142 JsonItem::Object(fields) => {
143 let keys: usize = fields.iter().map(|f| f.0.len()).sum();
144 (4 + 8 * fields.len() + keys, fields.iter().map(|f| f.1).collect())
145 }
146 };
147 offsets[at] = i64::try_from(start - 4).unwrap_or(i64::MAX);
148 start += header;
149 for child in children {
150 start = self.place(child, start, offsets);
151 }
152 start
153 }
154}
155
156fn numeric_size(n: &Numeric) -> usize {
158 let short = n.dscale() <= 0x3f && (-64..=63).contains(&n.weight());
159 4 + if short { 2 } else { 4 } + 2 * n.digits().len()
160}
161
162fn numeric_text(n: &Numeric) -> String {
164 let mut out = Vec::new();
165 numeric_out(n, &mut out);
166 String::from_utf8(out).unwrap_or_default()
167}
168
169fn int4(n: &Numeric) -> Option<i32> {
171 n.to_integer("integer").ok().and_then(|v| i32::try_from(v).ok())
172}
173
174fn is_special(n: &Numeric) -> bool {
176 matches!(n.sign(), NumericSign::NaN | NumericSign::Infinity | NumericSign::NegativeInfinity)
177}
178
179#[derive(Clone)]
181enum Item {
182 Null,
183 Bool(bool),
184 Numeric(Numeric),
185 String(String),
186 Node(Rc<Doc>, usize),
188 Datetime(Value, String),
190}
191
192impl Item {
193 fn array(&self) -> Option<(&Rc<Doc>, usize, usize)> {
195 match self {
196 Item::Node(doc, at) => match doc.item(*at) {
197 JsonItem::Array(items) => Some((doc, *at, items.len())),
198 _ => None,
199 },
200 _ => None,
201 }
202 }
203
204 fn is_array(&self) -> bool {
205 self.array().is_some()
206 }
207
208 fn object(&self) -> Option<(&Rc<Doc>, usize)> {
210 match self {
211 Item::Node(doc, at) if matches!(doc.item(*at), JsonItem::Object(_)) => Some((doc, *at)),
212 _ => None,
213 }
214 }
215
216 fn type_name(&self) -> &'static str {
218 match self {
219 Item::Null => "null",
220 Item::Bool(_) => "boolean",
221 Item::Numeric(_) => "number",
222 Item::String(_) => "string",
223 Item::Node(..) if self.is_array() => "array",
224 Item::Node(..) => "object",
225 Item::Datetime(value, _) => match value {
226 Value::Date(_) => "date",
227 Value::Time(_) => "time without time zone",
228 Value::TimeTz(..) => "time with time zone",
229 Value::Timestamp(_) => "timestamp without time zone",
230 _ => "timestamp with time zone",
231 },
232 }
233 }
234
235 fn write(&self, out: &mut String) {
237 match self {
238 Item::Null => out.push_str("null"),
239 Item::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
240 Item::Numeric(n) => out.push_str(&numeric_text(n)),
241 Item::String(s) | Item::Datetime(_, s) => json_escape(s, out),
242 Item::Node(doc, at) => out.push_str(doc.text(*at)),
243 }
244 }
245
246 fn to_value(&self) -> Value {
248 let mut out = String::new();
249 self.write(&mut out);
250 Value::Text(out)
251 }
252}
253
254#[derive(Clone, Copy, Debug, PartialEq, Eq)]
256enum Res {
257 Ok,
258 NotFound,
259 Error,
260}
261
262#[derive(Clone, Copy, Debug, PartialEq, Eq)]
264enum Truth {
265 False,
266 True,
267 Unknown,
268}
269
270impl Truth {
271 fn of(b: bool) -> Truth {
272 if b { Truth::True } else { Truth::False }
273 }
274}
275
276type Found<'f> = Option<&'f mut Vec<Item>>;
278
279type Test<'t> = &'t mut dyn FnMut(&Item, Option<&Item>) -> Result<Truth>;
281
282#[derive(Clone)]
284struct Base {
285 node: Option<(Rc<Doc>, usize)>,
286 id: i64,
287}
288
289impl Base {
290 fn of(item: &Item, id: i64) -> Base {
292 let node = match item {
293 Item::Node(doc, at) => Some((Rc::clone(doc), *at)),
294 _ => None,
295 };
296 Base { node, id }
297 }
298}
299
300struct Walk<'c> {
302 chain: &'c [PathItem],
304 first: u32,
305 last: u32,
306 ignore_structural: bool,
307 unwrap: bool,
308}
309
310impl Walk<'_> {
311 fn one(chain: &[PathItem], unwrap: bool) -> Walk<'_> {
313 Walk { chain, first: 1, last: 1, ignore_structural: false, unwrap }
314 }
315}
316
317fn too_deep() -> Error {
319 Error::new(SqlState::STATEMENT_TOO_COMPLEX, "stack depth limit exceeded").with_hint("Increase the configuration parameter \"max_stack_depth\" (currently 2048kB), after ensuring the platform's stack depth limit is adequate.")
320}
321
322fn head(chain: &[PathItem]) -> Result<&PathItem> {
324 chain.first().ok_or_else(|| Error::internal("empty jsonpath expression"))
325}
326
327enum Vars<'a> {
329 None,
331 Object(Rc<Doc>),
333 Passing(&'a [PathVar]),
335}
336
337#[derive(Clone, Debug, PartialEq)]
339pub struct PathVar {
340 pub name: String,
341 pub ty: u32,
342 pub value: Value,
343}
344
345#[derive(Clone, Debug, PartialEq)]
347pub enum SqlJsonItem {
348 Null,
349 Bool(bool),
350 Numeric(Numeric),
351 String(String),
352 Datetime(Value, String),
354 Container(String),
356}
357
358impl SqlJsonItem {
359 pub fn jsonb(&self) -> String {
361 let mut out = String::new();
362 match self {
363 SqlJsonItem::Null => out.push_str("null"),
364 SqlJsonItem::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
365 SqlJsonItem::Numeric(n) => out.push_str(&numeric_text(n)),
366 SqlJsonItem::String(s) | SqlJsonItem::Datetime(_, s) => json_escape(s, &mut out),
367 SqlJsonItem::Container(text) => out.push_str(text),
368 }
369 out
370 }
371}
372
373fn passing_item(call: &Call<'_>, var: &PathVar) -> Result<Item> {
375 Ok(match (var.ty, &var.value) {
376 (_, Value::Null) => Item::Null,
377 (oid::BOOL, Value::Bool(b)) => Item::Bool(*b),
378 (oid::NUMERIC, Value::Numeric(n)) => Item::Numeric(n.clone()),
379 (oid::INT2 | oid::INT4 | oid::INT8, value) => {
380 Item::Numeric(Numeric::from_integer(value.as_i64().ok_or_else(bad_value)?.into()))
381 }
382 (oid::FLOAT4, value @ Value::Float4(_)) => {
383 match crate::cast::float4_to_numeric(call, std::slice::from_ref(value))? {
384 Value::Numeric(n) => Item::Numeric(n),
385 _ => return Err(bad_value()),
386 }
387 }
388 (oid::FLOAT8, Value::Float8(f)) => Item::Numeric(Numeric::from_f64(*f)),
389 (oid::TEXT | oid::VARCHAR, Value::Text(s)) => Item::String(s.clone()),
390 (oid::DATE | oid::TIME | oid::TIMETZ | oid::TIMESTAMP | oid::TIMESTAMPTZ, value) => {
391 Item::Datetime(value.clone(), datetime_text(value, 0)?)
392 }
393 (oid::JSONB, Value::Text(text)) => Doc::parse(text.clone())?.get(0)?,
394 (oid::JSON, Value::Text(text)) => {
395 let Value::Text(text) = crate::input(oid::JSONB, text, -1, call.session)? else {
396 return Err(bad_value());
397 };
398 Doc::parse(text)?.get(0)?
399 }
400 (ty, _) => {
401 return Err(Error::new(
402 SqlState::INVALID_PARAMETER_VALUE,
403 format!(
404 "could not convert value of type {} to jsonpath",
405 crate::type_message_name(ty, call.session)
406 ),
407 ));
408 }
409 })
410}
411
412struct Exec<'a> {
414 call: &'a Call<'a>,
415 vars: Vars<'a>,
416 lax: bool,
417 ignore_structural: bool,
418 throw: bool,
419 root: Item,
420 current: Item,
421 base: Base,
422 last_id: i64,
423 innermost: Option<usize>,
425 depth: usize,
426 use_tz: bool,
428}
429
430impl Exec<'_> {
431 fn fail(&self, error: Error) -> Result<Res> {
433 if self.throw { Err(error) } else { Ok(Res::Error) }
434 }
435
436 fn fail_value<T>(&self, error: Error) -> Result<Option<T>> {
438 if self.throw { Err(error) } else { Ok(None) }
439 }
440
441 fn method_fail(&self, state: SqlState, op: PathOp, what: &str) -> Result<Res> {
443 self.fail(Error::new(
444 state,
445 format!("jsonpath item method .{}() can only be applied to {what}", op.name()),
446 ))
447 }
448
449 fn invalid_argument(&self, text: &str, op: PathOp, sqltype: &str) -> Result<Res> {
451 self.fail(Error::new(
452 SqlState::NON_NUMERIC_SQL_JSON_ITEM,
453 format!(
454 "argument \"{text}\" of jsonpath item method .{}() is invalid for type {sqltype}",
455 op.name()
456 ),
457 ))
458 }
459
460 fn quiet<T>(&mut self, f: impl FnOnce(&mut Self) -> Result<T>) -> Result<T> {
462 let throw = std::mem::replace(&mut self.throw, false);
463 let out = f(self);
464 self.throw = throw;
465 out
466 }
467
468 fn item(&mut self, chain: &[PathItem], jb: &Item, found: Found<'_>) -> Result<Res> {
470 self.target(chain, jb, found, self.lax)
471 }
472
473 fn next(&mut self, rest: &[PathItem], v: Item, found: Found<'_>) -> Result<Res> {
475 if !rest.is_empty() {
476 return self.item(rest, &v, found);
477 }
478 if let Some(found) = found {
479 found.push(v);
480 }
481 Ok(Res::Ok)
482 }
483
484 fn target(
486 &mut self,
487 chain: &[PathItem],
488 jb: &Item,
489 found: Found<'_>,
490 unwrap: bool,
491 ) -> Result<Res> {
492 if self.depth >= MAX_DEPTH {
493 return Err(too_deep());
494 }
495 self.depth += 1;
496 let res = self.step(chain, jb, found, unwrap);
497 self.depth -= 1;
498 res
499 }
500
501 fn unwrap_target(&mut self, chain: &[PathItem], jb: &Item, found: Found<'_>) -> Result<Res> {
503 let Some((doc, at, _)) = jb.array() else { return Err(bad_value()) };
504 self.any(&Walk::one(chain, false), doc, at, found, 1)
505 }
506
507 fn step(
509 &mut self,
510 chain: &[PathItem],
511 jb: &Item,
512 mut found: Found<'_>,
513 unwrap: bool,
514 ) -> Result<Res> {
515 let (jsp, rest) = (head(chain)?, &chain[1..]);
516 match jsp {
517 PathItem::Null
518 | PathItem::Bool(_)
519 | PathItem::Numeric(_)
520 | PathItem::String(_)
521 | PathItem::Variable(_) => {
522 if rest.is_empty() && found.is_none() && !matches!(jsp, PathItem::Variable(_)) {
524 return Ok(Res::Ok);
525 }
526 let base = self.base.clone();
527 let v = self.literal(jsp)?;
528 let res = self.next(rest, v, found);
529 self.base = base;
530 res
531 }
532 PathItem::Binary(
533 op @ (PathOp::Add | PathOp::Sub | PathOp::Mul | PathOp::Div | PathOp::Mod),
534 l,
535 r,
536 ) => self.binary_arith(*op, l, r, rest, jb, found),
537 PathItem::Unary(op @ (PathOp::Plus | PathOp::Minus), arg) => {
538 self.unary_arith(*op, arg, rest, jb, found)
539 }
540 PathItem::Unary(PathOp::Filter, cond) => {
541 if unwrap && jb.is_array() {
542 return self.unwrap_target(chain, jb, found);
543 }
544 if self.nested_truth(cond, jb)? != Truth::True {
545 return Ok(Res::NotFound);
546 }
547 self.next(rest, jb.clone(), found)
548 }
549 PathItem::Binary(..) | PathItem::Unary(..) | PathItem::LikeRegex { .. } => {
550 let st = self.truth(jsp, jb)?;
551 self.append_bool(rest, found, st)
552 }
553 PathItem::AnyArray => {
554 if let Some((doc, at, _)) = jb.array() {
555 return self.any(&Walk::one(rest, self.lax), doc, at, found, 1);
556 }
557 if self.lax {
558 return self.next(rest, jb.clone(), found);
559 }
560 if !self.ignore_structural {
561 return self.fail(Error::new(
562 SqlState::SQL_JSON_ARRAY_NOT_FOUND,
563 "jsonpath wildcard array accessor can only be applied to an array",
564 ));
565 }
566 Ok(Res::NotFound)
567 }
568 PathItem::AnyKey => {
569 if let Some((doc, at)) = jb.object() {
570 return self.any(&Walk::one(rest, self.lax), doc, at, found, 1);
571 }
572 if unwrap && jb.is_array() {
573 return self.unwrap_target(chain, jb, found);
574 }
575 if !self.ignore_structural {
576 return self.fail(Error::new(
577 SqlState::SQL_JSON_OBJECT_NOT_FOUND,
578 "jsonpath wildcard member accessor can only be applied to an object",
579 ));
580 }
581 Ok(Res::NotFound)
582 }
583 PathItem::IndexArray(subscripts) => {
584 if !jb.is_array() && !self.lax {
585 if !self.ignore_structural {
586 return self.fail(Error::new(
587 SqlState::SQL_JSON_ARRAY_NOT_FOUND,
588 "jsonpath array accessor can only be applied to an array",
589 ));
590 }
591 return Ok(Res::NotFound);
592 }
593 let saved = self.innermost;
594 let res = self.subscripts(subscripts, rest, jb, found);
595 self.innermost = saved;
596 res
597 }
598 PathItem::Any { first, last } => {
599 let mut res = Res::NotFound;
600 if *first == 0 {
602 let saved = std::mem::replace(&mut self.ignore_structural, true);
603 res = self.next(rest, jb.clone(), found.as_deref_mut())?;
604 self.ignore_structural = saved;
605 if res == Res::Ok && found.is_none() {
606 return Ok(res);
607 }
608 }
609 if let Item::Node(doc, at) = jb {
610 let walk = Walk {
611 chain: rest,
612 first: *first,
613 last: *last,
614 ignore_structural: true,
615 unwrap: self.lax,
616 };
617 res = self.any(&walk, doc, *at, found, 1)?;
618 }
619 Ok(res)
620 }
621 PathItem::Key(name) => {
622 if let Some((doc, at)) = jb.object() {
623 if let Some(child) = doc.field(at, name) {
624 return self.next(rest, doc.get(child)?, found);
625 }
626 if !self.ignore_structural {
627 return self.fail(Error::new(
628 SqlState::SQL_JSON_MEMBER_NOT_FOUND,
629 format!("JSON object does not contain key \"{name}\""),
630 ));
631 }
632 return Ok(Res::NotFound);
633 }
634 if unwrap && jb.is_array() {
635 return self.unwrap_target(chain, jb, found);
636 }
637 if !self.ignore_structural {
638 return self.fail(Error::new(
639 SqlState::SQL_JSON_MEMBER_NOT_FOUND,
640 "jsonpath member accessor can only be applied to an object",
641 ));
642 }
643 Ok(Res::NotFound)
644 }
645 PathItem::Current => {
646 let current = self.current.clone();
647 self.next(rest, current, found)
648 }
649 PathItem::Root => {
650 let root = self.root.clone();
651 let base = std::mem::replace(&mut self.base, Base::of(&root, 0));
652 let res = self.next(rest, root, found);
653 self.base = base;
654 res
655 }
656 PathItem::Last => {
657 let Some(size) = self.innermost else {
658 return Err(Error::internal(
659 "evaluating jsonpath LAST outside of array subscript",
660 ));
661 };
662 if rest.is_empty() && found.is_none() {
663 return Ok(Res::Ok);
664 }
665 let last = i128::try_from(size).unwrap_or(i128::MAX) - 1;
666 self.next(rest, Item::Numeric(Numeric::from_integer(last)), found)
667 }
668 PathItem::Method(op, args) => self.method(*op, args, chain, jb, found, unwrap),
669 }
670 }
671
672 fn literal(&mut self, jsp: &PathItem) -> Result<Item> {
674 Ok(match jsp {
675 PathItem::Null => Item::Null,
676 PathItem::Bool(b) => Item::Bool(*b),
677 PathItem::Numeric(n) => Item::Numeric(n.clone()),
678 PathItem::String(s) => Item::String(s.clone()),
679 PathItem::Variable(name) => {
680 let missing = || {
681 Error::new(
682 SqlState::UNDEFINED_OBJECT,
683 format!("could not find jsonpath variable \"{name}\""),
684 )
685 };
686 match &self.vars {
687 Vars::None => return Err(missing()),
688 Vars::Object(vars) => {
689 let vars = Rc::clone(vars);
690 let child = vars.field(0, name).ok_or_else(missing)?;
691 self.base = Base { node: Some((Rc::clone(&vars), 0)), id: 1 };
692 vars.get(child)?
693 }
694 Vars::Passing(vars) => {
695 let at = vars.iter().position(|v| v.name == *name).ok_or_else(missing)?;
696 let item = passing_item(self.call, &vars[at])?;
697 self.base = Base::of(&item, i64::try_from(at + 1).unwrap_or(i64::MAX));
698 item
699 }
700 }
701 }
702 _ => return Err(Error::internal("unexpected jsonpath item type")),
703 })
704 }
705
706 fn subscripts(
708 &mut self,
709 subscripts: &[(Vec<PathItem>, Option<Vec<PathItem>>)],
710 rest: &[PathItem],
711 jb: &Item,
712 mut found: Found<'_>,
713 ) -> Result<Res> {
714 let array = jb.array();
715 let size = array.map_or(1, |a| a.2);
716 self.innermost = Some(size);
717 let size = i64::try_from(size).unwrap_or(i64::MAX);
718 let mut res = Res::NotFound;
719 for (from, to) in subscripts {
720 let Some(index_from) = self.index(from, jb)? else {
721 res = Res::Error;
722 break;
723 };
724 let index_to = match to {
725 Some(to) => match self.index(to, jb)? {
726 Some(index) => index,
727 None => {
728 res = Res::Error;
729 break;
730 }
731 },
732 None => index_from,
733 };
734 let (index_from, index_to) = (i64::from(index_from), i64::from(index_to));
735 if !self.ignore_structural
736 && (index_from < 0 || index_from > index_to || index_to >= size)
737 {
738 return self.fail(Error::new(
739 SqlState::INVALID_SQL_JSON_SUBSCRIPT,
740 "jsonpath array subscript is out of bounds",
741 ));
742 }
743 res = Res::NotFound;
744 for index in index_from.max(0)..=index_to.min(size - 1) {
745 let v = match array {
746 None => jb.clone(),
747 Some((doc, at, _)) => {
748 let JsonItem::Array(items) = doc.item(at) else { break };
749 match usize::try_from(index).ok().and_then(|i| items.get(i)) {
750 Some(&child) => doc.get(child)?,
751 None => continue,
752 }
753 }
754 };
755 if rest.is_empty() && found.is_none() {
756 return Ok(Res::Ok);
757 }
758 res = self.next(rest, v, found.as_deref_mut())?;
759 if res == Res::Error || (res == Res::Ok && found.is_none()) {
760 break;
761 }
762 }
763 if res == Res::Error || (res == Res::Ok && found.is_none()) {
764 break;
765 }
766 }
767 Ok(res)
768 }
769
770 fn index(&mut self, chain: &[PathItem], jb: &Item) -> Result<Option<i32>> {
772 let mut found = Vec::new();
773 if self.item(chain, jb, Some(&mut found))? == Res::Error {
774 return Ok(None);
775 }
776 let [Item::Numeric(n)] = found.as_slice() else {
777 return self.fail_value(Error::new(
778 SqlState::INVALID_SQL_JSON_SUBSCRIPT,
779 "jsonpath array subscript is not a single numeric value",
780 ));
781 };
782 match n.trunc(0).ok().as_ref().and_then(int4) {
783 Some(index) => Ok(Some(index)),
784 None => self.fail_value(Error::new(
785 SqlState::INVALID_SQL_JSON_SUBSCRIPT,
786 "jsonpath array subscript is out of integer range",
787 )),
788 }
789 }
790
791 fn any(
793 &mut self,
794 walk: &Walk<'_>,
795 doc: &Rc<Doc>,
796 at: usize,
797 mut found: Found<'_>,
798 level: u32,
799 ) -> Result<Res> {
800 let mut res = Res::NotFound;
801 if level > walk.last {
802 return Ok(res);
803 }
804 if self.depth >= MAX_DEPTH {
805 return Err(too_deep());
806 }
807 self.depth += 1;
808 for child in doc.children(at) {
809 let v = doc.get(child)?;
810 let nested = matches!(v, Item::Node(..));
811 if level >= walk.first
813 || (walk.first == JSONPATH_LAST && walk.last == JSONPATH_LAST && !nested)
814 {
815 if !walk.chain.is_empty() {
816 let saved = self.ignore_structural;
817 if walk.ignore_structural {
818 self.ignore_structural = true;
819 }
820 res = self.target(walk.chain, &v, found.as_deref_mut(), walk.unwrap)?;
821 self.ignore_structural = saved;
822 if res == Res::Error || (res == Res::Ok && found.is_none()) {
823 break;
824 }
825 } else if let Some(found) = found.as_deref_mut() {
826 found.push(v);
827 } else {
828 self.depth -= 1;
829 return Ok(Res::Ok);
830 }
831 }
832 if level < walk.last && nested {
833 res = self.any(walk, doc, child, found.as_deref_mut(), level + 1)?;
834 if res == Res::Error || (res == Res::Ok && found.is_none()) {
835 break;
836 }
837 }
838 }
839 self.depth -= 1;
840 Ok(res)
841 }
842
843 fn sequence(
845 &mut self,
846 chain: &[PathItem],
847 jb: &Item,
848 unwrap: bool,
849 found: &mut Vec<Item>,
850 ) -> Result<Res> {
851 if !(unwrap && self.lax) {
852 return self.item(chain, jb, Some(found));
853 }
854 let mut seq = Vec::new();
855 if self.item(chain, jb, Some(&mut seq))? == Res::Error {
856 return Ok(Res::Error);
857 }
858 for item in seq {
859 match item.array() {
860 Some((doc, at, _)) => {
861 self.any(&Walk::one(&[], false), doc, at, Some(found), 1)?;
862 }
863 None => found.push(item),
864 }
865 }
866 Ok(Res::Ok)
867 }
868
869 fn truth(&mut self, jsp: &PathItem, jb: &Item) -> Result<Truth> {
871 Ok(match jsp {
872 PathItem::Binary(PathOp::And, l, r) => {
873 let res = self.truth(head(l)?, jb)?;
874 if res == Truth::False {
875 return Ok(Truth::False);
876 }
877 let res2 = self.truth(head(r)?, jb)?;
879 if res2 == Truth::True { res } else { res2 }
880 }
881 PathItem::Binary(PathOp::Or, l, r) => {
882 let res = self.truth(head(l)?, jb)?;
883 if res == Truth::True {
884 return Ok(Truth::True);
885 }
886 let res2 = self.truth(head(r)?, jb)?;
887 if res2 == Truth::False { res } else { res2 }
888 }
889 PathItem::Unary(PathOp::Not, arg) => match self.truth(head(arg)?, jb)? {
890 Truth::Unknown => Truth::Unknown,
891 Truth::True => Truth::False,
892 Truth::False => Truth::True,
893 },
894 PathItem::Unary(PathOp::IsUnknown, arg) => {
895 Truth::of(self.truth(head(arg)?, jb)? == Truth::Unknown)
896 }
897 PathItem::Binary(
898 op @ (PathOp::Equal
899 | PathOp::NotEqual
900 | PathOp::Less
901 | PathOp::Greater
902 | PathOp::LessOrEqual
903 | PathOp::GreaterOrEqual),
904 l,
905 r,
906 ) => {
907 let (op, call, use_tz) = (*op, self.call, self.use_tz);
908 let mut test = |a: &Item, b: Option<&Item>| match b {
909 Some(b) => compare_items(call, use_tz, op, a, b),
910 None => Ok(Truth::Unknown),
911 };
912 self.predicate(l, Some(r), jb, true, &mut test)?
913 }
914 PathItem::Binary(PathOp::StartsWith, l, r) => {
915 let mut test = |whole: &Item, initial: Option<&Item>| {
916 Ok(match (whole, initial) {
917 (Item::String(whole), Some(Item::String(initial))) => {
918 Truth::of(whole.as_bytes().starts_with(initial.as_bytes()))
919 }
920 _ => Truth::Unknown,
921 })
922 };
923 self.predicate(l, Some(r), jb, false, &mut test)?
924 }
925 PathItem::LikeRegex { expr, pattern, flags } => {
926 let cflags = jsonpath_regex_flags(*flags).map_err(type_error)?;
927 let mut regex = None;
928 let mut test = |s: &Item, _: Option<&Item>| {
929 let Item::String(s) = s else { return Ok(Truth::Unknown) };
930 if regex.is_none() {
931 regex = Some(crate::regex::compile(pattern, cflags)?);
932 }
933 Ok(Truth::of(regex.as_ref().is_some_and(|r| r.is_match(s))))
934 };
935 self.predicate(expr, None, jb, false, &mut test)?
936 }
937 PathItem::Unary(PathOp::Exists, arg) => {
938 if self.lax {
939 match self.quiet(|x| x.item(arg, jb, None))? {
940 Res::Error => Truth::Unknown,
941 res => Truth::of(res == Res::Ok),
942 }
943 } else {
944 let mut vals = Vec::new();
946 match self.quiet(|x| x.item(arg, jb, Some(&mut vals)))? {
947 Res::Error => Truth::Unknown,
948 _ => Truth::of(!vals.is_empty()),
949 }
950 }
951 }
952 _ => return Err(Error::internal("invalid boolean jsonpath item type")),
953 })
954 }
955
956 fn nested_truth(&mut self, cond: &[PathItem], jb: &Item) -> Result<Truth> {
958 let prev = std::mem::replace(&mut self.current, jb.clone());
959 let res = self.truth(head(cond)?, jb);
960 self.current = prev;
961 res
962 }
963
964 fn predicate(
966 &mut self,
967 l: &[PathItem],
968 r: Option<&[PathItem]>,
969 jb: &Item,
970 unwrap_right: bool,
971 test: Test<'_>,
972 ) -> Result<Truth> {
973 let mut lseq = Vec::new();
974 if self.quiet(|x| x.sequence(l, jb, true, &mut lseq))? == Res::Error {
975 return Ok(Truth::Unknown);
976 }
977 let mut rseq = Vec::new();
978 if let Some(r) = r
979 && self.quiet(|x| x.sequence(r, jb, unwrap_right, &mut rseq))? == Res::Error
980 {
981 return Ok(Truth::Unknown);
982 }
983 let (mut error, mut found) = (false, false);
984 for lval in &lseq {
985 let mut check = |rval: Option<&Item>| -> Result<bool> {
986 match test(lval, rval)? {
987 Truth::Unknown => {
988 error = true;
989 Ok(!self.lax)
990 }
991 Truth::True => {
992 found = true;
993 Ok(self.lax)
994 }
995 Truth::False => Ok(false),
996 }
997 };
998 let stop = if r.is_some() {
999 let mut stop = false;
1000 for rval in &rseq {
1001 if check(Some(rval))? {
1002 stop = true;
1003 break;
1004 }
1005 }
1006 stop
1007 } else {
1008 check(None)?
1009 };
1010 if stop {
1011 if error && !self.lax {
1013 return Ok(Truth::Unknown);
1014 }
1015 break;
1016 }
1017 }
1018 Ok(if found {
1019 Truth::True
1020 } else if error {
1021 Truth::Unknown
1022 } else {
1023 Truth::False
1024 })
1025 }
1026
1027 fn append_bool(&mut self, rest: &[PathItem], found: Found<'_>, st: Truth) -> Result<Res> {
1029 if rest.is_empty() && found.is_none() {
1030 return Ok(Res::Ok);
1031 }
1032 let v = match st {
1033 Truth::Unknown => Item::Null,
1034 st => Item::Bool(st == Truth::True),
1035 };
1036 self.next(rest, v, found)
1037 }
1038
1039 fn binary_arith(
1041 &mut self,
1042 op: PathOp,
1043 l: &[PathItem],
1044 r: &[PathItem],
1045 rest: &[PathItem],
1046 jb: &Item,
1047 found: Found<'_>,
1048 ) -> Result<Res> {
1049 let (mut lseq, mut rseq) = (Vec::new(), Vec::new());
1050 if self.sequence(l, jb, true, &mut lseq)? == Res::Error
1051 || self.sequence(r, jb, true, &mut rseq)? == Res::Error
1052 {
1053 return Ok(Res::Error);
1054 }
1055 let single = |side: &str| {
1056 Error::new(
1057 SqlState::SINGLETON_SQL_JSON_ITEM_REQUIRED,
1058 format!(
1059 "{side} operand of jsonpath operator {} is not a single numeric value",
1060 op.name()
1061 ),
1062 )
1063 };
1064 let [Item::Numeric(a)] = lseq.as_slice() else { return self.fail(single("left")) };
1065 let [Item::Numeric(b)] = rseq.as_slice() else { return self.fail(single("right")) };
1066 let v = match op {
1067 PathOp::Add => a.add(b),
1068 PathOp::Sub => a.sub(b),
1069 PathOp::Mul => a.mul(b),
1070 PathOp::Div => a.div(b),
1071 _ => a.modulo(b),
1072 };
1073 let v = match v {
1074 Ok(v) => v,
1075 Err(error) => return self.fail(type_error(error)),
1076 };
1077 if rest.is_empty() && found.is_none() {
1078 return Ok(Res::Ok);
1079 }
1080 self.next(rest, Item::Numeric(v), found)
1081 }
1082
1083 fn unary_arith(
1085 &mut self,
1086 op: PathOp,
1087 arg: &[PathItem],
1088 rest: &[PathItem],
1089 jb: &Item,
1090 mut found: Found<'_>,
1091 ) -> Result<Res> {
1092 let mut seq = Vec::new();
1093 if self.sequence(arg, jb, true, &mut seq)? == Res::Error {
1094 return Ok(Res::Error);
1095 }
1096 let mut res = Res::NotFound;
1097 let has_next = !rest.is_empty();
1098 for val in seq {
1099 let Item::Numeric(n) = val else {
1100 if found.is_none() && !has_next {
1101 continue;
1102 }
1103 return self.fail(Error::new(
1104 SqlState::SQL_JSON_NUMBER_NOT_FOUND,
1105 format!(
1106 "operand of unary jsonpath operator {} is not a numeric value",
1107 op.name()
1108 ),
1109 ));
1110 };
1111 if found.is_none() && !has_next {
1112 return Ok(Res::Ok);
1113 }
1114 let n = if op == PathOp::Minus { n.negate() } else { n };
1115 match self.next(rest, Item::Numeric(n), found.as_deref_mut())? {
1116 Res::Error => return Ok(Res::Error),
1117 Res::Ok if found.is_none() => return Ok(Res::Ok),
1118 Res::Ok => res = Res::Ok,
1119 Res::NotFound => {}
1120 }
1121 }
1122 Ok(res)
1123 }
1124
1125 fn method(
1127 &mut self,
1128 op: PathOp,
1129 args: &[PathItem],
1130 chain: &[PathItem],
1131 jb: &Item,
1132 found: Found<'_>,
1133 unwrap: bool,
1134 ) -> Result<Res> {
1135 let rest = &chain[1..];
1136 match op {
1137 PathOp::Type => self.next(rest, Item::String(jb.type_name().to_string()), found),
1138 PathOp::Size => {
1139 let size = match jb.array() {
1140 Some((.., size)) => size,
1141 None if !self.lax => {
1142 if !self.ignore_structural {
1143 return self.method_fail(
1144 SqlState::SQL_JSON_ARRAY_NOT_FOUND,
1145 op,
1146 "an array",
1147 );
1148 }
1149 return Ok(Res::NotFound);
1150 }
1151 None => 1,
1152 };
1153 let size = i128::try_from(size).unwrap_or(i128::MAX);
1154 self.next(rest, Item::Numeric(Numeric::from_integer(size)), found)
1155 }
1156 _ if unwrap && jb.is_array() => self.unwrap_target(chain, jb, found),
1157 PathOp::Abs | PathOp::Floor | PathOp::Ceiling => {
1158 let Item::Numeric(n) = jb else {
1159 return self.method_fail(
1160 SqlState::NON_NUMERIC_SQL_JSON_ITEM,
1161 op,
1162 "a numeric value",
1163 );
1164 };
1165 let v = match op {
1166 PathOp::Abs => n.abs(),
1167 PathOp::Floor => n.whole(false).map_err(type_error)?,
1168 _ => n.whole(true).map_err(type_error)?,
1169 };
1170 if rest.is_empty() && found.is_none() {
1171 return Ok(Res::Ok);
1172 }
1173 self.next(rest, Item::Numeric(v), found)
1174 }
1175 PathOp::Double => {
1176 let not_finite = || {
1177 Error::new(
1178 SqlState::NON_NUMERIC_SQL_JSON_ITEM,
1179 format!(
1180 "NaN or Infinity is not allowed for jsonpath item method .{}()",
1181 op.name()
1182 ),
1183 )
1184 };
1185 let sqltype = "double precision";
1186 let v = match jb {
1187 Item::Numeric(n) => {
1188 let text = numeric_text(n);
1189 match float8_in(&text) {
1190 Err(_) => return self.invalid_argument(&text, op, sqltype),
1191 Ok(f) if !f.is_finite() => return self.fail(not_finite()),
1192 Ok(_) => jb.clone(),
1193 }
1194 }
1195 Item::String(s) => match float8_in(s) {
1196 Err(_) => return self.invalid_argument(s, op, sqltype),
1197 Ok(f) if !f.is_finite() => return self.fail(not_finite()),
1198 Ok(f) => Item::Numeric(Numeric::from_f64(f)),
1199 },
1200 _ => {
1201 return self.method_fail(
1202 SqlState::NON_NUMERIC_SQL_JSON_ITEM,
1203 op,
1204 "a string or numeric value",
1205 );
1206 }
1207 };
1208 self.next(rest, v, found)
1209 }
1210 PathOp::Bigint | PathOp::Integer => {
1211 let sqltype = if op == PathOp::Bigint { "bigint" } else { "integer" };
1212 let v = match jb {
1213 Item::Numeric(n) => {
1214 let v = n.to_integer(sqltype).ok().filter(|&v| match op {
1215 PathOp::Bigint => i64::try_from(v).is_ok(),
1216 _ => i32::try_from(v).is_ok(),
1217 });
1218 match v {
1219 Some(v) => v,
1220 None => return self.invalid_argument(&numeric_text(n), op, sqltype),
1221 }
1222 }
1223 Item::String(s) => {
1224 let v = match op {
1225 PathOp::Bigint => int8_in(s).map(i128::from),
1226 _ => int4_in(s).map(i128::from),
1227 };
1228 match v {
1229 Ok(v) => v,
1230 Err(_) => return self.invalid_argument(s, op, sqltype),
1231 }
1232 }
1233 _ => {
1234 return self.method_fail(
1235 SqlState::NON_NUMERIC_SQL_JSON_ITEM,
1236 op,
1237 "a string or numeric value",
1238 );
1239 }
1240 };
1241 self.next(rest, Item::Numeric(Numeric::from_integer(v)), found)
1242 }
1243 PathOp::Boolean => {
1244 let v = match jb {
1245 Item::Bool(b) => *b,
1246 Item::Numeric(n) => {
1247 let text = numeric_text(n);
1248 match int4_in(&text) {
1249 Ok(v) => v != 0,
1250 Err(_) => return self.invalid_argument(&text, op, "boolean"),
1251 }
1252 }
1253 Item::String(s) => match parse_bool(s) {
1254 Some(b) => b,
1255 None => return self.invalid_argument(s, op, "boolean"),
1256 },
1257 _ => {
1258 return self.method_fail(
1259 SqlState::NON_NUMERIC_SQL_JSON_ITEM,
1260 op,
1261 "a boolean, string, or numeric value",
1262 );
1263 }
1264 };
1265 self.next(rest, Item::Bool(v), found)
1266 }
1267 PathOp::Decimal | PathOp::Number => self.number(op, args, rest, jb, found),
1268 PathOp::StringFunc => {
1269 let v = match jb {
1270 Item::String(s) => s.clone(),
1271 Item::Numeric(n) => numeric_text(n),
1272 Item::Bool(b) => b.to_string(),
1273 Item::Datetime(_, text) => text.clone(),
1274 _ => {
1275 return self.method_fail(
1276 SqlState::NON_NUMERIC_SQL_JSON_ITEM,
1277 op,
1278 "a boolean, string, numeric, or datetime value",
1279 );
1280 }
1281 };
1282 self.next(rest, Item::String(v), found)
1283 }
1284 PathOp::Replace
1285 | PathOp::Lower
1286 | PathOp::Upper
1287 | PathOp::Ltrim
1288 | PathOp::Rtrim
1289 | PathOp::Btrim
1290 | PathOp::Initcap
1291 | PathOp::SplitPart => self.string_method(op, args, rest, jb, found),
1292 PathOp::KeyValue => self.keyvalue(rest, jb, found),
1293 PathOp::Datetime
1294 | PathOp::Date
1295 | PathOp::Time
1296 | PathOp::TimeTz
1297 | PathOp::Timestamp
1298 | PathOp::TimestampTz => self.datetime_method(op, args, rest, jb, found),
1299 _ => Err(Error::internal("unrecognized jsonpath item type")),
1300 }
1301 }
1302
1303 fn number(
1305 &mut self,
1306 op: PathOp,
1307 args: &[PathItem],
1308 rest: &[PathItem],
1309 jb: &Item,
1310 found: Found<'_>,
1311 ) -> Result<Res> {
1312 let not_finite = || {
1313 Error::new(
1314 SqlState::NON_NUMERIC_SQL_JSON_ITEM,
1315 format!("NaN or Infinity is not allowed for jsonpath item method .{}()", op.name()),
1316 )
1317 };
1318 let (num, text) = match jb {
1319 Item::Numeric(n) => (n.clone(), None),
1320 Item::String(s) => match numeric_in(s, -1) {
1321 Ok(n) => (n, Some(s.as_str())),
1322 Err(_) => return self.invalid_argument(s, op, "numeric"),
1323 },
1324 _ => {
1325 return self.method_fail(
1326 SqlState::NON_NUMERIC_SQL_JSON_ITEM,
1327 op,
1328 "a string or numeric value",
1329 );
1330 }
1331 };
1332 if is_special(&num) {
1333 return self.fail(not_finite());
1334 }
1335 let num = match (op, args.first()) {
1336 (PathOp::Decimal, Some(precision)) => {
1337 let out_of_range = |what: &str| {
1338 Error::new(
1339 SqlState::NON_NUMERIC_SQL_JSON_ITEM,
1340 format!(
1341 "{what} of jsonpath item method .{}() is out of range for type integer",
1342 op.name()
1343 ),
1344 )
1345 };
1346 let PathItem::Numeric(precision) = precision else {
1347 return Err(Error::internal(
1348 "invalid jsonpath item type for .decimal() precision",
1349 ));
1350 };
1351 let Some(precision) = int4(precision) else {
1352 return self.fail(out_of_range("precision"));
1353 };
1354 let scale = match args.get(1) {
1355 None => 0,
1356 Some(PathItem::Numeric(scale)) => match int4(scale) {
1357 Some(scale) => scale,
1358 None => return self.fail(out_of_range("scale")),
1359 },
1360 Some(_) => {
1361 return Err(Error::internal(
1362 "invalid jsonpath item type for .decimal() scale",
1363 ));
1364 }
1365 };
1366 if !(1..=1000).contains(&precision) {
1367 return self.fail(Error::new(
1368 SqlState::INVALID_PARAMETER_VALUE,
1369 format!("NUMERIC precision {precision} must be between 1 and 1000"),
1370 ));
1371 }
1372 if !(-1000..=1000).contains(&scale) {
1373 return self.fail(Error::new(
1374 SqlState::INVALID_PARAMETER_VALUE,
1375 format!("NUMERIC scale {scale} must be between -1000 and 1000"),
1376 ));
1377 }
1378 let typmod = ((precision << 16) | (scale & 0x7ff)) + 4;
1379 let text = text.map_or_else(|| numeric_text(&num), str::to_string);
1380 match numeric_in(&text, typmod) {
1381 Ok(n) => n,
1382 Err(_) => return self.invalid_argument(&text, op, "numeric"),
1383 }
1384 }
1385 _ => num,
1386 };
1387 self.next(rest, Item::Numeric(num), found)
1388 }
1389
1390 fn string_method(
1392 &mut self,
1393 op: PathOp,
1394 args: &[PathItem],
1395 rest: &[PathItem],
1396 jb: &Item,
1397 found: Found<'_>,
1398 ) -> Result<Res> {
1399 let Item::String(s) = jb else {
1400 return self.method_fail(SqlState::INVALID_PARAMETER_VALUE, op, "a string");
1401 };
1402 let string = |at: usize| match args.get(at) {
1403 Some(PathItem::String(s)) => Ok(Value::text(s.as_str())),
1404 _ => Err(Error::internal(format!(
1405 "invalid jsonpath item type for .{}() argument",
1406 op.name()
1407 ))),
1408 };
1409 let mut call_args = vec![Value::text(s.as_str())];
1410 let src = match op {
1411 PathOp::Replace => {
1412 call_args.extend([string(0)?, string(1)?]);
1413 "replace_text"
1414 }
1415 PathOp::Lower => "lower",
1416 PathOp::Upper => "upper",
1417 PathOp::Initcap => "initcap",
1418 PathOp::Ltrim | PathOp::Rtrim | PathOp::Btrim => {
1419 if !args.is_empty() {
1420 call_args.push(string(0)?);
1421 }
1422 match op {
1423 PathOp::Ltrim => "ltrim",
1424 PathOp::Rtrim => "rtrim",
1425 _ => "btrim",
1426 }
1427 }
1428 _ => {
1429 let position = |what: &str| {
1430 Error::new(
1431 SqlState::INVALID_PARAMETER_VALUE,
1432 format!("field position of jsonpath item method .{}() {what}", op.name()),
1433 )
1434 };
1435 call_args.push(string(0)?);
1436 let Some(PathItem::Numeric(n)) = args.get(1) else {
1437 return Err(Error::internal("invalid jsonpath item type for .split_part()"));
1438 };
1439 match int4(n) {
1440 None => return self.fail(position("is out of range for type integer")),
1441 Some(0) => return self.fail(position("must not be zero")),
1442 Some(n) => call_args.push(Value::Int4(n)),
1443 }
1444 "split_part"
1445 }
1446 };
1447 let kernel = crate::text::by_src(src).ok_or_else(bad_value)?;
1448 let v = kernel(self.call, &call_args)?;
1449 let v = v.as_str().ok_or_else(bad_value)?.to_string();
1450 if rest.is_empty() && found.is_none() {
1451 return Ok(Res::Ok);
1452 }
1453 self.next(rest, Item::String(v), found)
1454 }
1455
1456 #[allow(clippy::too_many_lines)]
1458 fn datetime_method(
1459 &mut self,
1460 op: PathOp,
1461 args: &[PathItem],
1462 rest: &[PathItem],
1463 jb: &Item,
1464 found: Found<'_>,
1465 ) -> Result<Res> {
1466 use Value as V;
1467 let invalid = SqlState::INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION;
1468 let Item::String(s) = jb else { return self.method_fail(invalid, op, "a string") };
1469 let not_recognized =
1470 |name: &str| Error::new(invalid, format!("{name} format is not recognized: \"{s}\""));
1471 let mut precision = None;
1472 let (mut value, mut tz) = match (op, args.first()) {
1473 (PathOp::Datetime, Some(template)) => {
1474 let PathItem::String(template) = template else {
1475 return Err(Error::internal(
1476 "invalid jsonpath item type for .datetime() argument",
1477 ));
1478 };
1479 match parse_datetime(self.call, s, template)? {
1480 Ok(v) => v,
1481 Err(error) => return self.fail(error),
1482 }
1483 }
1484 _ => {
1485 if op != PathOp::Datetime
1486 && op != PathOp::Date
1487 && let Some(arg) = args.first()
1488 {
1489 let PathItem::Numeric(n) = arg else {
1490 return Err(Error::internal(format!(
1491 "invalid jsonpath item type for {} argument",
1492 op.name()
1493 )));
1494 };
1495 let Some(n) = int4(n) else {
1496 return self.fail(Error::new(
1497 invalid,
1498 format!(
1499 "time precision of jsonpath item method .{}() is out of range for type integer",
1500 op.name()
1501 ),
1502 ));
1503 };
1504 precision = Some(n);
1505 }
1506 let iso = ISO_FORMATS.iter().find_map(|format| {
1507 parse_datetime(self.call, s, format).ok().and_then(Result::ok)
1508 });
1509 match iso {
1510 Some(v) => v,
1511 None if op == PathOp::Datetime => {
1512 return self.fail(not_recognized("datetime").with_hint(
1513 "Use a datetime template argument to specify the input data format.",
1514 ));
1515 }
1516 None => return self.fail(not_recognized(op.name())),
1517 }
1518 }
1519 };
1520 let call = self.call;
1521 let use_tz = self.use_tz;
1522 value = match (op, &value) {
1523 (PathOp::Datetime, _)
1524 | (PathOp::Date, V::Date(_))
1525 | (PathOp::Time, V::Time(_))
1526 | (PathOp::TimeTz, V::TimeTz(..))
1527 | (PathOp::Timestamp, V::Timestamp(_))
1528 | (PathOp::TimestampTz, V::TimestampTz(_)) => value,
1529 (PathOp::Date, V::Timestamp(_)) => convert(call, "timestamp_date", &value)?,
1530 (PathOp::Date, V::TimestampTz(_)) => {
1531 need_tz(use_tz, "timestamptz", "date")?;
1532 convert(call, "timestamptz_date", &value)?
1533 }
1534 (PathOp::Time, V::TimeTz(..)) => {
1535 need_tz(use_tz, "timetz", "time")?;
1536 convert(call, "timetz_time", &value)?
1537 }
1538 (PathOp::Time, V::Timestamp(_)) => convert(call, "timestamp_time", &value)?,
1539 (PathOp::Time, V::TimestampTz(_)) => {
1540 need_tz(use_tz, "timestamptz", "time")?;
1541 convert(call, "timestamptz_time", &value)?
1542 }
1543 (PathOp::TimeTz, V::Time(_)) => {
1544 need_tz(use_tz, "time", "timetz")?;
1545 convert(call, "time_timetz", &value)?
1546 }
1547 (PathOp::TimeTz, V::TimestampTz(_)) => convert(call, "timestamptz_timetz", &value)?,
1548 (PathOp::Timestamp, V::Date(_)) => convert(call, "date_timestamp", &value)?,
1549 (PathOp::Timestamp, V::TimestampTz(_)) => {
1550 need_tz(use_tz, "timestamptz", "timestamp")?;
1551 convert(call, "timestamptz_timestamp", &value)?
1552 }
1553 (PathOp::TimestampTz, V::Date(date)) => {
1554 need_tz(use_tz, "date", "timestamptz")?;
1555 tz = session_west(call, midnight(*date)?)?;
1556 convert(call, "date_timestamptz", &value)?
1557 }
1558 (PathOp::TimestampTz, V::Timestamp(ts)) => {
1559 need_tz(use_tz, "timestamp", "timestamptz")?;
1560 tz = session_west(call, *ts)?;
1561 convert(call, "timestamp_timestamptz", &value)?
1562 }
1563 _ => return self.fail(not_recognized(op.name())),
1564 };
1565 if let Some(precision) = precision {
1566 let precision = typmod_check(call, op, precision)?;
1567 value = match value {
1568 V::Time(time) => V::Time(adjust_time(time, precision)),
1569 V::TimeTz(time, zone) => V::TimeTz(adjust_time(time, precision), zone),
1570 V::Timestamp(ts) => {
1571 V::Timestamp(adjust_timestamp(ts, precision).map_err(type_error)?)
1572 }
1573 V::TimestampTz(ts) => {
1574 V::TimestampTz(adjust_timestamp(ts, precision).map_err(type_error)?)
1575 }
1576 v => v,
1577 };
1578 }
1579 if rest.is_empty() && found.is_none() {
1580 return Ok(Res::Ok);
1581 }
1582 let text = datetime_text(&value, tz)?;
1583 self.next(rest, Item::Datetime(value, text), found)
1584 }
1585
1586 fn keyvalue(&mut self, rest: &[PathItem], jb: &Item, mut found: Found<'_>) -> Result<Res> {
1588 let Some((doc, at)) = jb.object() else {
1589 return self.method_fail(
1590 SqlState::SQL_JSON_OBJECT_NOT_FOUND,
1591 PathOp::KeyValue,
1592 "an object",
1593 );
1594 };
1595 let JsonItem::Object(fields) = doc.item(at) else { return Err(bad_value()) };
1596 let mut offset = doc.offset(at);
1597 if let Some((base, base_at)) = &self.base.node
1598 && Rc::ptr_eq(base, doc)
1599 {
1600 offset -= doc.offset(*base_at);
1601 }
1602 let id = offset + self.base.id * BASE_ID_FACTOR;
1603 let mut res = Res::NotFound;
1604 for (key, value) in fields {
1605 res = Res::Ok;
1606 if rest.is_empty() && found.is_none() {
1607 break;
1608 }
1609 let mut text = format!("{{\"id\": {id}, \"key\": ");
1610 json_escape(key, &mut text);
1611 text.push_str(", \"value\": ");
1612 text.push_str(doc.text(*value));
1613 text.push('}');
1614 let obj = Doc::parse(text)?;
1615 let base = Base { node: Some((Rc::clone(&obj), 0)), id: self.last_id };
1616 self.last_id += 1;
1617 let base = std::mem::replace(&mut self.base, base);
1618 let next = self.next(rest, Item::Node(obj, 0), found.as_deref_mut());
1619 self.base = base;
1620 res = next?;
1621 if res == Res::Error {
1622 return Ok(res);
1623 }
1624 if res == Res::Ok && found.is_none() {
1625 break;
1626 }
1627 }
1628 Ok(res)
1629 }
1630}
1631
1632fn need_tz(use_tz: bool, from: &str, to: &str) -> Result<()> {
1634 if use_tz {
1635 return Ok(());
1636 }
1637 Err(Error::new(
1638 SqlState::FEATURE_NOT_SUPPORTED,
1639 format!("cannot convert value from {from} to {to} without time zone usage"),
1640 )
1641 .with_hint("Use *_tz() function for time zone support."))
1642}
1643
1644fn convert(call: &Call<'_>, src: &str, value: &Value) -> Result<Value> {
1646 let kernel =
1647 crate::cast::by_src(src).or_else(|| crate::calendar::by_src(src)).ok_or_else(bad_value)?;
1648 kernel(call, std::slice::from_ref(value))
1649}
1650
1651fn session_west(call: &Call<'_>, local: i64) -> Result<i32> {
1653 let zone = call.session.zone()?;
1654 let seconds = local.div_euclid(USECS_PER_SEC) - UNIX_TO_POSTGRES_USECS / USECS_PER_SEC;
1655 Ok(local_offset(seconds, zone.as_ref()).0)
1656}
1657
1658fn typmod_check(call: &Call<'_>, op: PathOp, precision: i32) -> Result<i32> {
1660 let (kind, zone) = match op {
1661 PathOp::Time => ("TIME", ""),
1662 PathOp::TimeTz => ("TIME", " WITH TIME ZONE"),
1663 PathOp::Timestamp => ("TIMESTAMP", ""),
1664 _ => ("TIMESTAMP", " WITH TIME ZONE"),
1665 };
1666 if precision < 0 {
1667 return Err(Error::new(
1668 SqlState::INVALID_PARAMETER_VALUE,
1669 format!("{kind}({precision}){zone} precision must not be negative"),
1670 ));
1671 }
1672 if precision > 6 {
1673 call.session.warn(Error::new(
1674 SqlState::INVALID_PARAMETER_VALUE,
1675 format!("{kind}({precision}){zone} precision reduced to maximum allowed, 6"),
1676 ));
1677 return Ok(6);
1678 }
1679 Ok(precision)
1680}
1681
1682fn datetime_text(value: &Value, tz: i32) -> Result<String> {
1684 const XSD: DateFormat = DateFormat { style: DateStyle::Xsd, order: DateOrder::Mdy };
1685 let mut out = Vec::new();
1686 match *value {
1687 Value::Date(date) => date_out(date, XSD, &mut out),
1688 Value::Time(time) => time_out(time, &mut out),
1689 Value::TimeTz(time, zone) => {
1690 time_out(time, &mut out);
1691 let total = zone.unsigned_abs();
1693 let sign = if zone <= 0 { '+' } else { '-' };
1694 write!(out, "{sign}{:02}:{:02}", total / 3600, total / 60 % 60)
1695 .map_err(|_| bad_value())?;
1696 if total % 60 != 0 {
1697 write!(out, ":{:02}", total % 60).map_err(|_| bad_value())?;
1698 }
1699 }
1700 Value::Timestamp(ts) => timestamp_out(ts, XSD, &mut out).map_err(type_error)?,
1701 Value::TimestampTz(ts) => {
1702 let zone = FixedZone { offset: -tz, abbrev: String::new() };
1703 timestamptz_out(ts, XSD, &zone, &mut out).map_err(type_error)?;
1704 }
1705 _ => return Err(bad_value()),
1706 }
1707 String::from_utf8(out).map_err(|_| bad_value())
1708}
1709
1710fn compare_datetime(
1712 call: &Call<'_>,
1713 use_tz: bool,
1714 a: &Value,
1715 b: &Value,
1716) -> Result<Option<Ordering>> {
1717 use Value as V;
1718 let timetz = |v: &Value| {
1719 need_tz(use_tz, "time", "timetz")?;
1720 convert(call, "time_timetz", v)
1721 };
1722 Ok(Some(match (a, b) {
1723 (V::Time(_), V::TimeTz(..)) => compare(0, &timetz(a)?, 0, b)?,
1724 (V::TimeTz(..), V::Time(_)) => compare(0, a, 0, &timetz(b)?)?,
1725 (V::Time(_), V::Time(_)) | (V::TimeTz(..), V::TimeTz(..)) => compare(0, a, 0, b)?,
1726 (V::Time(_) | V::TimeTz(..), _) | (_, V::Time(_) | V::TimeTz(..)) => return Ok(None),
1727 (V::Date(_), V::TimestampTz(_)) | (V::TimestampTz(_), V::Date(_)) => {
1728 need_tz(use_tz, "date", "timestamptz")?;
1729 datetime_order(call, a, b)?
1730 }
1731 (V::Timestamp(_), V::TimestampTz(_)) | (V::TimestampTz(_), V::Timestamp(_)) => {
1732 need_tz(use_tz, "timestamp", "timestamptz")?;
1733 datetime_order(call, a, b)?
1734 }
1735 _ => datetime_order(call, a, b)?,
1736 }))
1737}
1738
1739fn parse_bool(s: &str) -> Option<bool> {
1741 let space = |c: char| c.is_ascii_whitespace() || c == '\u{b}';
1742 if s.starts_with(space) || s.ends_with(space) {
1743 return None;
1744 }
1745 bool_in(s).ok()
1746}
1747
1748fn compare_items(call: &Call<'_>, use_tz: bool, op: PathOp, a: &Item, b: &Item) -> Result<Truth> {
1750 let order = match (a, b) {
1751 (Item::Null, Item::Null) => Ordering::Equal,
1752 (Item::Null, _) | (_, Item::Null) => return Ok(Truth::of(op == PathOp::NotEqual)),
1753 (Item::Bool(x), Item::Bool(y)) => x.cmp(y),
1754 (Item::Numeric(x), Item::Numeric(y)) => x.compare(y),
1755 (Item::String(x), Item::String(y)) => x.as_bytes().cmp(y.as_bytes()),
1756 (Item::Datetime(x, _), Item::Datetime(y, _)) => {
1757 match compare_datetime(call, use_tz, x, y)? {
1758 Some(order) => order,
1759 None => return Ok(Truth::Unknown),
1760 }
1761 }
1762 _ => return Ok(Truth::Unknown),
1763 };
1764 Ok(Truth::of(match op {
1765 PathOp::Equal => order.is_eq(),
1766 PathOp::NotEqual => order.is_ne(),
1767 PathOp::Less => order.is_lt(),
1768 PathOp::Greater => order.is_gt(),
1769 PathOp::LessOrEqual => order.is_le(),
1770 _ => order.is_ge(),
1771 }))
1772}
1773
1774fn execute(
1776 call: &Call<'_>,
1777 args: &[Value],
1778 want: bool,
1779 use_tz: bool,
1780) -> Result<(Res, Vec<Item>, bool)> {
1781 let target = Doc::parse(text(args, 0)?.to_string())?;
1782 let Some(Value::JsonPath(path)) = args.get(1) else { return Err(bad_value()) };
1783 let (vars, silent) = match args {
1784 [_, _] => (None, true),
1785 [_, _, vars, Value::Bool(silent)] => {
1786 (Some(Doc::parse(vars.as_str().ok_or_else(bad_value)?.to_string())?), *silent)
1787 }
1788 _ => return Err(bad_value()),
1789 };
1790 if let Some(vars) = &vars
1791 && !matches!(vars.item(0), JsonItem::Object(_))
1792 {
1793 return Err(Error::new(
1794 SqlState::INVALID_PARAMETER_VALUE,
1795 "\"vars\" argument is not an object",
1796 )
1797 .with_detail(
1798 "Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.",
1799 ));
1800 }
1801 let last_id = 1 + i64::from(vars.is_some());
1802 let vars = vars.map_or(Vars::None, Vars::Object);
1803 let (res, items) = run(call, &target, path, vars, last_id, !silent, want, use_tz)?;
1804 Ok((res, items, silent))
1805}
1806
1807#[allow(clippy::too_many_arguments)]
1809fn run(
1810 call: &Call<'_>,
1811 target: &Rc<Doc>,
1812 path: &JsonPath,
1813 vars: Vars<'_>,
1814 last_id: i64,
1815 throw: bool,
1816 want: bool,
1817 use_tz: bool,
1818) -> Result<(Res, Vec<Item>)> {
1819 let root = target.get(0)?;
1820 let mut exec = Exec {
1821 call,
1822 last_id,
1823 vars,
1824 lax: path.lax,
1825 ignore_structural: path.lax,
1826 throw,
1827 root: root.clone(),
1828 current: root.clone(),
1829 base: Base { node: None, id: 0 },
1830 innermost: None,
1831 depth: 0,
1832 use_tz,
1833 };
1834 let mut items = Vec::new();
1835 let res = if !want && !path.lax {
1836 match exec.item(&path.expr, &root, Some(&mut items))? {
1838 Res::Error => Res::Error,
1839 _ if items.is_empty() => Res::NotFound,
1840 _ => Res::Ok,
1841 }
1842 } else {
1843 exec.item(&path.expr, &root, want.then_some(&mut items))?
1844 };
1845 Ok((res, items))
1846}
1847
1848pub fn sql_json_path(
1854 call: &Call<'_>,
1855 target: &Value,
1856 path: &Value,
1857 vars: &[PathVar],
1858 throw: bool,
1859 want: bool,
1860) -> Result<Option<(bool, Vec<SqlJsonItem>)>> {
1861 let Value::Text(target) = target else { return Err(bad_value()) };
1862 let Value::JsonPath(path) = path else { return Err(bad_value()) };
1863 let target = Doc::parse(target.clone())?;
1864 let last_id = 1 + i64::try_from(vars.len()).unwrap_or(i64::MAX);
1865 let (res, items) = run(call, &target, path, Vars::Passing(vars), last_id, throw, want, true)?;
1866 if res == Res::Error {
1867 return Ok(None);
1868 }
1869 let items = items
1870 .into_iter()
1871 .map(|item| match item {
1872 Item::Null => SqlJsonItem::Null,
1873 Item::Bool(b) => SqlJsonItem::Bool(b),
1874 Item::Numeric(n) => SqlJsonItem::Numeric(n),
1875 Item::String(s) => SqlJsonItem::String(s),
1876 Item::Datetime(value, text) => SqlJsonItem::Datetime(value, text),
1877 Item::Node(doc, at) => SqlJsonItem::Container(doc.text(at).to_owned()),
1878 })
1879 .collect();
1880 Ok(Some((res == Res::Ok, items)))
1881}
1882
1883fn exists(call: &Call<'_>, args: &[Value], use_tz: bool) -> Result<Value> {
1885 Ok(match execute(call, args, false, use_tz)?.0 {
1886 Res::Error => Value::Null,
1887 res => Value::Bool(res == Res::Ok),
1888 })
1889}
1890
1891fn path_match(call: &Call<'_>, args: &[Value], use_tz: bool) -> Result<Value> {
1893 let (_, items, silent) = execute(call, args, true, use_tz)?;
1894 match items.as_slice() {
1895 [Item::Bool(b)] => Ok(Value::Bool(*b)),
1896 [Item::Null] => Ok(Value::Null),
1897 _ if !silent => Err(Error::new(
1898 SqlState::SINGLETON_SQL_JSON_ITEM_REQUIRED,
1899 "single boolean result is expected",
1900 )),
1901 _ => Ok(Value::Null),
1902 }
1903}
1904
1905pub(crate) fn jsonb_path_query(call: &Call<'_>, args: &[Value]) -> Result<Vec<Vec<Value>>> {
1907 query(call, args, false)
1908}
1909
1910pub(crate) fn jsonb_path_query_tz(call: &Call<'_>, args: &[Value]) -> Result<Vec<Vec<Value>>> {
1912 query(call, args, true)
1913}
1914
1915fn query(call: &Call<'_>, args: &[Value], use_tz: bool) -> Result<Vec<Vec<Value>>> {
1916 let (_, items, _) = execute(call, args, true, use_tz)?;
1917 Ok(items.iter().map(|item| vec![item.to_value()]).collect())
1918}
1919
1920fn query_array(call: &Call<'_>, args: &[Value], use_tz: bool) -> Result<Value> {
1922 let (_, items, _) = execute(call, args, true, use_tz)?;
1923 let mut out = String::from("[");
1924 for (i, item) in items.iter().enumerate() {
1925 if i > 0 {
1926 out.push_str(", ");
1927 }
1928 item.write(&mut out);
1929 }
1930 out.push(']');
1931 Ok(Value::Text(out))
1932}
1933
1934fn query_first(call: &Call<'_>, args: &[Value], use_tz: bool) -> Result<Value> {
1936 let (_, items, _) = execute(call, args, true, use_tz)?;
1937 Ok(items.first().map_or(Value::Null, Item::to_value))
1938}