Skip to main content

lance_jsonb/jsonpath/
parser.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3// SPDX-FileCopyrightText: Copyright 2023 Datafuse Labs
4// Adapted from databendlabs/jsonb at commit fba895c5ebe77ce2539e187f9c652f51cbf195c3.
5
6use std::borrow::Cow;
7
8use nom::IResult;
9use nom::Parser;
10use nom::branch::alt;
11use nom::bytes::complete::tag;
12use nom::bytes::complete::tag_no_case;
13use nom::character::complete::char;
14use nom::character::complete::i32;
15use nom::character::complete::i64;
16use nom::character::complete::multispace0;
17use nom::character::complete::u8;
18use nom::character::complete::u64;
19use nom::combinator::cond;
20use nom::combinator::map;
21use nom::combinator::map_res;
22use nom::combinator::opt;
23use nom::combinator::value;
24use nom::error::Error as NomError;
25use nom::error::ErrorKind;
26use nom::multi::many0;
27use nom::multi::separated_list1;
28use nom::number::complete::double;
29use nom::sequence::delimited;
30use nom::sequence::pair;
31use nom::sequence::preceded;
32use nom::sequence::separated_pair;
33use nom::sequence::terminated;
34
35use crate::constants::UNICODE_LEN;
36use crate::error::Error;
37use crate::jsonpath::path::*;
38use crate::number::Number;
39use crate::util::parse_string;
40
41/// Parsing the input string to JSON Path.
42pub fn parse_json_path(input: &[u8]) -> Result<JsonPath<'_>, Error> {
43    match json_path(input) {
44        Ok((rest, json_path)) => {
45            if !rest.is_empty() {
46                return Err(Error::InvalidJsonPath);
47            }
48            Ok(json_path)
49        }
50        Err(nom::Err::Error(_) | nom::Err::Failure(_)) => Err(Error::InvalidJsonPath),
51        Err(nom::Err::Incomplete(_)) => unreachable!(),
52    }
53}
54
55fn json_path(input: &[u8]) -> IResult<&[u8], JsonPath<'_>> {
56    map(
57        delimited(multispace0, expr_or_paths, multispace0),
58        |paths| JsonPath { paths },
59    )
60    .parse(input)
61}
62
63fn check_escaped(input: &[u8], i: &mut usize) -> bool {
64    if *i + 1 >= input.len() {
65        return false;
66    }
67    if input[*i + 1] == b'u' {
68        if *i + 5 >= input.len() {
69            return false;
70        }
71        if input[*i + 2] == b'{' {
72            if *i + 7 >= input.len() {
73                return false;
74            }
75            *i += UNICODE_LEN + 4;
76        } else {
77            *i += UNICODE_LEN + 2;
78        }
79    } else {
80        *i += 2;
81    }
82    true
83}
84
85pub fn raw_string(input: &[u8]) -> IResult<&[u8], Cow<'_, str>> {
86    let mut i = 0;
87    let mut escapes = 0;
88    while i < input.len() {
89        let c = input[i];
90        match c {
91            b'\\' => {
92                escapes += 1;
93                if !check_escaped(input, &mut i) {
94                    return Err(nom::Err::Error(NomError::new(input, ErrorKind::Char)));
95                }
96            }
97            b' ' | b',' | b'.' | b':' | b'{' | b'}' | b'[' | b']' | b'(' | b')' | b'?' | b'@'
98            | b'$' | b'|' | b'<' | b'>' | b'!' | b'=' | b'+' | b'-' | b'*' | b'/' | b'%' | b'"'
99            | b'\'' => {
100                break;
101            }
102            _ => {
103                i += 1;
104            }
105        }
106    }
107    if i > 0 {
108        if escapes == 0 {
109            if let Ok(s) = std::str::from_utf8(&input[..i]) {
110                return Ok((&input[i..], Cow::Borrowed(s)));
111            }
112        } else {
113            let data = &input[..i];
114            let len = i - escapes;
115            let mut idx = 0;
116            let s = parse_string(data, len, &mut idx)
117                .map_err(|_| nom::Err::Error(NomError::new(input, ErrorKind::Char)))?;
118            return Ok((&input[i..], Cow::Owned(s)));
119        }
120    }
121    Err(nom::Err::Error(NomError::new(input, ErrorKind::Char)))
122}
123
124pub fn string(input: &[u8]) -> IResult<&[u8], Cow<'_, str>> {
125    if input.is_empty() || input[0] != b'"' {
126        return Err(nom::Err::Error(NomError::new(input, ErrorKind::Char)));
127    }
128    let mut i = 1;
129    let mut escapes = 0;
130    while i < input.len() {
131        let c = input[i];
132        match c {
133            b'\\' => {
134                escapes += 1;
135                if !check_escaped(input, &mut i) {
136                    return Err(nom::Err::Error(NomError::new(input, ErrorKind::Char)));
137                }
138            }
139            b'"' => {
140                break;
141            }
142            _ => {
143                i += 1;
144            }
145        }
146    }
147    if i > 1 {
148        if escapes == 0 {
149            if let Ok(s) = std::str::from_utf8(&input[1..i]) {
150                return Ok((&input[i + 1..], Cow::Borrowed(s)));
151            }
152        } else {
153            let data = &input[1..i];
154            let len = i - 1 - escapes;
155            let mut idx = 1;
156            let s = parse_string(data, len, &mut idx)
157                .map_err(|_| nom::Err::Error(NomError::new(input, ErrorKind::Char)))?;
158            return Ok((&input[i + 1..], Cow::Owned(s)));
159        }
160    }
161    Err(nom::Err::Error(NomError::new(input, ErrorKind::Char)))
162}
163
164fn bracket_wildcard(input: &[u8]) -> IResult<&[u8], ()> {
165    value(
166        (),
167        delimited(
168            char('['),
169            delimited(multispace0, char('*'), multispace0),
170            char(']'),
171        ),
172    )
173    .parse(input)
174}
175
176fn recursive_dot_wildcard(input: &[u8]) -> IResult<&[u8], Option<RecursiveLevel>> {
177    preceded(tag(".**"), opt(recursive_level)).parse(input)
178}
179
180fn recursive_level(input: &[u8]) -> IResult<&[u8], RecursiveLevel> {
181    alt((
182        delimited(
183            char('{'),
184            delimited(multispace0, recursive_level_range, multispace0),
185            char('}'),
186        ),
187        map(
188            delimited(
189                char('{'),
190                delimited(multispace0, u8, multispace0),
191                char('}'),
192            ),
193            |s| RecursiveLevel {
194                start: s,
195                end: None,
196            },
197        ),
198    ))
199    .parse(input)
200}
201
202fn recursive_level_range(input: &[u8]) -> IResult<&[u8], RecursiveLevel> {
203    alt((
204        map(
205            separated_pair(
206                u8,
207                delimited(multispace0, tag_no_case("to"), multispace0),
208                u8,
209            ),
210            |(s, e)| RecursiveLevel {
211                start: s,
212                end: Some(RecursiveLevelEnd::Index(e)),
213            },
214        ),
215        map(
216            separated_pair(
217                u8,
218                delimited(multispace0, tag_no_case("to"), multispace0),
219                tag_no_case("last"),
220            ),
221            |(s, _)| RecursiveLevel {
222                start: s,
223                end: Some(RecursiveLevelEnd::Last),
224            },
225        ),
226    ))
227    .parse(input)
228}
229
230fn colon_field(input: &[u8]) -> IResult<&[u8], Cow<'_, str>> {
231    alt((preceded(char(':'), string), preceded(char(':'), raw_string))).parse(input)
232}
233
234fn dot_field(input: &[u8]) -> IResult<&[u8], Cow<'_, str>> {
235    alt((preceded(char('.'), string), preceded(char('.'), raw_string))).parse(input)
236}
237
238fn object_field(input: &[u8]) -> IResult<&[u8], Cow<'_, str>> {
239    delimited(
240        terminated(char('['), multispace0),
241        string,
242        preceded(multispace0, char(']')),
243    )
244    .parse(input)
245}
246
247fn index(input: &[u8]) -> IResult<&[u8], Index> {
248    alt((
249        map(i32, Index::Index),
250        map(
251            preceded(
252                separated_pair(tag_no_case("last"), multispace0, char('-')),
253                preceded(multispace0, i32),
254            ),
255            |v| Index::LastIndex(v.saturating_neg()),
256        ),
257        map(
258            preceded(
259                separated_pair(tag_no_case("last"), multispace0, char('+')),
260                preceded(multispace0, i32),
261            ),
262            Index::LastIndex,
263        ),
264        map(tag_no_case("last"), |_| Index::LastIndex(0)),
265    ))
266    .parse(input)
267}
268
269fn array_index(input: &[u8]) -> IResult<&[u8], ArrayIndex> {
270    alt((
271        map(
272            separated_pair(
273                index,
274                delimited(multispace0, tag_no_case("to"), multispace0),
275                index,
276            ),
277            |(s, e)| ArrayIndex::Slice((s, e)),
278        ),
279        map(index, ArrayIndex::Index),
280    ))
281    .parse(input)
282}
283
284fn array_indices(input: &[u8]) -> IResult<&[u8], Vec<ArrayIndex>> {
285    delimited(
286        char('['),
287        separated_list1(char(','), delimited(multispace0, array_index, multispace0)),
288        char(']'),
289    )
290    .parse(input)
291}
292
293fn inner_path(input: &[u8]) -> IResult<&[u8], Path<'_>> {
294    alt((
295        map(recursive_dot_wildcard, Path::RecursiveDotWildcard),
296        value(Path::DotWildcard, tag(".*")),
297        value(Path::BracketWildcard, bracket_wildcard),
298        map(colon_field, Path::ColonField),
299        map(dot_field, Path::DotField),
300        map(array_indices, Path::ArrayIndices),
301        map(object_field, Path::ObjectField),
302    ))
303    .parse(input)
304}
305
306// Compatible with Snowflake query syntax, the first field name does not require the leading period
307fn pre_path(input: &[u8]) -> IResult<&[u8], Path<'_>> {
308    alt((
309        value(Path::Root, char('$')),
310        map(delimited(multispace0, raw_string, multispace0), |v| {
311            Path::DotField(v)
312        }),
313    ))
314    .parse(input)
315}
316
317fn path(input: &[u8]) -> IResult<&[u8], Path<'_>> {
318    alt((
319        map(delimited(multispace0, inner_path, multispace0), |v| v),
320        map(delimited(multispace0, filter_expr, multispace0), |v| {
321            Path::FilterExpr(Box::new(v))
322        }),
323    ))
324    .parse(input)
325}
326
327fn expr_or_paths(input: &[u8]) -> IResult<&[u8], Vec<Path<'_>>> {
328    alt((root_expr, paths)).parse(input)
329}
330
331fn root_expr(input: &[u8]) -> IResult<&[u8], Vec<Path<'_>>> {
332    map(
333        delimited(multispace0, |i| expr_or(i, true), multispace0),
334        |v| vec![Path::Expr(Box::new(v))],
335    )
336    .parse(input)
337}
338
339fn paths(input: &[u8]) -> IResult<&[u8], Vec<Path<'_>>> {
340    map(
341        pair(opt(pre_path), many0(path)),
342        |(opt_pre_path, mut paths)| {
343            if let Some(pre_path) = opt_pre_path {
344                paths.insert(0, pre_path);
345            }
346            paths
347        },
348    )
349    .parse(input)
350}
351
352fn expr_paths(input: &[u8], is_root_expr: bool) -> IResult<&[u8], Vec<Path<'_>>> {
353    let parse_current = map_res(
354        cond(!is_root_expr, value(Path::Current, char('@'))),
355        |res| match res {
356            Some(v) => Ok(v),
357            None => Err(NomError::new(input, ErrorKind::Char)),
358        },
359    );
360    map(
361        pair(
362            alt((value(Path::Root, char('$')), parse_current)),
363            many0(delimited(multispace0, inner_path, multispace0)),
364        ),
365        |(pre_path, mut paths)| {
366            paths.insert(0, pre_path);
367            paths
368        },
369    )
370    .parse(input)
371}
372
373fn filter_expr(input: &[u8]) -> IResult<&[u8], Expr<'_>> {
374    map(
375        delimited(
376            delimited(char('?'), multispace0, char('(')),
377            delimited(multispace0, |i| expr_or(i, false), multispace0),
378            char(')'),
379        ),
380        |v| v,
381    )
382    .parse(input)
383}
384
385fn op(input: &[u8]) -> IResult<&[u8], BinaryOperator> {
386    alt((
387        value(BinaryOperator::Eq, tag("==")),
388        value(BinaryOperator::NotEq, tag("!=")),
389        value(BinaryOperator::NotEq, tag("<>")),
390        value(BinaryOperator::Lte, tag("<=")),
391        value(BinaryOperator::Lt, char('<')),
392        value(BinaryOperator::Gte, tag(">=")),
393        value(BinaryOperator::Gt, char('>')),
394        value(BinaryOperator::StartsWith, tag("starts with")),
395        value(BinaryOperator::Add, char('+')),
396        value(BinaryOperator::Subtract, char('-')),
397        value(BinaryOperator::Multiply, char('*')),
398        value(BinaryOperator::Divide, char('/')),
399        value(BinaryOperator::Modulo, char('%')),
400    ))
401    .parse(input)
402}
403
404fn unary_arith_op(input: &[u8]) -> IResult<&[u8], UnaryOperator> {
405    alt((
406        value(UnaryOperator::Add, char('+')),
407        value(UnaryOperator::Subtract, char('-')),
408    ))
409    .parse(input)
410}
411
412fn path_value(input: &[u8]) -> IResult<&[u8], PathValue<'_>> {
413    alt((
414        value(PathValue::Null, tag("null")),
415        value(PathValue::Boolean(true), tag("true")),
416        value(PathValue::Boolean(false), tag("false")),
417        map(u64, |v| PathValue::Number(Number::UInt64(v))),
418        map(i64, |v| PathValue::Number(Number::Int64(v))),
419        map(double, |v| PathValue::Number(Number::Float64(v))),
420        map(string, PathValue::String),
421    ))
422    .parse(input)
423}
424
425fn inner_expr(input: &[u8], is_root_expr: bool) -> IResult<&[u8], Expr<'_>> {
426    alt((
427        map(|i| expr_paths(i, is_root_expr), Expr::Paths),
428        map(path_value, |v| Expr::Value(Box::new(v))),
429    ))
430    .parse(input)
431}
432
433fn expr_atom(input: &[u8], is_root_expr: bool) -> IResult<&[u8], Expr<'_>> {
434    alt((
435        map(
436            pair(
437                unary_arith_op,
438                delimited(multispace0, |i| inner_expr(i, is_root_expr), multispace0),
439            ),
440            |(op, operand)| Expr::UnaryOp {
441                op,
442                operand: Box::new(operand),
443            },
444        ),
445        map(
446            pair(
447                pair(
448                    delimited(multispace0, |i| inner_expr(i, is_root_expr), multispace0),
449                    op,
450                ),
451                delimited(multispace0, |i| inner_expr(i, is_root_expr), multispace0),
452            ),
453            |((left, op), right)| Expr::BinaryOp {
454                op,
455                left: Box::new(left),
456                right: Box::new(right),
457            },
458        ),
459        map(
460            delimited(
461                terminated(char('('), multispace0),
462                |i| expr_or(i, is_root_expr),
463                preceded(multispace0, char(')')),
464            ),
465            |expr| expr,
466        ),
467        map(exists_func, Expr::ExistsFunc),
468    ))
469    .parse(input)
470}
471
472fn exists_func(input: &[u8]) -> IResult<&[u8], Vec<Path<'_>>> {
473    preceded(
474        tag("exists"),
475        preceded(
476            multispace0,
477            delimited(
478                terminated(char('('), multispace0),
479                exists_paths,
480                preceded(multispace0, char(')')),
481            ),
482        ),
483    )
484    .parse(input)
485}
486
487fn exists_paths(input: &[u8]) -> IResult<&[u8], Vec<Path<'_>>> {
488    map(
489        pair(
490            alt((
491                value(Path::Root, char('$')),
492                value(Path::Current, char('@')),
493            )),
494            many0(path),
495        ),
496        |(pre, mut paths)| {
497            paths.insert(0, pre);
498            paths
499        },
500    )
501    .parse(input)
502}
503
504fn expr_and(input: &[u8], is_root_expr: bool) -> IResult<&[u8], Expr<'_>> {
505    map(
506        separated_list1(delimited(multispace0, tag("&&"), multispace0), |i| {
507            expr_atom(i, is_root_expr)
508        }),
509        |exprs| {
510            let mut expr = exprs[0].clone();
511            for right in exprs.iter().skip(1) {
512                expr = Expr::BinaryOp {
513                    op: BinaryOperator::And,
514                    left: Box::new(expr),
515                    right: Box::new(right.clone()),
516                };
517            }
518            expr
519        },
520    )
521    .parse(input)
522}
523
524fn expr_or(input: &[u8], is_root_expr: bool) -> IResult<&[u8], Expr<'_>> {
525    map(
526        separated_list1(delimited(multispace0, tag("||"), multispace0), |i| {
527            expr_and(i, is_root_expr)
528        }),
529        |exprs| {
530            let mut expr = exprs[0].clone();
531            for right in exprs.iter().skip(1) {
532                expr = Expr::BinaryOp {
533                    op: BinaryOperator::Or,
534                    left: Box::new(expr),
535                    right: Box::new(right.clone()),
536                };
537            }
538            expr
539        },
540    )
541    .parse(input)
542}