1use super::types::*;
7use crate::error::{Error, Result};
8use nom::{
9 branch::alt,
10 bytes::complete::{tag, take_while, take_while1},
11 character::complete::{char, satisfy},
12 combinator::{opt, recognize, value},
13 multi::{many0, separated_list0},
14 sequence::{pair, preceded},
15 IResult,
16};
17use percent_encoding::percent_decode_str;
18
19pub fn parse_query_params(query: &str, is_rpc: bool) -> Result<QueryParams> {
29 let mut params = QueryParams::default();
30
31 if query.is_empty() {
32 return Ok(params);
33 }
34
35 let mut pairs: Vec<(&str, &str)> = query
37 .split('&')
38 .filter_map(|pair| {
39 let mut parts = pair.splitn(2, '=');
40 Some((parts.next()?, parts.next().unwrap_or("")))
41 })
42 .collect();
43 pairs.sort_by_key(|(k, _)| *k);
44 params.canonical = pairs
45 .iter()
46 .map(|(k, v)| format!("{}={}", k, v))
47 .collect::<Vec<_>>()
48 .join("&");
49
50 for (key, value) in pairs {
51 let decoded_value = percent_decode_str(value)
52 .decode_utf8()
53 .map_err(|_| Error::InvalidQueryParam(key.into()))?
54 .to_string();
55
56 let decoded_key = percent_decode_str(key)
60 .decode_utf8()
61 .map_err(|_| Error::InvalidQueryParam(key.into()))?
62 .to_string();
63 let key: &str = &decoded_key;
64
65 if let Some((path, modifier)) = parse_modifier_key(key) {
68 match modifier {
69 Modifier::Order => {
70 let (_, terms) = parse_order_param(&decoded_value)?;
71 params.order.push((path, terms));
72 }
73 Modifier::Limit => {
74 let limit: i64 = decoded_value
75 .parse()
76 .map_err(|_| Error::InvalidQueryParam("limit".into()))?;
77 if limit < 0 {
81 return Err(Error::InvalidRange(
82 "Limit should be greater than or equal to zero.".into(),
83 ));
84 }
85 params.ranges.entry(path.join(".")).or_default().limit = Some(limit);
86 }
87 Modifier::Offset => {
88 let offset: i64 = decoded_value
89 .parse()
90 .map_err(|_| Error::InvalidQueryParam("offset".into()))?;
91 let range = params.ranges.entry(path.join(".")).or_default();
92 range.offset = offset;
93 range.offset_explicit = true;
94 }
95 Modifier::Logic(op, negated) => {
96 check_nesting(&decoded_value)?;
97 params
98 .logic
99 .push((path, parse_logic_param(op, negated, &decoded_value)?));
100 }
101 }
102 continue;
103 }
104
105 match key {
106 "select" => {
107 params.select = parse_select(&decoded_value)?;
108 }
109 "columns" => {
110 params.columns = Some(parse_columns(&decoded_value)?.into_iter().collect());
111 }
112 "on_conflict" => {
113 params.on_conflict = Some(
114 decoded_value
115 .split(',')
116 .map(|s| s.trim().to_string())
117 .collect(),
118 );
119 }
120 key if !key.starts_with('_') => {
121 if is_rpc {
122 params.params.push((key.to_string(), decoded_value.clone()));
123 }
124
125 let parsed = parse_filter_param(key, &decoded_value);
127 let (path, filter) = match parsed {
128 Ok(parsed) => parsed,
129 Err(_) if is_rpc => continue,
132 Err(e) => return Err(e),
133 };
134
135 if path.is_empty() {
136 params.filter_fields.insert(filter.field.name.clone());
137 params.filters_root.push(filter);
138 } else {
139 params.filters.push((path, filter));
140 }
141 }
142 _ => {
143 params.params.push((key.to_string(), decoded_value));
145 }
146 }
147 }
148
149 Ok(params)
150}
151
152fn parse_columns(input: &str) -> Result<Vec<String>> {
165 const EXPECTED: &str = "expecting field name (* or [a..z0..9_$])";
166
167 let mut columns = Vec::new();
168 let mut at = 0usize;
169
170 for (index, field) in input.split(',').enumerate() {
171 if index > 0 {
172 at += 1;
174 }
175 let trimmed = field.trim();
176 if trimmed.is_empty() {
177 return Err(Error::UnparsableQuery {
178 kind: "columns parameter",
179 text: input.to_string(),
180 column: at + 1,
183 expected: match at >= input.len() {
184 true => format!("unexpected end of input {}", EXPECTED),
185 false => format!(
186 "unexpected {:?} {}",
187 input[at..].chars().next().unwrap_or_default(),
188 EXPECTED
189 ),
190 },
191 });
192 }
193 columns.push(trimmed.to_string());
194 at += field.len();
195 }
196
197 Ok(columns)
198}
199
200pub fn parse_select(input: &str) -> Result<Vec<SelectItem>> {
202 if input.is_empty() {
203 return Ok(vec![]);
204 }
205
206 check_nesting(input)?;
207
208 match parse_select_items(input) {
212 Ok(("", items)) => Ok(items),
213 _ => Err(Error::InvalidQueryParam(format!("select={}", input))),
214 }
215}
216
217const MAX_NESTING: usize = 64;
225
226fn check_nesting(input: &str) -> Result<()> {
231 let mut depth = 0usize;
232 let mut quoted = false;
233 let mut escaped = false;
234 for ch in input.chars() {
235 if quoted {
236 match ch {
237 _ if escaped => escaped = false,
238 '\\' => escaped = true,
239 '"' => quoted = false,
240 _ => {}
241 }
242 continue;
243 }
244 match ch {
245 '"' => quoted = true,
246 '(' => {
247 depth += 1;
248 if depth > MAX_NESTING {
249 return Err(Error::InvalidQueryParam(format!(
250 "nested more than {MAX_NESTING} levels deep"
251 )));
252 }
253 }
254 ')' => depth = depth.saturating_sub(1),
255 _ => {}
256 }
257 }
258 Ok(())
259}
260
261fn parse_select_items(input: &str) -> IResult<&str, Vec<SelectItem>> {
262 separated_list0(char(','), parse_select_item)(input)
263}
264
265fn parse_select_item(input: &str) -> IResult<&str, SelectItem> {
266 let (input, _) = nom::character::complete::space0(input)?;
269 alt((
270 parse_star,
271 parse_bare_aggregate,
274 parse_spread_relation,
275 parse_relation_select,
276 parse_field_select,
277 ))(input)
278}
279
280fn parse_star(input: &str) -> IResult<&str, SelectItem> {
286 let (input, _) = char('*')(input)?;
287 Ok((input, SelectItem::field("*")))
288}
289
290fn parse_bare_aggregate(input: &str) -> IResult<&str, SelectItem> {
295 let (input, alias) = opt(parse_alias_prefix)(input)?;
296 let (input, _) = tag("count()")(input)?;
297 let (input, cast) = opt(preceded(tag("::"), parse_cast_type))(input)?;
298
299 Ok((
300 input,
301 SelectItem::Field {
302 field: Field::simple(""),
305 aggregate: Some(AggregateFunction::Count),
306 aggregate_cast: cast.map(|s| s.to_string()),
307 cast: None,
308 alias: alias.map(|s| s.to_string()),
309 },
310 ))
311}
312
313fn parse_aggregate_suffix(input: &str) -> IResult<&str, AggregateFunction> {
315 let (input, _) = char('.')(input)?;
316 let (input, function) = alt((
317 value(AggregateFunction::Sum, tag("sum")),
318 value(AggregateFunction::Avg, tag("avg")),
319 value(AggregateFunction::Max, tag("max")),
320 value(AggregateFunction::Min, tag("min")),
321 value(AggregateFunction::Count, tag("count")),
322 ))(input)?;
323 let (input, _) = tag("()")(input)?;
324 Ok((input, function))
325}
326
327fn parse_spread_relation(input: &str) -> IResult<&str, SelectItem> {
329 let (input, _) = tag("...")(input)?;
330 let (input, relation) = parse_field_name(input)?;
331 let (input, (hint, join_type)) = parse_relation_modifiers(input)?;
332 let (input, _) = char('(')(input)?;
336 let (input, nested) = parse_nested_select(input)?;
337 let (input, _) = char(')')(input)?;
338
339 Ok((
340 input,
341 SelectItem::SpreadRelation {
342 relation,
343 hint,
344 join_type,
345 select: nested,
346 },
347 ))
348}
349
350fn parse_nested_select(input: &str) -> IResult<&str, Vec<SelectItem>> {
356 let mut depth = 0usize;
357 let mut end = input.len();
358
359 for (idx, ch) in input.char_indices() {
360 match ch {
361 '(' => depth += 1,
362 ')' => {
363 if depth == 0 {
364 end = idx;
365 break;
366 }
367 depth -= 1;
368 }
369 _ => {}
370 }
371 }
372
373 let (body, rest) = input.split_at(end);
374
375 if body.is_empty() {
376 return Ok((rest, Vec::new()));
377 }
378
379 match parse_select_items(body) {
380 Ok(("", items)) => Ok((rest, items)),
381 _ => Err(nom::Err::Error(nom::error::Error::new(
382 input,
383 nom::error::ErrorKind::Verify,
384 ))),
385 }
386}
387
388fn parse_relation_select(input: &str) -> IResult<&str, SelectItem> {
390 let (input, alias) = opt(parse_alias_prefix)(input)?;
392 let (input, name) = parse_field_name(input)?;
393 let (input, (hint, join_type)) = parse_relation_modifiers(input)?;
394 let (input, _) = char('(')(input)?;
398 let (input, nested) = parse_nested_select(input)?;
399 let (input, _) = char(')')(input)?;
400
401 Ok((
402 input,
403 SelectItem::Relation {
404 relation: name,
405 alias,
406 hint,
407 join_type,
408 select: nested,
409 },
410 ))
411}
412
413fn parse_field_select(input: &str) -> IResult<&str, SelectItem> {
415 let (input, alias) = opt(parse_alias_prefix)(input)?;
419
420 let (input, name) = parse_field_name(input)?;
421 let (input, json_path) = parse_json_path(input)?;
422
423 let (input, cast) = opt(preceded(tag("::"), parse_cast_type))(input)?;
426 let (input, aggregate) = opt(parse_aggregate_suffix)(input)?;
427 let (input, aggregate_cast) = if aggregate.is_some() {
428 let (input, cast) = opt(preceded(tag("::"), parse_cast_type))(input)?;
429 (input, cast.map(|s| s.to_string()))
430 } else {
431 (input, None)
432 };
433
434 Ok((
435 input,
436 SelectItem::Field {
437 field: Field { name, json_path },
438 aggregate,
439 aggregate_cast,
440 cast: cast.map(|s| s.to_string()),
441 alias: alias.map(|s| s.to_string()),
442 },
443 ))
444}
445
446fn parse_alias_prefix(input: &str) -> IResult<&str, String> {
460 let (rest, name) = parse_field_name(input)?;
461 let (rest, _) = char(':')(rest)?;
462
463 if rest.starts_with(':') {
464 return Err(nom::Err::Error(nom::error::Error::new(
465 input,
466 nom::error::ErrorKind::Verify,
467 )));
468 }
469
470 Ok((rest, name))
471}
472
473fn parse_relation_modifiers(input: &str) -> IResult<&str, (Option<String>, Option<JoinType>)> {
474 let (input, modifiers) = many0(preceded(char('!'), parse_identifier))(input)?;
475
476 let mut hint = None;
477 let mut join_type = None;
478 for modifier in modifiers {
479 match modifier {
480 "inner" => join_type = Some(JoinType::Inner),
481 "left" => join_type = Some(JoinType::Left),
482 other => hint = Some(other.to_string()),
483 }
484 }
485
486 Ok((input, (hint, join_type)))
487}
488
489fn parse_filter_param(key: &str, value: &str) -> Result<(EmbedPath, Filter)> {
495 let (path, field) = parse_filter_key(key)?;
497
498 let op_expr = parse_filter_value(value)?;
500
501 Ok((path, Filter::new(field, op_expr)))
502}
503
504fn split_json_path(reference: &str) -> Field {
511 let Some(arrow) = reference.find("->") else {
512 return Field::simple(reference);
513 };
514
515 let (column, rest) = reference.split_at(arrow);
516 match parse_json_path(rest) {
517 Ok(("", json_path)) if !json_path.is_empty() => Field::with_json_path(column, json_path),
518 _ => Field::simple(reference),
519 }
520}
521
522fn parse_filter_key(key: &str) -> Result<(EmbedPath, Field)> {
534 if let Some(parsed) = parse_field_reference(key) {
535 return Ok(parsed);
536 }
537
538 let mut parts: Vec<&str> = key.split('.').collect();
539 let field = parts
540 .pop()
541 .ok_or_else(|| Error::InvalidQueryParam(key.into()))?;
542 let path = parts.into_iter().map(String::from).collect();
543 Ok((path, split_json_path(field)))
544}
545
546fn parse_field_reference(key: &str) -> Option<(EmbedPath, Field)> {
551 let mut names = Vec::new();
552 let mut rest = key;
553
554 loop {
555 let (remainder, name) = parse_field_name(rest).ok()?;
556 names.push(name);
557 match remainder.strip_prefix('.') {
558 Some(after) => rest = after,
559 None => {
560 rest = remainder;
561 break;
562 }
563 }
564 }
565
566 let (rest, json_path) = parse_json_path(rest).ok()?;
567 if !rest.is_empty() {
568 return None;
569 }
570
571 let field = names.pop()?;
572 Some((names, Field::with_json_path(field, json_path)))
573}
574
575pub fn value_is_filter(value: &str) -> bool {
582 parse_filter_value(value).is_ok()
583}
584
585fn parse_filter_value(value: &str) -> Result<OpExpr> {
587 let whole = value;
588 let (value, negated) = if let Some(rest) = value.strip_prefix("not.") {
589 (rest, true)
590 } else {
591 (value, false)
592 };
593
594 let operation = parse_operation(value).map_err(|error| match error {
595 Error::InvalidQueryParam(_) => Error::UnparsableQuery {
600 kind: "filter",
601 text: whole.to_string(),
602 column: 1,
603 expected: format!(
604 "unexpected \"{}\" expecting \"not\" or operator (eq, gt, ...)",
605 whole.chars().next().unwrap_or_default()
606 ),
607 },
608 other => other,
609 })?;
610 Ok(OpExpr { negated, operation })
611}
612
613fn quant_operator(name: &str) -> Option<QuantOperator> {
617 Some(match name {
618 "eq" => QuantOperator::Equal,
619 "gt" => QuantOperator::GreaterThan,
620 "gte" => QuantOperator::GreaterThanEqual,
621 "lt" => QuantOperator::LessThan,
622 "lte" => QuantOperator::LessThanEqual,
623 "like" => QuantOperator::Like,
624 "ilike" => QuantOperator::ILike,
625 "match" => QuantOperator::Match,
626 "imatch" => QuantOperator::IMatch,
627 _ => return None,
628 })
629}
630
631fn parse_quantifier(modifier: &str) -> Option<OpQuantifier> {
633 match modifier {
634 "any" => Some(OpQuantifier::Any),
635 "all" => Some(OpQuantifier::All),
636 _ => None,
637 }
638}
639
640fn parse_operation(value: &str) -> Result<Operation> {
642 if let Some((name, rest)) = value.split_once('(') {
646 if let Some((modifier, operand)) = rest.split_once(").") {
647 if let (Some(op), Some(quantifier)) = (quant_operator(name), parse_quantifier(modifier))
648 {
649 return Ok(Operation::Quant {
650 op,
651 quantifier: Some(quantifier),
652 value: operand.to_string(),
653 });
654 }
655 }
656 }
657
658 if let Some((name, rest)) = value.split_once('.') {
661 if let Some(op) = quant_operator(name) {
662 return Ok(Operation::Quant {
663 op,
664 quantifier: None,
665 value: rest.to_string(),
666 });
667 }
668 }
669
670 if let Some(rest) = value.strip_prefix("neq.") {
671 return Ok(Operation::Simple {
672 op: SimpleOperator::NotEqual,
673 value: rest.to_string(),
674 });
675 }
676
677 if let Some(rest) = value.strip_prefix("cs.") {
679 return Ok(Operation::Simple {
680 op: SimpleOperator::Contains,
681 value: rest.to_string(),
682 });
683 }
684 if let Some(rest) = value.strip_prefix("cd.") {
685 return Ok(Operation::Simple {
686 op: SimpleOperator::Contained,
687 value: rest.to_string(),
688 });
689 }
690 if let Some(rest) = value.strip_prefix("ov.") {
691 return Ok(Operation::Simple {
692 op: SimpleOperator::Overlap,
693 value: rest.to_string(),
694 });
695 }
696 if let Some(rest) = value.strip_prefix("sl.") {
697 return Ok(Operation::Simple {
698 op: SimpleOperator::StrictlyLeft,
699 value: rest.to_string(),
700 });
701 }
702 if let Some(rest) = value.strip_prefix("sr.") {
703 return Ok(Operation::Simple {
704 op: SimpleOperator::StrictlyRight,
705 value: rest.to_string(),
706 });
707 }
708 if let Some(rest) = value.strip_prefix("nxr.") {
709 return Ok(Operation::Simple {
710 op: SimpleOperator::NotExtendsRight,
711 value: rest.to_string(),
712 });
713 }
714 if let Some(rest) = value.strip_prefix("nxl.") {
715 return Ok(Operation::Simple {
716 op: SimpleOperator::NotExtendsLeft,
717 value: rest.to_string(),
718 });
719 }
720 if let Some(rest) = value.strip_prefix("adj.") {
721 return Ok(Operation::Simple {
722 op: SimpleOperator::Adjacent,
723 value: rest.to_string(),
724 });
725 }
726
727 if let Some(rest) = value.strip_prefix("in.") {
729 let values = parse_in_list(rest)?;
730 return Ok(Operation::In(values));
731 }
732
733 if let Some(rest) = value.strip_prefix("is.") {
735 let is_val = match rest.to_ascii_lowercase().as_str() {
739 "null" => IsValue::Null,
740 "not_null" => IsValue::NotNull,
741 "true" => IsValue::True,
742 "false" => IsValue::False,
743 "unknown" => IsValue::Unknown,
744 _ => return Err(unreadable_is_value(rest)),
745 };
746 return Ok(Operation::Is(is_val));
747 }
748
749 if let Some(rest) = value.strip_prefix("isdistinct.") {
751 return Ok(Operation::IsDistinctFrom(rest.to_string()));
752 }
753
754 if let Some(rest) = value.strip_prefix("fts") {
756 return parse_fts(FtsOperator::Fts, rest);
757 }
758 if let Some(rest) = value.strip_prefix("plfts") {
759 return parse_fts(FtsOperator::Plain, rest);
760 }
761 if let Some(rest) = value.strip_prefix("phfts") {
762 return parse_fts(FtsOperator::Phrase, rest);
763 }
764 if let Some(rest) = value.strip_prefix("wfts") {
765 return parse_fts(FtsOperator::Websearch, rest);
766 }
767
768 Err(Error::InvalidQueryParam(value.into()))
769}
770
771fn unreadable_order(term: &str, at: usize, part: &str) -> Error {
777 const MODIFIERS: [&str; 4] = ["asc", "desc", "nullsfirst", "nullslast"];
778
779 let read = MODIFIERS
780 .iter()
781 .map(|modifier| {
782 part.chars()
783 .zip(modifier.chars())
784 .take_while(|(a, b)| a == b)
785 .count()
786 })
787 .max()
788 .unwrap_or(0);
789
790 Error::UnparsableQuery {
791 kind: "order",
792 text: term.to_string(),
793 column: at + read + 1,
794 expected: format!(
795 "unexpected {:?} expecting \",\" or end of input",
796 part.chars().nth(read).unwrap_or_default()
797 ),
798 }
799}
800
801fn unreadable_is_value(rest: &str) -> Error {
809 const VALUES: [&str; 5] = ["null", "not_null", "true", "false", "unknown"];
810
811 let lowered = rest.to_ascii_lowercase();
812 let read = VALUES
813 .iter()
814 .map(|value| {
815 lowered
816 .chars()
817 .zip(value.chars())
818 .take_while(|(a, b)| a == b)
819 .count()
820 })
821 .max()
822 .unwrap_or(0);
823
824 Error::UnparsableQuery {
825 kind: "filter",
826 text: format!("is.{}", rest),
827 column: 4 + read,
829 expected: format!(
830 "unexpected \"{}\" expecting isVal: ({})",
831 rest.chars().nth(read).unwrap_or_default(),
832 VALUES.join(", ")
833 ),
834 }
835}
836
837fn parse_in_list(value: &str) -> Result<Vec<String>> {
839 let inner = value
843 .trim()
844 .strip_prefix('(')
845 .and_then(|s| s.strip_suffix(')'))
846 .map(|inner| {
847 inner
848 .trim_start_matches([' ', '\t'])
849 .trim_end_matches([' ', '\t'])
850 })
851 .ok_or_else(|| Error::InvalidQueryParam(format!("in.{}", value)))?;
852
853 if inner.is_empty() {
856 return Ok(Vec::new());
857 }
858
859 let mut values = Vec::new();
860 let mut rest = inner;
861
862 loop {
863 let element = match quoted_element(rest) {
868 Some((unquoted, remainder)) => {
869 rest = remainder;
870 unquoted
871 }
872 None => {
873 let end = rest.find(',').unwrap_or(rest.len());
874 let (element, remainder) = rest.split_at(end);
875 rest = remainder;
876 element.to_string()
877 }
878 };
879 values.push(element);
880
881 match rest.strip_prefix(',') {
882 Some(remainder) => rest = remainder,
883 None => break,
884 }
885 }
886
887 Ok(values)
888}
889
890fn quoted_element(input: &str) -> Option<(String, &str)> {
895 let body = input.strip_prefix('"')?;
896
897 let mut value = String::new();
898 let mut chars = body.char_indices();
899 while let Some((index, ch)) = chars.next() {
900 match ch {
901 '\\' => {
902 let (_, escaped) = chars.next()?;
903 value.push(escaped);
904 }
905 '"' => {
906 let rest = &body[index + 1..];
907 return match rest.is_empty() || rest.starts_with(',') {
908 true => Some((value, rest)),
909 false => None,
910 };
911 }
912 other => value.push(other),
913 }
914 }
915
916 None
917}
918
919fn parse_fts(op: FtsOperator, rest: &str) -> Result<Operation> {
921 if let Some(rest) = rest.strip_prefix('(') {
922 let (lang, query) = rest
924 .split_once(").")
925 .ok_or_else(|| Error::InvalidQueryParam(format!("fts{}", rest)))?;
926 return Ok(Operation::Fts {
927 op,
928 language: Some(lang.to_string()),
929 value: query.to_string(),
930 });
931 }
932
933 let query = rest
934 .strip_prefix('.')
935 .ok_or_else(|| Error::InvalidQueryParam(format!("fts{}", rest)))?;
936 Ok(Operation::Fts {
937 op,
938 language: None,
939 value: query.to_string(),
940 })
941}
942
943fn parse_order_param(value: &str) -> Result<(EmbedPath, Vec<OrderTerm>)> {
949 let terms: Vec<OrderTerm> = value
950 .split(',')
951 .map(|s| parse_order_term(s.trim()))
952 .collect::<Result<Vec<_>>>()?;
953 Ok((vec![], terms))
954}
955
956fn parse_order_term(value: &str) -> Result<OrderTerm> {
957 let parts: Vec<&str> = value.split('.').collect();
958 if parts.is_empty() {
959 return Err(Error::InvalidQueryParam("order".into()));
960 }
961
962 let field_name = parts[0];
963 let field = split_json_path(field_name);
964 let mut direction = None;
965 let mut nulls = None;
966
967 let mut at = field_name.len() + 1;
970 for part in &parts[1..] {
971 match *part {
972 "asc" => direction = Some(OrderDirection::Asc),
973 "desc" => direction = Some(OrderDirection::Desc),
974 "nullsfirst" => nulls = Some(OrderNulls::First),
975 "nullslast" => nulls = Some(OrderNulls::Last),
976 other => return Err(unreadable_order(value, at, other)),
981 }
982 at += part.len() + 1;
983 }
984
985 if let Some((relation, rest)) = field_name.split_once('(') {
988 if let Some(column) = rest.strip_suffix(')') {
989 return Ok(OrderTerm::Relation {
990 relation: relation.to_string(),
991 field: split_json_path(column),
992 direction,
993 nulls,
994 });
995 }
996 }
997
998 Ok(OrderTerm::Field {
999 field,
1000 direction,
1001 nulls,
1002 })
1003}
1004
1005fn parse_logic_param(op: LogicOperator, negated: bool, value: &str) -> Result<LogicTree> {
1011 let trimmed = value.trim();
1018 let inner = balanced_group(trimmed).ok_or_else(|| Error::InvalidQueryParam(value.into()))?;
1019
1020 let members = split_top_level(inner);
1027 for member in &members {
1028 if !member.trim().is_empty() {
1029 continue;
1030 }
1031 let at = offset_within(trimmed, member);
1032 return Err(Error::UnparsableQuery {
1033 kind: "logic tree",
1034 text: trimmed.to_string(),
1035 column: negated as usize * "not.".len()
1036 + match op {
1037 LogicOperator::And => 3,
1038 LogicOperator::Or => 2,
1039 }
1040 + at
1041 + 1,
1042 expected: format!(
1043 "unexpected \"{}\" expecting field name (* or [a..z0..9_$]), \
1044 negation operator (not) or logic operator (and, or)",
1045 trimmed[at..].chars().next().unwrap_or_default()
1046 ),
1047 });
1048 }
1049
1050 Ok(LogicTree::Expr {
1051 negated,
1052 op,
1053 children: members
1054 .into_iter()
1055 .map(parse_logic_child)
1056 .collect::<Result<Vec<_>>>()?,
1057 })
1058}
1059
1060fn offset_within(whole: &str, part: &str) -> usize {
1066 (part.as_ptr() as usize).saturating_sub(whole.as_ptr() as usize)
1067}
1068
1069fn balanced_group(input: &str) -> Option<&str> {
1074 let body = input.strip_prefix('(')?;
1075
1076 let mut depth = 0usize;
1077 let mut quoted = false;
1078 let mut escaped = false;
1079
1080 for (idx, ch) in body.char_indices() {
1081 if quoted {
1082 match ch {
1083 _ if escaped => escaped = false,
1084 '\\' => escaped = true,
1085 '"' => quoted = false,
1086 _ => {}
1087 }
1088 continue;
1089 }
1090 match ch {
1091 '"' => quoted = true,
1092 '(' => depth += 1,
1093 ')' if depth == 0 => return Some(&body[..idx]),
1094 ')' => depth -= 1,
1095 _ => {}
1096 }
1097 }
1098
1099 None
1100}
1101
1102fn split_top_level(input: &str) -> Vec<&str> {
1108 let mut parts = Vec::new();
1109 let mut depth = 0usize;
1110 let mut quoted = false;
1111 let mut escaped = false;
1112 let mut start = 0usize;
1113
1114 for (idx, ch) in input.char_indices() {
1115 if quoted {
1116 match ch {
1117 _ if escaped => escaped = false,
1118 '\\' => escaped = true,
1119 '"' => quoted = false,
1120 _ => {}
1121 }
1122 continue;
1123 }
1124 match ch {
1125 '"' => quoted = true,
1126 '(' | '{' | '[' => depth += 1,
1127 ')' | '}' | ']' => depth = depth.saturating_sub(1),
1128 ',' if depth == 0 => {
1129 parts.push(&input[start..idx]);
1130 start = idx + ch.len_utf8();
1131 }
1132 _ => {}
1133 }
1134 }
1135 parts.push(&input[start..]);
1136 parts
1137}
1138
1139fn parse_logic_child(item: &str) -> Result<LogicTree> {
1141 let item = item.trim();
1142
1143 let (body, negated) = match item.strip_prefix("not.") {
1147 Some(rest) if rest.starts_with("and(") || rest.starts_with("or(") => (rest, true),
1148 _ => (item, false),
1149 };
1150
1151 for (name, op) in [("and", LogicOperator::And), ("or", LogicOperator::Or)] {
1152 if let Some(rest) = body.strip_prefix(name) {
1153 let rest = rest.trim_start();
1154 if rest.starts_with('(') && rest.ends_with(')') {
1155 return parse_logic_param(op, negated, rest);
1156 }
1157 }
1158 }
1159
1160 let (key, val) = body
1161 .split_once('.')
1162 .ok_or_else(|| Error::InvalidQueryParam(item.into()))?;
1163 let (_, filter) = parse_filter_param(key, &unquote_logic_operand(val))?;
1164 Ok(LogicTree::Stmt(filter))
1165}
1166
1167fn unquote_logic_operand(value: &str) -> String {
1175 let Some(open) = value.find('"') else {
1176 return value.to_string();
1177 };
1178 if open != 0 && !value[..open].ends_with('.') {
1181 return value.to_string();
1182 }
1183 if !value.ends_with('"') || value.len() < open + 2 {
1184 return value.to_string();
1185 }
1186
1187 let mut unquoted = String::from(&value[..open]);
1188 let mut chars = value[open + 1..value.len() - 1].chars();
1189 while let Some(ch) = chars.next() {
1190 match ch {
1191 '\\' => {
1192 if let Some(escaped) = chars.next() {
1193 unquoted.push(escaped);
1194 }
1195 }
1196 other => unquoted.push(other),
1197 }
1198 }
1199 unquoted
1200}
1201
1202enum Modifier {
1204 Order,
1205 Limit,
1206 Offset,
1207 Logic(LogicOperator, bool),
1208}
1209
1210fn parse_modifier_key(key: &str) -> Option<(EmbedPath, Modifier)> {
1217 let mut parts: Vec<&str> = key.split('.').collect();
1218 let last = parts.pop()?;
1219
1220 let modifier = match last {
1221 "order" => Modifier::Order,
1222 "limit" => Modifier::Limit,
1223 "offset" => Modifier::Offset,
1224 "and" | "or" => {
1225 let op = if last == "and" {
1226 LogicOperator::And
1227 } else {
1228 LogicOperator::Or
1229 };
1230 let negated = parts.last() == Some(&"not");
1232 if negated {
1233 parts.pop();
1234 }
1235 Modifier::Logic(op, negated)
1236 }
1237 _ => return None,
1238 };
1239
1240 Some((parts.into_iter().map(String::from).collect(), modifier))
1241}
1242
1243const MULTI_WORD_TYPES: &[&str] = &[
1259 "timestamp without time zone",
1260 "timestamp with time zone",
1261 "time without time zone",
1262 "time with time zone",
1263 "character varying",
1264 "double precision",
1265 "bit varying",
1266];
1267
1268fn parse_cast_type(input: &str) -> IResult<&str, &str> {
1282 for name in MULTI_WORD_TYPES {
1283 let Some(head) = input.get(..name.len()) else {
1284 continue;
1285 };
1286 let rest = &input[name.len()..];
1287 let ends_there =
1288 !rest.starts_with(|c: char| c.is_ascii_alphanumeric() || c == '_' || c == '$');
1289 if head.eq_ignore_ascii_case(name) && ends_there {
1290 return Ok((rest, name));
1291 }
1292 }
1293 recognize(pair(
1294 satisfy(|c| c.is_ascii_alphabetic() || c == '_'),
1295 take_while(|c: char| c.is_ascii_alphanumeric() || c == '_' || c == '$'),
1296 ))(input)
1297}
1298
1299fn parse_identifier(input: &str) -> IResult<&str, &str> {
1300 let (rest, matched) =
1301 take_while1(|c: char| c.is_alphanumeric() || c == '_' || c == '$' || c == ' ')(input)?;
1302 Ok((rest, matched.trim()))
1303}
1304
1305fn parse_field_name(input: &str) -> IResult<&str, String> {
1315 if let Ok(quoted) = parse_quoted_name(input) {
1316 return Ok(quoted);
1317 }
1318
1319 let (mut rest, first) = parse_identifier(input)?;
1320 let mut name = first.to_string();
1321
1322 while let Some(after_dash) = rest.strip_prefix('-') {
1323 if after_dash.starts_with('>') {
1324 break;
1325 }
1326 let Ok((next, segment)) = parse_identifier(after_dash) else {
1327 break;
1328 };
1329 name.push('-');
1330 name.push_str(segment);
1331 rest = next;
1332 }
1333
1334 Ok((rest, name))
1335}
1336
1337fn parse_quoted_name(input: &str) -> IResult<&str, String> {
1339 let (rest, _) = char('"')(input)?;
1340
1341 let mut name = String::new();
1342 let mut chars = rest.char_indices();
1343 while let Some((index, ch)) = chars.next() {
1344 match ch {
1345 '\\' => {
1346 if let Some((_, escaped)) = chars.next() {
1347 name.push(escaped);
1348 }
1349 }
1350 '"' => return Ok((&rest[index + 1..], name)),
1351 other => name.push(other),
1352 }
1353 }
1354
1355 Err(nom::Err::Error(nom::error::Error::new(
1356 input,
1357 nom::error::ErrorKind::Verify,
1358 )))
1359}
1360
1361fn parse_json_path(input: &str) -> IResult<&str, JsonPath> {
1362 many0(alt((parse_double_arrow, parse_arrow)))(input)
1365}
1366
1367fn parse_json_operand(input: &str) -> IResult<&str, JsonOperand> {
1379 let mut end = input.len();
1380 for (idx, ch) in input.char_indices() {
1381 if matches!(ch, ',' | '(' | ')' | ':') || input[idx..].starts_with("->") {
1384 end = idx;
1385 break;
1386 }
1387 }
1388
1389 let (key, rest) = input.split_at(end);
1390 if key.is_empty() {
1391 return Err(nom::Err::Error(nom::error::Error::new(
1392 input,
1393 nom::error::ErrorKind::TakeWhile1,
1394 )));
1395 }
1396
1397 let operand = match key.parse::<i32>() {
1398 Ok(index) => JsonOperand::Idx(index),
1399 Err(_) if key.starts_with('-') => {
1409 return Err(nom::Err::Failure(nom::error::Error::new(
1410 &input[1..],
1411 nom::error::ErrorKind::Digit,
1412 )))
1413 }
1414 Err(_) => JsonOperand::Key(key.to_string()),
1415 };
1416 Ok((rest, operand))
1417}
1418
1419fn parse_arrow(input: &str) -> IResult<&str, JsonOperation> {
1420 let (input, _) = tag("->")(input)?;
1421 let (input, operand) = parse_json_operand(input)?;
1422 Ok((input, JsonOperation::Arrow(operand)))
1423}
1424
1425fn parse_double_arrow(input: &str) -> IResult<&str, JsonOperation> {
1426 let (input, _) = tag("->>")(input)?;
1427 let (input, operand) = parse_json_operand(input)?;
1428 Ok((input, JsonOperation::DoubleArrow(operand)))
1429}
1430
1431#[cfg(test)]
1432mod tests {
1433
1434 #[test]
1440 fn nesting_is_bounded() {
1441 let select = |depth: usize| format!("{}id{}", "a(".repeat(depth), ")".repeat(depth));
1442 let or = |depth: usize| format!("or=({}a.eq.1{})", "or(".repeat(depth), ")".repeat(depth));
1443
1444 assert!(parse_select(&select(MAX_NESTING)).is_ok());
1445 assert!(parse_query_params(&or(MAX_NESTING - 1), false).is_ok());
1446 for depth in [MAX_NESTING + 1, 100_000] {
1447 assert!(matches!(
1448 parse_select(&select(depth)),
1449 Err(Error::InvalidQueryParam(_))
1450 ));
1451 assert!(matches!(
1452 parse_query_params(&or(depth), false),
1453 Err(Error::InvalidQueryParam(_))
1454 ));
1455 }
1456
1457 let quoted = format!("or=(name.eq.\"{}\")", "(".repeat(1_000));
1459 assert!(parse_query_params("ed, false).is_ok());
1460 }
1461
1462 #[test]
1463 fn a_cast_is_a_type_name_and_nothing_more() {
1464 for select in [
1465 "id::text as rolname",
1466 "id::text from pg_roles where true or true",
1467 "id::int4 union select",
1468 "count()::text as x",
1469 "amount.sum()::numeric as x",
1470 "id::1text",
1471 "id::",
1472 "id::text;",
1473 ] {
1474 assert!(parse_select(select).is_err(), "{select:?} was accepted");
1475 }
1476 }
1477
1478 #[test]
1479 fn a_cast_names_a_bare_type_or_a_standard_multi_word_one() {
1480 let cast_of = |select: &str| match &parse_select(select).unwrap()[0] {
1481 SelectItem::Field {
1482 cast,
1483 aggregate_cast,
1484 ..
1485 } => cast.clone().or_else(|| aggregate_cast.clone()),
1486 other => panic!("{other:?}"),
1487 };
1488 assert_eq!(cast_of("id::text").as_deref(), Some("text"));
1489 assert_eq!(cast_of("id::int4").as_deref(), Some("int4"));
1490 assert_eq!(cast_of("id::_text").as_deref(), Some("_text"));
1491 assert_eq!(cast_of("id::my$type").as_deref(), Some("my$type"));
1492 assert_eq!(cast_of("count()::text").as_deref(), Some("text"));
1493 assert_eq!(
1494 cast_of("amount::Double Precision").as_deref(),
1495 Some("double precision"),
1496 "written in the standard's spelling, not the client's"
1497 );
1498 assert_eq!(
1499 cast_of("at::timestamp with time zone").as_deref(),
1500 Some("timestamp with time zone")
1501 );
1502 assert_eq!(
1503 cast_of("at::time with time zone").as_deref(),
1504 Some("time with time zone")
1505 );
1506 assert_eq!(cast_of("at::timestamptz").as_deref(), Some("timestamptz"));
1508 assert_eq!(
1510 parse_select("amount::double precision,id").unwrap().len(),
1511 2
1512 );
1513 }
1514
1515 use super::*;
1516
1517 #[test]
1518 fn test_parse_simple_filter() {
1519 let params = parse_query_params("name=eq.John", false).unwrap();
1520 assert_eq!(params.filters_root.len(), 1);
1521 assert_eq!(params.filters_root[0].field.name, "name");
1522 }
1523
1524 #[test]
1525 fn test_parse_negated_filter() {
1526 let params = parse_query_params("status=not.eq.active", false).unwrap();
1527 assert!(params.filters_root[0].op_expr.negated);
1528 }
1529
1530 #[test]
1531 fn test_parse_in_filter() {
1532 let params = parse_query_params("id=in.(1,2,3)", false).unwrap();
1533 match ¶ms.filters_root[0].op_expr.operation {
1534 Operation::In(values) => {
1535 assert_eq!(values, &vec!["1", "2", "3"]);
1536 }
1537 _ => panic!("Expected In operation"),
1538 }
1539 }
1540
1541 #[test]
1542 fn test_json_path_double_arrow_wins_over_single() {
1543 let items = parse_select("settings->foo->>bar").unwrap();
1544 match &items[0] {
1545 SelectItem::Field { field, .. } => {
1546 assert_eq!(
1547 field.json_path,
1548 vec![
1549 JsonOperation::Arrow(JsonOperand::Key("foo".into())),
1550 JsonOperation::DoubleArrow(JsonOperand::Key("bar".into())),
1551 ]
1552 );
1553 }
1554 other => panic!("expected a field, got {:?}", other),
1555 }
1556 }
1557
1558 #[test]
1559 fn test_json_path_accepts_awkward_keys() {
1560 let items = parse_select("data->23-xy-45->>!@#$%^&*_e").unwrap();
1563 match &items[0] {
1564 SelectItem::Field { field, .. } => {
1565 assert_eq!(
1566 field.json_path,
1567 vec![
1568 JsonOperation::Arrow(JsonOperand::Key("23-xy-45".into())),
1569 JsonOperation::DoubleArrow(JsonOperand::Key("!@#$%^&*_e".into())),
1570 ]
1571 );
1572 }
1573 other => panic!("expected a field, got {:?}", other),
1574 }
1575 }
1576
1577 #[test]
1578 fn test_json_path_digits_are_an_index_but_only_when_whole() {
1579 let items = parse_select("data->0,other->0xy1").unwrap();
1580 let paths: Vec<_> = items
1581 .iter()
1582 .map(|i| match i {
1583 SelectItem::Field { field, .. } => field.json_path.clone(),
1584 other => panic!("expected a field, got {:?}", other),
1585 })
1586 .collect();
1587 assert_eq!(paths[0], vec![JsonOperation::Arrow(JsonOperand::Idx(0))]);
1588 assert_eq!(
1589 paths[1],
1590 vec![JsonOperation::Arrow(JsonOperand::Key("0xy1".into()))]
1591 );
1592 }
1593
1594 #[test]
1595 fn test_json_path_stops_at_a_cast() {
1596 let items = parse_select("settings->>foo::json").unwrap();
1597 match &items[0] {
1598 SelectItem::Field { field, cast, .. } => {
1599 assert_eq!(
1600 field.json_path,
1601 vec![JsonOperation::DoubleArrow(JsonOperand::Key("foo".into()))]
1602 );
1603 assert_eq!(cast.as_deref(), Some("json"));
1604 }
1605 other => panic!("expected a field, got {:?}", other),
1606 }
1607 }
1608
1609 #[test]
1610 fn test_parse_spread_relation_keeps_its_columns() {
1611 let items = parse_select("title,...directors(first_name,last_name)").unwrap();
1612 assert_eq!(items.len(), 2);
1613 match &items[1] {
1614 SelectItem::SpreadRelation {
1615 relation, select, ..
1616 } => {
1617 assert_eq!(relation, "directors");
1618 let names: Vec<_> = select
1619 .iter()
1620 .map(|item| match item {
1621 SelectItem::Field { field, .. } => field.name.clone(),
1622 other => panic!("expected a field, got {:?}", other),
1623 })
1624 .collect();
1625 assert_eq!(names, vec!["first_name", "last_name"]);
1626 }
1627 other => panic!("expected a spread relation, got {:?}", other),
1628 }
1629 }
1630
1631 #[test]
1632 fn test_parse_spread_relation_does_not_swallow_later_items() {
1633 let items = parse_select("...directors(name),year").unwrap();
1636 assert_eq!(items.len(), 2);
1637 assert!(matches!(items[0], SelectItem::SpreadRelation { .. }));
1638 match &items[1] {
1639 SelectItem::Field { field, .. } => assert_eq!(field.name, "year"),
1640 other => panic!("expected a field, got {:?}", other),
1641 }
1642 }
1643
1644 #[test]
1645 fn test_parse_nested_spread_relation() {
1646 let items = parse_select("id,films(title,...directors(name))").unwrap();
1647 match &items[1] {
1648 SelectItem::Relation { select, .. } => {
1649 assert_eq!(select.len(), 2);
1650 assert!(matches!(select[1], SelectItem::SpreadRelation { .. }));
1651 }
1652 other => panic!("expected a relation, got {:?}", other),
1653 }
1654 }
1655
1656 #[test]
1657 fn test_parse_in_filter_with_quoted_commas() {
1658 let params = parse_query_params(r#"name=in.("hi,there","yes,you")"#, false).unwrap();
1659 match ¶ms.filters_root[0].op_expr.operation {
1660 Operation::In(values) => {
1661 assert_eq!(values, &vec!["hi,there".to_string(), "yes,you".to_string()]);
1662 }
1663 other => panic!("expected an In operation, got {:?}", other),
1664 }
1665 }
1666
1667 #[test]
1668 fn test_parse_in_filter_with_escapes() {
1669 let params = parse_query_params(r#"name=in.("a\"b","c\\d")"#, false).unwrap();
1670 match ¶ms.filters_root[0].op_expr.operation {
1671 Operation::In(values) => {
1672 assert_eq!(values, &vec![r#"a"b"#.to_string(), r#"c\d"#.to_string()]);
1673 }
1674 other => panic!("expected an In operation, got {:?}", other),
1675 }
1676 }
1677
1678 #[test]
1679 fn test_parse_quantified_filter() {
1680 for (query, expected) in [
1681 ("id=eq(any).{1,2,3}", OpQuantifier::Any),
1682 ("id=eq(all).{1,2,3}", OpQuantifier::All),
1683 ] {
1684 let params = parse_query_params(query, false).unwrap();
1685 match ¶ms.filters_root[0].op_expr.operation {
1686 Operation::Quant {
1687 op,
1688 quantifier,
1689 value,
1690 } => {
1691 assert_eq!(op, &QuantOperator::Equal);
1692 assert_eq!(quantifier.as_ref(), Some(&expected));
1693 assert_eq!(value, "{1,2,3}");
1694 }
1695 other => panic!("expected a quantified operation, got {:?}", other),
1696 }
1697 }
1698 }
1699
1700 #[test]
1701 fn test_parse_quantified_like_keeps_array_literal() {
1702 let params = parse_query_params("name=like(any).{foo*,bar*}", false).unwrap();
1703 match ¶ms.filters_root[0].op_expr.operation {
1704 Operation::Quant {
1705 op,
1706 quantifier,
1707 value,
1708 } => {
1709 assert_eq!(op, &QuantOperator::Like);
1710 assert_eq!(quantifier.as_ref(), Some(&OpQuantifier::Any));
1711 assert_eq!(value, "{foo*,bar*}");
1714 }
1715 other => panic!("expected a quantified operation, got {:?}", other),
1716 }
1717 }
1718
1719 #[test]
1720 fn test_parse_negated_quantified_filter() {
1721 let params = parse_query_params("id=not.eq(any).{1,2}", false).unwrap();
1722 assert!(params.filters_root[0].op_expr.negated);
1723 assert!(matches!(
1724 params.filters_root[0].op_expr.operation,
1725 Operation::Quant {
1726 quantifier: Some(OpQuantifier::Any),
1727 ..
1728 }
1729 ));
1730 }
1731
1732 #[test]
1733 fn test_quantifier_form_does_not_capture_fts_language() {
1734 let params = parse_query_params("body=fts(english).cat", false).unwrap();
1737 match ¶ms.filters_root[0].op_expr.operation {
1738 Operation::Fts { language, .. } => {
1739 assert_eq!(language.as_deref(), Some("english"));
1740 }
1741 other => panic!("expected an fts operation, got {:?}", other),
1742 }
1743 }
1744
1745 #[test]
1746 fn test_parse_is_null() {
1747 let params = parse_query_params("deleted_at=is.null", false).unwrap();
1748 match ¶ms.filters_root[0].op_expr.operation {
1749 Operation::Is(IsValue::Null) => {}
1750 _ => panic!("Expected Is Null"),
1751 }
1752 }
1753
1754 #[test]
1755 fn test_parse_order() {
1756 let params = parse_query_params("order=name.asc,age.desc.nullslast", false).unwrap();
1757 assert_eq!(params.order.len(), 1);
1758 let (_, terms) = ¶ms.order[0];
1759 assert_eq!(terms.len(), 2);
1760 }
1761
1762 #[test]
1763 fn test_parse_limit_offset() {
1764 let params = parse_query_params("limit=10&offset=20", false).unwrap();
1765 let range = params.ranges.get("").unwrap();
1766 assert_eq!(range.limit, Some(10));
1767 assert_eq!(range.offset, 20);
1768 }
1769
1770 #[test]
1771 fn test_parse_select() {
1772 let items = parse_select("id,name,orders(id,amount)").unwrap();
1773 assert_eq!(items.len(), 3);
1774 }
1775
1776 #[test]
1777 fn test_parse_fts() {
1778 let params = parse_query_params("content=fts(english).search+term", false).unwrap();
1779 match ¶ms.filters_root[0].op_expr.operation {
1780 Operation::Fts {
1781 op,
1782 language,
1783 value,
1784 } => {
1785 assert_eq!(*op, FtsOperator::Fts);
1786 assert_eq!(language.as_deref(), Some("english"));
1787 assert_eq!(value, "search+term");
1788 }
1789 _ => panic!("Expected FTS operation"),
1790 }
1791 }
1792 #[test]
1794 fn an_empty_columns_parameter_is_a_parse_error() {
1795 let error = parse_columns("").unwrap_err();
1796 assert_eq!(
1797 error.to_string(),
1798 "\"failed to parse columns parameter ()\" (line 1, column 1)"
1799 );
1800 match error {
1801 Error::UnparsableQuery { expected, .. } => assert_eq!(
1802 expected,
1803 "unexpected end of input expecting field name (* or [a..z0..9_$])"
1804 ),
1805 other => panic!("wrong variant: {:?}", other),
1806 }
1807 }
1808
1809 #[test]
1811 fn a_missing_field_in_columns_is_reported_where_it_is() {
1812 let error = parse_columns("a,,b").unwrap_err();
1813 assert_eq!(
1814 error.to_string(),
1815 "\"failed to parse columns parameter (a,,b)\" (line 1, column 3)"
1816 );
1817 }
1818
1819 #[test]
1820 fn columns_parses_a_plain_list() {
1821 assert_eq!(
1822 parse_columns("a, b ,c").unwrap(),
1823 vec!["a".to_string(), "b".to_string(), "c".to_string()]
1824 );
1825 }
1826
1827 #[test]
1830 fn a_json_path_index_that_is_not_a_number_is_refused() {
1831 assert!(parse_select("data->>--34").is_err());
1832 assert!(parse_select("data->>-34").is_ok());
1833 assert!(parse_select("data->>34").is_ok());
1834 assert!(parse_select("data->>key").is_ok());
1835 }
1836 #[test]
1840 fn an_empty_logic_tree_member_names_the_character_and_the_column() {
1841 let error = parse_logic_param(LogicOperator::Or, false, "()").unwrap_err();
1842 assert_eq!(
1843 error.to_string(),
1844 "\"failed to parse logic tree (())\" (line 1, column 4)"
1845 );
1846 match &error {
1847 Error::UnparsableQuery { expected, .. } => assert_eq!(
1848 expected,
1849 "unexpected \")\" expecting field name (* or [a..z0..9_$]), \
1850 negation operator (not) or logic operator (and, or)"
1851 ),
1852 other => panic!("wrong variant: {:?}", other),
1853 }
1854
1855 let error = parse_logic_param(LogicOperator::And, false, "()").unwrap_err();
1857 assert_eq!(
1858 error.to_string(),
1859 "\"failed to parse logic tree (())\" (line 1, column 5)"
1860 );
1861
1862 let error = parse_logic_param(LogicOperator::Or, false, "(,)").unwrap_err();
1864 assert_eq!(
1865 error.to_string(),
1866 "\"failed to parse logic tree ((,))\" (line 1, column 4)"
1867 );
1868 }
1869}