Skip to main content

postrust_core/api_request/
query_params.rs

1//! Query parameter parsing using nom.
2//!
3//! Parses URL query strings into structured filter, select, order, and range data.
4//! Mirrors PostgREST's QueryParams.hs parsing logic.
5
6use 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
19/// Parse a query string into QueryParams.
20///
21/// `is_rpc` changes what an unrecognized key means. On a table, `a=2` is a
22/// malformed filter and an error, because a filter value must carry an
23/// operator. On a function, it is an argument -- so every such key is also
24/// recorded as a candidate argument, and one that fails to parse as a filter
25/// is no longer fatal. Which candidates are really arguments is settled at
26/// plan time, against the parameters the routine actually declares; a
27/// well-formed filter such as `id=eq.1` stays available to filter the result.
28pub 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    // Sort parameters for canonical form
36    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        // The key is decoded as well as the value. A filter on a JSON path
57        // arrives as `data-%3E%3Eb` from any client that escapes `>`, and
58        // would otherwise be looked up as a column of that literal name.
59        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        // Modifiers come before filters, since both are dotted: `clients.order`
66        // orders an embedded resource while `clients.name` filters one.
67        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                    // Caught here rather than left to `LIMIT -1`, which
78                    // PostgreSQL rejects with a message about SQL syntax for
79                    // what is a plainly out-of-range request.
80                    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                // Filter parameter
126                let parsed = parse_filter_param(key, &decoded_value);
127                let (path, filter) = match parsed {
128                    Ok(parsed) => parsed,
129                    // On a function, a key that isn't a filter is just an
130                    // argument, which was recorded above.
131                    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                // RPC parameters (anything else)
144                params.params.push((key.to_string(), decoded_value));
145            }
146        }
147    }
148
149    Ok(params)
150}
151
152// ============================================================================
153// Select Parsing
154// ============================================================================
155
156/// Parse the `columns` parameter value.
157///
158/// A list of field names and nothing else, so the only thing that can go wrong
159/// is a name that is not there -- `?columns=` names none, and `?columns=a,,b`
160/// leaves a gap in the middle. Both were read as a field whose name is the
161/// empty string, which got as far as the schema cache and came back as
162/// "Could not find the '' column", describing a lookup the client never asked
163/// for rather than the parameter it actually wrote.
164fn 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            // The comma that separated this field from the last one.
173            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                // Counting from one, at the character that should have begun
181                // the name -- the end of the input where there is none left.
182                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
200/// Parse the `select` parameter value.
201pub 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    // Anything left over is a parse failure, not something to ignore. Left
209    // ignored, one unparsable item silently discarded every item after it --
210    // `select=id, name, billing(address)` returned the id alone.
211    match parse_select_items(input) {
212        Ok(("", items)) => Ok(items),
213        _ => Err(Error::InvalidQueryParam(format!("select={}", input))),
214    }
215}
216
217/// How deeply a `select` or a logic tree may nest its parentheses.
218///
219/// Both are parsed by recursion, one level per parenthesis, and so are the
220/// plans and statements built from them. Unbounded, a few thousand levels --
221/// some 15 KB of query string -- overflowed a worker thread's stack, and a
222/// stack overflow aborts the process rather than failing the request. No
223/// embedding or condition a client means to send comes near this.
224const MAX_NESTING: usize = 64;
225
226/// Refuse a value nested deeper than [`MAX_NESTING`], before parsing it.
227///
228/// Parentheses inside a double-quoted value are the value's, not structure,
229/// and are not counted: `or=(name.eq."((((")` nests once.
230fn 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    // A space after a comma is ordinary in a hand-written URL, and PostgREST
267    // accepts it.
268    let (input, _) = nom::character::complete::space0(input)?;
269    alt((
270        parse_star,
271        // Before relations: `count()` is spelled exactly like an embed of a
272        // relation named `count` with an empty selection.
273        parse_bare_aggregate,
274        parse_spread_relation,
275        parse_relation_select,
276        parse_field_select,
277    ))(input)
278}
279
280/// Parse `*`, which names every column.
281///
282/// It is a select item like any other, so it composes: `*,clients(id)` asks
283/// for every column of this table and an embed besides, and `clients(*)` asks
284/// for every column of the embedded one.
285fn parse_star(input: &str) -> IResult<&str, SelectItem> {
286    let (input, _) = char('*')(input)?;
287    Ok((input, SelectItem::field("*")))
288}
289
290/// Parse a field-less aggregate: `count()`, `cnt:count()`, `count()::text`.
291///
292/// Only `count` is meaningful without a field -- the others have nothing to
293/// sum -- which is also what keeps this from swallowing embeds.
294fn 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            // An empty name marks the `COUNT(*)` form: there is no column to
303            // resolve, and nothing to group by.
304            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
313/// Parse an aggregate applied to a field: the `.sum()` of `amount.sum()`.
314fn 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
327/// Parse spread relation: `...relation(cols)`
328fn 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    // The parentheses are as much a part of the syntax here as they are for a
333    // plain embed. Leaving them for the caller to trip over meant the column
334    // list was silently discarded along with the rest of the select.
335    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
350/// Parse the contents of a relation's parentheses.
351///
352/// Scans to the parenthesis that closes the group -- tracking nesting, so
353/// `books(chapters(id))` is not truncated at the first `)` -- then parses the
354/// contents as a select list.
355fn 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
388/// Parse relation with embedded select: `relation(select_items)`
389fn parse_relation_select(input: &str) -> IResult<&str, SelectItem> {
390    // As with fields, the alias precedes what it names: `c:clients(*)`.
391    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    // The nested selection is parsed rather than skipped: it says which
395    // columns of the related resource to return, and may embed further
396    // relations of its own.
397    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
413/// Parse field select: `field`, `field::cast`, `field:alias`, `agg(field)`
414fn parse_field_select(input: &str) -> IResult<&str, SelectItem> {
415    // `alias:expression` -- the alias comes first, as in `myId:id` or
416    // `total:sum(amount)`. Only a name immediately followed by `:` is one;
417    // anything else backtracks and is read as the expression itself.
418    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    // A cast on the column binds before the aggregate: `key::integer.sum()`
424    // sums integers, where `.sum()::text` renders the sum as text.
425    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
446/// Parse the `!`-prefixed modifiers on an embedded relation.
447///
448/// A relation may carry a disambiguating hint, a join type, or both, in either
449/// order: `books!author(*)`, `books!inner(*)`, `books!author!inner(*)`. They
450/// cannot be told apart by position, only by spelling -- `inner` and `left`
451/// are join types and anything else names a foreign key or table -- so each
452/// modifier is read in turn and classified.
453/// An `alias:` prefix, distinguished from the `::` of a cast.
454///
455/// `myId:id` names the column; `id::text` casts it. Both start with an
456/// identifier followed by a colon, so the second colon is what tells them
457/// apart -- without this check `id::text` would be read as an alias `id`
458/// applied to nothing.
459fn 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
489// ============================================================================
490// Filter Parsing
491// ============================================================================
492
493/// Parse a filter parameter (key=value where key is a field name).
494fn parse_filter_param(key: &str, value: &str) -> Result<(EmbedPath, Filter)> {
495    // Parse the key for embedded path: rel.field or field
496    let (path, field) = parse_filter_key(key)?;
497
498    // Parse the value for operator and operand
499    let op_expr = parse_filter_value(value)?;
500
501    Ok((path, Filter::new(field, op_expr)))
502}
503
504/// Split a reference like `data->a->>b` into its column and JSON path.
505///
506/// A name with no arrows is returned unchanged, so this is safe to apply to
507/// every field reference. A trailing fragment that does not parse as a path is
508/// left attached to the column name rather than silently dropped -- the column
509/// then fails to resolve, which is the honest outcome.
510fn 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
522/// Parse a filter key into the embedded path and the field it names.
523///
524/// The key is a run of field names separated by `.`, optionally followed by a
525/// JSON path: `clients.id`, `data->>a`, `"a.dotted.column"`. Splitting on `.`
526/// alone would cut a quoted name in half, and would take the dot of
527/// `id.something` for a path separator, so the names are parsed rather than
528/// split.
529///
530/// A key the grammar cannot account for falls back to the plain split, which
531/// leaves whatever it produced to fail at column resolution -- an honest
532/// outcome, and the one this had before.
533fn 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
546/// A dotted path of field names followed by an optional JSON path.
547///
548/// `None` when the whole key was not consumed, which is the caller's signal
549/// that this grammar does not describe it.
550fn 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
575/// Whether a query parameter reads as a filter rather than a function argument.
576///
577/// On `/rpc/f?id=5&id=gt.2` the same name is both: `5` is the argument the
578/// function takes and `gt.2` filters what it returned. What tells them apart
579/// is the value -- an operator prefix means a filter -- so the classification
580/// has to look at the value rather than the name.
581pub fn value_is_filter(value: &str) -> bool {
582    parse_filter_value(value).is_ok()
583}
584
585/// Parse filter value: `operator.value` or `not.operator.value`
586fn 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        // Nothing in the value named an operator, so reading stopped at its
596        // first character -- and what a filter may begin with is a short,
597        // published list. Any other failure got further in and has more to say
598        // about where.
599        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
613/// Map an operator name to its comparison operator, if it is one.
614///
615/// These are exactly the operators that accept an `any`/`all` quantifier.
616fn 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
631/// Parse the parenthesised modifier of a quantified comparison.
632fn parse_quantifier(modifier: &str) -> Option<OpQuantifier> {
633    match modifier {
634        "any" => Some(OpQuantifier::Any),
635        "all" => Some(OpQuantifier::All),
636        _ => None,
637    }
638}
639
640/// Parse an operation: `eq.value`, `in.(a,b,c)`, `is.null`, etc.
641fn parse_operation(value: &str) -> Result<Operation> {
642    // A quantified comparison spells the quantifier in parentheses after the
643    // operator name: `col=like(any).{foo,bar}`. Full-text search borrows the
644    // same shape for its language, so the operator name decides the reading.
645    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    // Comparison operators. Every one of these accepts a quantifier, so they
659    // share the lookup above rather than repeating the name-to-operator map.
660    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    // Array/Range operators
678    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    // IN operator
728    if let Some(rest) = value.strip_prefix("in.") {
729        let values = parse_in_list(rest)?;
730        return Ok(Operation::In(values));
731    }
732
733    // IS operator
734    if let Some(rest) = value.strip_prefix("is.") {
735        // These are the only case-insensitive operands in the grammar, and
736        // `not_null` has no operator of its own -- `is.not_null` is the only
737        // spelling of `IS NOT NULL`.
738        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    // IS DISTINCT FROM
750    if let Some(rest) = value.strip_prefix("isdistinct.") {
751        return Ok(Operation::IsDistinctFrom(rest.to_string()));
752    }
753
754    // Full-text search
755    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
771/// The four modifiers an order term accepts, and where reading one stopped.
772///
773/// Reading gets as far as the word still could be one of them and reports the
774/// character that ruled the last one out, so `nullslasttt` fails on the tenth
775/// character rather than on the word.
776fn 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
801/// The five things `is.` accepts, and where reading one stopped.
802///
803/// Reading gets as far as the operand still could be one of them and reports
804/// the character that ruled the last one out -- `is.nil` fails on the `i`,
805/// having read `n` as the start of `null`, while `is.ok` fails on the `o`.
806/// That is where a client has to look, and it is the difference between a
807/// message about a typo and one about the whole request.
808fn 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        // Counting from one, past the `is.` that was read before this.
828        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
837/// Parse IN list: `(a,b,c)` -> vec!["a", "b", "c"]
838fn parse_in_list(value: &str) -> Result<Vec<String>> {
839    // Whitespace immediately inside the parentheses is not part of the first
840    // or last element: `in.(    )` is the empty list, while `in.( ,3,4)` has
841    // an empty first element and is a type error the database reports.
842    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    // `in.()` names no values at all, which is not the same as naming one
854    // empty value -- and is what `?id=in.()` returning nothing means.
855    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        // Quoting is what lets a value contain the comma that would otherwise
864        // end it, so it counts only when it spans the whole element: in
865        // `Double"Quote"McGraw"` the quotes are part of the name, and taking
866        // them for syntax would silently change what was asked for.
867        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
890/// A complete double-quoted element at the head of `input`.
891///
892/// `None` unless the closing quote ends the element -- what follows it has to
893/// be the comma that separates elements, or nothing at all.
894fn 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
919/// Parse FTS operation: `(language).query` or `.query`
920fn parse_fts(op: FtsOperator, rest: &str) -> Result<Operation> {
921    if let Some(rest) = rest.strip_prefix('(') {
922        // Has language specifier
923        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
943// ============================================================================
944// Order Parsing
945// ============================================================================
946
947/// Parse order parameter: `col.desc.nullsfirst,col2.asc`
948fn 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    // Where each modifier starts within the term, so a word that is not one
968    // can be reported at the character that made it not one.
969    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            // Not one of the four. Ignoring it answered a request nobody made:
977            // `order=id.asc.nullslasttt` was read as `id.asc` and the typo
978            // silently changed nothing, so a client asking for nulls last got
979            // whatever the table happened to give it.
980            other => return Err(unreadable_order(value, at, other)),
981        }
982        at += part.len() + 1;
983    }
984
985    // `clients(name)` orders by a column of an embedded resource rather than
986    // by one of this table's own.
987    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
1005// ============================================================================
1006// Logic Tree Parsing
1007// ============================================================================
1008
1009/// Parse `and` or `or` parameter: `(filter1,filter2)`
1010fn parse_logic_param(op: LogicOperator, negated: bool, value: &str) -> Result<LogicTree> {
1011    // Whitespace around the group and around each member is insignificant, as
1012    // it is in PostgREST: `and=( a.eq.1 , b.eq.2 )` is the same request as
1013    // `and=(a.eq.1,b.eq.2)`.
1014    // The group ends at the parenthesis that closes it, not at the last one in
1015    // the string: `and=(a.eq.1,b.eq.2))` is a well-formed group with a stray
1016    // character after it, and PostgREST reads it as one.
1017    let trimmed = value.trim();
1018    let inner = balanced_group(trimmed).ok_or_else(|| Error::InvalidQueryParam(value.into()))?;
1019
1020    // Where a member is empty there is nothing to hand the filter parser, and
1021    // "Invalid request" says only that something in a URL the client wrote is
1022    // wrong. PostgREST names the character and what would have been accepted
1023    // there, counting from the start of the tree as it spells it -- `or=(...)`
1024    // is read as `or(...)`, so the `=` is not a column and the operator's own
1025    // name is.
1026    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
1060/// Where `part` begins inside `whole`, which it is a subslice of.
1061///
1062/// The split loses the positions and the message needs them; taking them from
1063/// the pointers is exact where searching for the text would find the wrong
1064/// occurrence of a repeated member.
1065fn offset_within(whole: &str, part: &str) -> usize {
1066    (part.as_ptr() as usize).saturating_sub(whole.as_ptr() as usize)
1067}
1068
1069/// The contents of a parenthesised group at the head of `input`.
1070///
1071/// Anything after the closing parenthesis is not part of the group and is
1072/// discarded, which is what a parser that does not demand end-of-input does.
1073fn 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
1102/// Split a comma-separated list on its top-level commas only.
1103///
1104/// A logic list holds whole conditions, and a condition may contain commas of
1105/// its own -- `id.in.(1,2)`, `arr.cs.{1,2}`, a nested `and(...)`, or a quoted
1106/// value. Splitting on every comma tears those apart.
1107fn 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
1139/// Parse one member of a logic list: either a nested group or a condition.
1140fn parse_logic_child(item: &str) -> Result<LogicTree> {
1141    let item = item.trim();
1142
1143    // `not.and(...)` negates the group. A leading `not.` on anything else
1144    // belongs to the condition's operator (`arr.not.cs.{1,2}`), so it is left
1145    // in place for the filter parser to deal with.
1146    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
1167/// Strip the quotes from a logic-tree operand.
1168///
1169/// Inside `or=(...)` a value may be quoted so that it can contain the comma
1170/// that would otherwise end it -- `name.eq."(grandchild,entity,4)"` -- and the
1171/// quotes are then syntax rather than part of the value. Outside a logic tree
1172/// there is nothing to protect the value from, so a quote there is a
1173/// character like any other and is left alone.
1174fn unquote_logic_operand(value: &str) -> String {
1175    let Some(open) = value.find('"') else {
1176        return value.to_string();
1177    };
1178    // The quote has to open the operand rather than sit inside one: `id.in.(
1179    // "a","b")` is a list, which does its own unquoting element by element.
1180    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
1202/// A query parameter that modifies a resource rather than filtering it.
1203enum Modifier {
1204    Order,
1205    Limit,
1206    Offset,
1207    Logic(LogicOperator, bool),
1208}
1209
1210/// Recognise a modifier key and the embedded resource it addresses.
1211///
1212/// Every modifier may be aimed at an embedded resource by prefixing it with
1213/// the path to that resource: `clients.order=name` orders the embedded
1214/// clients, `clients.limit=1` pages them, `clients.or=(...)` filters them. The
1215/// bare forms are the same thing with an empty path.
1216fn 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            // `not.or=(...)` negates the whole group.
1231            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
1243// ============================================================================
1244// Helper Parsers
1245// ============================================================================
1246
1247/// The characters a bare identifier is made of.
1248///
1249/// PostgREST's set exactly: letters, digits, `_`, `$` and the space, with the
1250/// result trimmed. A space is allowed because a column may genuinely have one
1251/// -- `?select=Just A Server Model` -- and trimming is what keeps
1252/// `?select=id, name` from asking for a column called `name ` .
1253/// The SQL standard's type names that are more than one word.
1254///
1255/// Longest first, so that `time with time zone` is not read as `time` with
1256/// something left over. Each is written into the statement in this spelling,
1257/// never in the client's.
1258const 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
1268/// Parse the type a value is cast to: `id::text`, `amount::double precision`.
1269///
1270/// The type is written into the statement as it is -- a type name is not a
1271/// value, so it cannot be bound, and quoting it would change its meaning
1272/// (`::"int"` names no type). So this is the whole of what keeps a cast from
1273/// carrying SQL of the client's into the statement: one type name, an
1274/// identifier of letters, digits, `_` and `$` that does not start with a
1275/// digit, or one of the standard's few multi-word names. Anything else after
1276/// `::` is not a cast, and the select it is in is refused as unparsable.
1277///
1278/// It used to be [`parse_identifier`], which admits spaces -- so that a column
1279/// name may contain one -- and with them `id::text as rolname` and any other
1280/// run of words.
1281fn 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
1305/// Parse a field name: a quoted name, or dash-joined identifiers.
1306///
1307/// Quoting is how a client names a column the bare grammar cannot spell --
1308/// `"a.dotted.column"`, `"(inside,parens)"` -- and a backslash escapes the
1309/// next character inside it.
1310///
1311/// Unquoted, a `-` joins identifiers into one name, so `field-with_sep` is a
1312/// single column. A `-` that starts `->` is not a join: that is a JSON path,
1313/// and it belongs to whatever comes after the name.
1314fn 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
1337/// Parse a double-quoted name, honouring backslash escapes.
1338fn 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    // `->>` first: it starts with `->`, so trying the shorter one first would
1363    // match it and leave the second `>` at the head of the key.
1364    many0(alt((parse_double_arrow, parse_arrow)))(input)
1365}
1366
1367/// Parse one step of a JSON path.
1368///
1369/// A JSON object key can be very nearly anything, and PostgREST's grammar says
1370/// so: everything up to the next thing the select grammar itself reserves is
1371/// the key. `data->>!@#$%^&*_e` and `data->23-xy-45` are ordinary keys, not
1372/// syntax errors, and reading a key as alphanumeric-only silently truncated
1373/// the path and returned the whole column instead.
1374///
1375/// A key made entirely of digits is an array index, which is why this decides
1376/// between the two only once the whole key has been read: `data->0xy1` steps
1377/// into the key `0xy1`, not into element 0 of an array.
1378fn parse_json_operand(input: &str) -> IResult<&str, JsonOperand> {
1379    let mut end = input.len();
1380    for (idx, ch) in input.char_indices() {
1381        // `,` ends the select item, `(` and `)` bound an embedding, `:` starts
1382        // a cast or an alias, and `->` starts the next step.
1383        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        // A leading `-` begins an array index counted from the end, so what
1400        // follows it has to be a number. `data->>--34` was read as a key
1401        // literally named `--34` and answered 200 with nulls, where the
1402        // grammar has no such key in it and PostgREST says so.
1403        // A `Failure` rather than an `Error`: the arrow has already been read,
1404        // so this is not a step that some other rule might match instead.
1405        // Reported as a recoverable error, `alt` backtracked from `->>` to
1406        // `->` and read the rest as a key beginning with `>`, which turned a
1407        // malformed index into an ordinary lookup that answered 200.
1408        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    /// A cast is one type name. Words after it are not part of the type, and
1435    /// a select that carries them is refused rather than written into the
1436    /// statement.
1437    /// Nesting is bounded before anything recurses over it, so a deep
1438    /// `select` or logic tree is refused rather than overflowing the stack.
1439    #[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        // A quoted value's parentheses are its own.
1458        let quoted = format!("or=(name.eq.\"{}\")", "(".repeat(1_000));
1459        assert!(parse_query_params(&quoted, 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        // `timestamptz` is one word, and not `timestamp` with `tz` left over.
1507        assert_eq!(cast_of("at::timestamptz").as_deref(), Some("timestamptz"));
1508        // A multi-word name followed by another item.
1509        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 &params.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        // PostgREST reserves only what its own grammar needs, so a key may
1561        // hold punctuation and hyphens.
1562        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        // The column list used to be left unconsumed, which silently truncated
1634        // everything after it.
1635        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 &params.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 &params.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 &params.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 &params.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                // The `*`-to-`%` mapping belongs to SQL generation, so the
1712                // parsed operand is still the literal the client sent.
1713                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        // `fts(english).x` has the same shape but is not a quantified
1735        // comparison; the operator name is what tells the two apart.
1736        let params = parse_query_params("body=fts(english).cat", false).unwrap();
1737        match &params.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 &params.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) = &params.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 &params.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    /// `?columns=` names no field, and is reported as the parameter it is.
1793    #[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    /// A gap in the middle is reported where the gap is, not at the end.
1810    #[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    /// A `-` inside a JSON path begins an index, so a non-number after it is
1828    /// not a key by another name.
1829    #[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    /// An empty member of a logic tree is reported the way PostgREST reports
1837    /// it, which counts from the tree as it spells it: `or=(...)` is read as
1838    /// `or(...)`, so the `=` is not a column and the operator's name is.
1839    #[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        // `and` is a longer name, so the same shape reports one column later.
1856        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        // A gap between two members is reported where the gap is.
1863        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}