Skip to main content

rudb_plan/
parse.rs

1//! The reader for the textual form.
2//!
3//! Recursive descent over lines for the tree and over characters for the expressions. It is a
4//! hand-written parser rather than a generated one because the grammar is fixed by the printer in
5//! `print.rs`, the two are edited together, and the useful property is that every construct the
6//! printer emits has exactly one place here that reads it back.
7//!
8//! Two things make this a small parser rather than a large one. Expressions are fully bracketed,
9//! so there is no precedence and no lookahead beyond one token. And every expression is followed
10//! by its type, so a constant is read with its type already in hand: `5` is an `INTEGER` or a
11//! `DATE` or the unscaled part of a `DECIMAL(6,2)` depending on what comes after it, and none of
12//! that has to be guessed from the digits.
13
14use std::ops::Range;
15
16use rudb_common::{Error, Field, LogicalType, Result, Value};
17
18use crate::expr::{Arm, ColumnBinding, CompareOp, ConjunctionOp, Expr, SortKey};
19use crate::node::{JoinKind, Node, SetOpKind};
20use crate::plan::Plan;
21use crate::{ExprRef, NodeRef, Slice};
22
23impl Plan {
24    /// Reads a plan back from its textual form.
25    ///
26    /// Printing the result produces the text that was read, which is a test in
27    /// `tests/roundtrip.rs` rather than a claim here. The plan is validated before it is returned,
28    /// so a text that parses is a text that names a plan somebody could have built.
29    ///
30    /// # Errors
31    ///
32    /// With the line number and the column, because the thing a person wants from a dump that will
33    /// not read back is which character of which operator.
34    pub fn parse(text: &str) -> Result<Self> {
35        let lines = split_lines(text)?;
36        if lines.is_empty() {
37            return Err(Error::parser("a plan has at least one operator".to_string()));
38        }
39        let mut reader = Reader { lines, at: 0, plan: Self::without_nodes() };
40        let root = reader.node(0)?;
41        if let Some(line) = reader.lines.get(reader.at) {
42            return Err(Error::parser(format!(
43                "line {}: \"{}\" is past the end of the plan",
44                line.number, line.text
45            )));
46        }
47        let mut plan = reader.plan;
48        plan.set_root(root);
49        plan.validate()?;
50        Ok(plan)
51    }
52}
53
54/// One operator line, with its blank lines and its indentation already dealt with.
55#[derive(Debug)]
56struct Line<'a> {
57    /// How many levels in, which is half the leading spaces.
58    depth: usize,
59    /// The line with the indentation removed.
60    text: &'a str,
61    /// The line number in the original text, one based, for error messages.
62    number: usize,
63}
64
65fn split_lines(text: &str) -> Result<Vec<Line<'_>>> {
66    let mut lines = Vec::new();
67    for (index, raw) in text.lines().enumerate() {
68        let number = index + 1;
69        if raw.trim().is_empty() {
70            continue;
71        }
72        if raw.contains('\t') {
73            return Err(Error::parser(format!("line {number}: indented with a tab")));
74        }
75        let spaces = raw.len() - raw.trim_start_matches(' ').len();
76        if spaces % 2 != 0 {
77            return Err(Error::parser(format!(
78                "line {number}: indented {spaces} spaces, which is not a whole number of levels"
79            )));
80        }
81        lines.push(Line { depth: spaces / 2, text: raw[spaces..].trim_end(), number });
82    }
83    Ok(lines)
84}
85
86struct Reader<'a> {
87    lines: Vec<Line<'a>>,
88    at: usize,
89    plan: Plan,
90}
91
92impl Reader<'_> {
93    /// Reads the operator at `depth` and everything under it.
94    ///
95    /// Children are read before the node is built, which is not a stylistic choice: the arena's
96    /// backwards-reference rule says a child index is smaller than its parent's, and building the
97    /// parent first would break it on every plan with more than one operator.
98    fn node(&mut self, depth: usize) -> Result<NodeRef> {
99        let Some(line) = self.lines.get(self.at) else {
100            return Err(Error::parser(format!("expected an operator {depth} levels in")));
101        };
102        if line.depth != depth {
103            return Err(Error::parser(format!(
104                "line {}: \"{}\" is {} levels in and {depth} was expected",
105                line.number, line.text, line.depth
106            )));
107        }
108        let number = line.number;
109        let text = line.text;
110        self.at += 1;
111
112        let (keyword, arguments) = match text.find(' ') {
113            Some(space) => (&text[..space], &text[space..]),
114            None => (text, ""),
115        };
116        let mut cursor = Cursor::new(arguments);
117        let built = self
118            .arguments(keyword, &mut cursor)
119            .map_err(|error| Error::parser(format!("line {number}: {}", error.message())))?;
120        if !cursor.done() {
121            return Err(Error::parser(format!(
122                "line {number}: \"{}\" is left over after the {keyword}",
123                cursor.rest()
124            )));
125        }
126
127        let left = if built.arity > 0 { Some(self.node(depth + 1)?) } else { None };
128        let right = if built.arity > 1 { Some(self.node(depth + 1)?) } else { None };
129        Ok(self.plan.add_node((built.assemble)(left.unwrap_or(0), right.unwrap_or(0))))
130    }
131
132    /// Reads one operator's arguments, returning how many inputs it takes and how to build it once
133    /// they have been read.
134    fn arguments(&mut self, keyword: &str, c: &mut Cursor<'_>) -> Result<Built> {
135        let plan = &mut self.plan;
136        match keyword {
137            "Dummy" => Ok(Built::leaf(Node::Dummy)),
138            "CrossProduct" => Ok(Built {
139                arity: 2,
140                assemble: Box::new(|left, right| Node::CrossProduct { left, right }),
141            }),
142            "Get" => {
143                let catalog = read_name(plan, c)?;
144                c.expect(".")?;
145                let schema = read_name(plan, c)?;
146                c.expect(".")?;
147                let table = read_name(plan, c)?;
148                c.expect_word("AS")?;
149                let alias = read_name(plan, c)?;
150                let index = read_table_index(c)?;
151                let columns = read_schema(plan, c)?;
152                Ok(Built::leaf(Node::Get { catalog, schema, table, alias, index, columns }))
153            }
154            "Values" => {
155                let index = read_table_index(c)?;
156                let columns = read_schema(plan, c)?;
157                c.expect_word("rows")?;
158                c.expect("=")?;
159                c.expect("[")?;
160                let mut rows = Vec::new();
161                if !c.eat_space_then("]") {
162                    loop {
163                        rows.push(read_expr_list(plan, c)?);
164                        if !c.eat_space_then(",") {
165                            break;
166                        }
167                    }
168                    c.expect("]")?;
169                }
170                let rows = plan.add_rows(&rows);
171                Ok(Built::leaf(Node::Values { index, columns, rows }))
172            }
173            "TableFunction" => {
174                let function = read_name(plan, c)?;
175                c.expect_word("args")?;
176                c.expect("=")?;
177                let args = read_expr_list(plan, c)?;
178                let (options, settings) = read_options(plan, c)?;
179                let index = read_table_index(c)?;
180                let columns = read_schema(plan, c)?;
181                Ok(Built::leaf(Node::TableFunction {
182                    index,
183                    function,
184                    args,
185                    options,
186                    settings,
187                    columns,
188                }))
189            }
190            "Fetch" => {
191                c.expect_word("args")?;
192                c.expect("=")?;
193                let args = read_expr_list(plan, c)?;
194                c.expect_word("row")?;
195                c.expect("=")?;
196                let row = read_expr(plan, c)?;
197                let index = read_table_index(c)?;
198                let columns = read_schema(plan, c)?;
199                Ok(Built::unary(move |input| Node::Fetch { input, index, args, columns, row }))
200            }
201            "TableFetch" => {
202                let catalog = read_name(plan, c)?;
203                c.expect(".")?;
204                let schema = read_name(plan, c)?;
205                c.expect(".")?;
206                let table = read_name(plan, c)?;
207                c.expect_word("row")?;
208                c.expect("=")?;
209                let row = read_expr(plan, c)?;
210                let index = read_table_index(c)?;
211                let columns = read_schema(plan, c)?;
212                Ok(Built::unary(move |input| Node::TableFetch {
213                    input,
214                    index,
215                    catalog,
216                    schema,
217                    table,
218                    columns,
219                    row,
220                }))
221            }
222            "Filter" => {
223                let predicate = read_expr(plan, c)?;
224                Ok(Built::unary(move |input| Node::Filter { input, predicate }))
225            }
226            "Project" => {
227                let index = read_table_index(c)?;
228                let mut exprs = Vec::new();
229                let mut names = Vec::new();
230                c.expect("[")?;
231                if !c.eat_space_then("]") {
232                    loop {
233                        exprs.push(read_expr(plan, c)?);
234                        c.expect_word("AS")?;
235                        names.push(read_name(plan, c)?);
236                        if !c.eat_space_then(",") {
237                            break;
238                        }
239                    }
240                    c.expect("]")?;
241                }
242                let exprs = plan.add_expr_list(&exprs);
243                let names = plan.add_name_list(&names);
244                Ok(Built::unary(move |input| Node::Project { input, index, exprs, names }))
245            }
246            "Aggregate" => {
247                let index = read_table_index(c)?;
248                c.expect_word("groups")?;
249                c.expect("=")?;
250                let groups = read_expr_list(plan, c)?;
251                c.expect_word("aggregates")?;
252                c.expect("=")?;
253                let aggregates = read_aggregate_list(plan, c)?;
254                Ok(Built::unary(move |input| Node::Aggregate { input, index, groups, aggregates }))
255            }
256            "Sort" => {
257                let keys = read_sort_keys(plan, c)?;
258                Ok(Built::unary(move |input| Node::Sort { input, keys }))
259            }
260            "Limit" => {
261                let count = if c.eat_word("ALL") { None } else { Some(read_count(c)?) };
262                c.expect_word("offset")?;
263                let offset = read_count(c)?;
264                Ok(Built::unary(move |input| Node::Limit { input, count, offset }))
265            }
266            "TopN" => {
267                let count = read_count(c)?;
268                c.expect_word("offset")?;
269                let offset = read_count(c)?;
270                let keys = read_sort_keys(plan, c)?;
271                Ok(Built::unary(move |input| Node::TopN { input, keys, count, offset }))
272            }
273            "Distinct" => {
274                c.expect_word("on")?;
275                c.expect("=")?;
276                let on = read_expr_list(plan, c)?;
277                Ok(Built::unary(move |input| Node::Distinct { input, on }))
278            }
279            "Join" => {
280                let kind = read_keyword(c, &JoinKind::ALL, JoinKind::keyword, "a join kind")?;
281                c.expect_word("on")?;
282                c.expect("=")?;
283                let conditions = read_expr_list(plan, c)?;
284                Ok(Built {
285                    arity: 2,
286                    assemble: Box::new(move |left, right| Node::Join {
287                        left,
288                        right,
289                        kind,
290                        conditions,
291                    }),
292                })
293            }
294            "SetOp" => {
295                let kind = read_keyword(c, &SetOpKind::ALL, SetOpKind::keyword, "a set operation")?;
296                let all = if c.eat_word("ALL") {
297                    true
298                } else if c.eat_word("DISTINCT") {
299                    false
300                } else {
301                    return Err(c.error("expected ALL or DISTINCT"));
302                };
303                let index = read_table_index(c)?;
304                Ok(Built {
305                    arity: 2,
306                    assemble: Box::new(move |left, right| Node::SetOp {
307                        left,
308                        right,
309                        kind,
310                        all,
311                        index,
312                    }),
313                })
314            }
315            other => Err(Error::parser(format!("\"{other}\" is not an operator"))),
316        }
317    }
318}
319
320/// An operator whose arguments have been read and whose inputs have not.
321struct Built {
322    arity: usize,
323    assemble: Box<dyn FnOnce(NodeRef, NodeRef) -> Node>,
324}
325
326impl Built {
327    fn leaf(node: Node) -> Self {
328        Self { arity: 0, assemble: Box::new(move |_, _| node) }
329    }
330
331    fn unary(make: impl FnOnce(NodeRef) -> Node + 'static) -> Self {
332        Self { arity: 1, assemble: Box::new(move |input, _| make(input)) }
333    }
334}
335
336// Node arguments.
337
338fn read_table_index(c: &mut Cursor<'_>) -> Result<u32> {
339    c.expect("#")?;
340    read_number(c)
341}
342
343fn read_count(c: &mut Cursor<'_>) -> Result<u64> {
344    c.skip_space();
345    let start = c.at;
346    while c.peek().is_some_and(|ch| ch.is_ascii_digit()) {
347        c.at += 1;
348    }
349    if c.at == start {
350        return Err(c.error("expected a row count"));
351    }
352    c.text[start..c.at].parse().map_err(|_| c.error("that row count does not fit in 64 bits"))
353}
354
355fn read_number(c: &mut Cursor<'_>) -> Result<u32> {
356    c.skip_space();
357    let start = c.at;
358    while c.peek().is_some_and(|ch| ch.is_ascii_digit()) {
359        c.at += 1;
360    }
361    if c.at == start {
362        return Err(c.error("expected a number"));
363    }
364    c.text[start..c.at].parse().map_err(|_| c.error("that number does not fit in 32 bits"))
365}
366
367/// One of a fixed set of keywords, matched longest first so that a keyword which is a prefix of
368/// another cannot shadow it.
369fn read_keyword<T: Copy>(
370    c: &mut Cursor<'_>,
371    all: &[T],
372    spell: impl Fn(T) -> &'static str,
373    what: &str,
374) -> Result<T> {
375    let mut candidates: Vec<T> = all.to_vec();
376    candidates.sort_by_key(|&kind| std::cmp::Reverse(spell(kind).len()));
377    for candidate in candidates {
378        if c.eat_word(spell(candidate)) {
379            return Ok(candidate);
380        }
381    }
382    Err(c.error(&format!("expected {what}")))
383}
384
385fn read_name(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<u32> {
386    let name = read_identifier(c)?;
387    Ok(plan.intern(&name))
388}
389
390/// A named and typed column list, which is what a scan and a `VALUES` produce.
391fn read_schema(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Slice> {
392    let mut fields = Vec::new();
393    c.expect("[")?;
394    if !c.eat_space_then("]") {
395        loop {
396            let name = read_identifier(c)?;
397            c.expect("::")?;
398            let ty = read_type(c)?;
399            fields.push(Field::new(name, ty));
400            if !c.eat_space_then(",") {
401                break;
402            }
403        }
404        c.expect("]")?;
405    }
406    Ok(plan.add_fields(&fields))
407}
408
409/// The named parameters a table function call was written with, which most calls have none of.
410///
411/// Nothing is written when there are none, so the whole segment is optional and its absence is two
412/// empty slices rather than an error.
413fn read_options(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<(Slice, Slice)> {
414    if !c.eat_space_then("options=[") {
415        return Ok((Slice::EMPTY, Slice::EMPTY));
416    }
417    let mut names = Vec::new();
418    let mut settings = Vec::new();
419    if !c.eat_space_then("]") {
420        loop {
421            names.push(read_name(plan, c)?);
422            c.expect("=")?;
423            settings.push(read_expr(plan, c)?);
424            if !c.eat_space_then(",") {
425                break;
426            }
427        }
428        c.expect("]")?;
429    }
430    Ok((plan.add_name_list(&names), plan.add_expr_list(&settings)))
431}
432
433fn read_expr_list(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Slice> {
434    let mut exprs = Vec::new();
435    c.expect("[")?;
436    if !c.eat_space_then("]") {
437        loop {
438            exprs.push(read_expr(plan, c)?);
439            if !c.eat_space_then(",") {
440                break;
441            }
442        }
443        c.expect("]")?;
444    }
445    Ok(plan.add_expr_list(&exprs))
446}
447
448/// The aggregate list of an `Aggregate`, which is the only place an aggregate can appear.
449///
450/// An aggregate and a scalar function print identically, so this is not a different syntax, it is
451/// the same syntax read in the one slot where it means something else. Reading it anywhere else
452/// would produce a plan `Plan::validate` rejects, which is the check that keeps the two in step.
453fn read_aggregate_list(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Slice> {
454    let mut exprs = Vec::new();
455    c.expect("[")?;
456    if !c.eat_space_then("]") {
457        loop {
458            exprs.push(read_aggregate(plan, c)?);
459            if !c.eat_space_then(",") {
460                break;
461            }
462        }
463        c.expect("]")?;
464    }
465    Ok(plan.add_expr_list(&exprs))
466}
467
468fn read_aggregate(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<ExprRef> {
469    c.skip_space();
470    let Some(name) = try_call_name(c) else {
471        return Err(c.error("expected an aggregate call"));
472    };
473    let name = plan.intern(&name);
474    let distinct = c.eat_keyword_before_argument("DISTINCT");
475    let mut args = Vec::new();
476    let mut filter = None;
477    if !c.eat_space_then(")") {
478        // `count(*) FILTER (WHERE p)` binds to a zero argument aggregate with a filter, so the
479        // filter has to be reachable without going through the argument loop first.
480        let mut filtered = c.eat_keyword_before_argument("FILTER");
481        if !filtered {
482            loop {
483                args.push(read_expr(plan, c)?);
484                if !c.eat_space_then(",") {
485                    break;
486                }
487            }
488            filtered = c.eat_keyword_before_argument("FILTER");
489        }
490        if filtered {
491            filter = Some(read_expr(plan, c)?);
492        }
493        c.expect(")")?;
494    }
495    let args = plan.add_expr_list(&args);
496    let ty = read_annotation(c)?;
497    Ok(plan.add_expr(Expr::Aggregate { name, args, distinct, filter }, ty))
498}
499
500/// A bracketed list of sort keys, which is what a sort and a top N both carry.
501fn read_sort_keys(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Slice> {
502    let mut keys = Vec::new();
503    c.expect("[")?;
504    if !c.eat_space_then("]") {
505        loop {
506            keys.push(read_sort_key(plan, c)?);
507            if !c.eat_space_then(",") {
508                break;
509            }
510        }
511        c.expect("]")?;
512    }
513    Ok(plan.add_sort_keys(&keys))
514}
515
516fn read_sort_key(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<SortKey> {
517    let expr = read_expr(plan, c)?;
518    let descending = if c.eat_word("DESC") {
519        true
520    } else if c.eat_word("ASC") {
521        false
522    } else {
523        return Err(c.error("expected ASC or DESC"));
524    };
525    c.expect_word("NULLS")?;
526    let nulls_first = if c.eat_word("FIRST") {
527        true
528    } else if c.eat_word("LAST") {
529        false
530    } else {
531        return Err(c.error("expected FIRST or LAST"));
532    };
533    Ok(SortKey { expr, descending, nulls_first })
534}
535
536// Expressions.
537
538/// What an expression's form turned out to be.
539///
540/// A constant cannot be finished until its type has been read, because the type is what says
541/// whether `5` is an integer, a day number or the unscaled part of a decimal. Everything else is
542/// finished by the time the type arrives.
543enum Form {
544    Done(Expr),
545    Constant(Range<usize>),
546}
547
548fn read_expr(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<ExprRef> {
549    let form = read_form(plan, c)?;
550    let ty = read_annotation(c)?;
551    let expr = match form {
552        Form::Done(expr) => expr,
553        Form::Constant(span) => {
554            let text = &c.text[span];
555            let value = read_value(text, &ty)
556                .map_err(|error| c.error(&format!("{text} is not a {ty}: {}", error.message())))?;
557            Expr::Constant(plan.add_value(value))
558        }
559    };
560    Ok(plan.add_expr(expr, ty))
561}
562
563fn read_annotation(c: &mut Cursor<'_>) -> Result<LogicalType> {
564    c.expect("::")?;
565    read_type(c)
566}
567
568fn read_type(c: &mut Cursor<'_>) -> Result<LogicalType> {
569    c.skip_space();
570    let end = type_extent(c.text, c.at);
571    if end == c.at {
572        return Err(c.error("expected a type"));
573    }
574    let text = &c.text[c.at..end];
575    c.at = end;
576    LogicalType::parse(text)
577}
578
579fn read_form(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Form> {
580    c.skip_space();
581    if c.eat("#") {
582        let table = read_number(c)?;
583        c.expect(".")?;
584        let column = read_number(c)?;
585        return Ok(Form::Done(Expr::Column(ColumnBinding::new(table, column))));
586    }
587    if c.eat("(") {
588        return read_bracketed(plan, c).map(Form::Done);
589    }
590    if c.eat_word("CASE") {
591        return read_case(plan, c).map(Form::Done);
592    }
593    for (word, try_cast) in [("TRY_CAST", true), ("CAST", false)] {
594        if c.eat_word(word) {
595            c.expect("(")?;
596            let input = read_expr(plan, c)?;
597            c.expect(")")?;
598            return Ok(Form::Done(Expr::Cast { input, try_cast }));
599        }
600    }
601    if let Some(name) = try_call_name(c) {
602        let name = plan.intern(&name);
603        let mut args = Vec::new();
604        if !c.eat_space_then(")") {
605            loop {
606                args.push(read_expr(plan, c)?);
607                if !c.eat_space_then(",") {
608                    break;
609                }
610            }
611            c.expect(")")?;
612        }
613        let args = plan.add_expr_list(&args);
614        return Ok(Form::Done(Expr::Function { name, args }));
615    }
616
617    let end = literal_extent(c.text, c.at);
618    if end == c.at {
619        return Err(c.error("expected an expression"));
620    }
621    let span = c.at..end;
622    c.at = end;
623    Ok(Form::Constant(span))
624}
625
626/// A comparison or a conjunction, with the opening parenthesis already eaten.
627///
628/// Which of the two it is only becomes clear after the first operand, since both start the same
629/// way. That is one token of lookahead over an operand that has already been parsed, which is why
630/// there is no backtracking here.
631fn read_bracketed(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Expr> {
632    let left = read_expr(plan, c)?;
633    for op in [ConjunctionOp::And, ConjunctionOp::Or] {
634        if c.eat_word(op.keyword()) {
635            let mut children = vec![left];
636            loop {
637                children.push(read_expr(plan, c)?);
638                if !c.eat_word(op.keyword()) {
639                    break;
640                }
641            }
642            c.expect(")")?;
643            let children = plan.add_expr_list(&children);
644            return Ok(Expr::Conjunction { op, children });
645        }
646    }
647    c.skip_space();
648    let found = CompareOp::SPELLINGS.into_iter().find(|op| c.eat(op.symbol()));
649    let Some(op) = found else {
650        return Err(c.error("expected a comparison, AND or OR"));
651    };
652    let right = read_expr(plan, c)?;
653    c.expect(")")?;
654    Ok(Expr::Compare { op, left, right })
655}
656
657fn read_case(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Expr> {
658    let mut arms = Vec::new();
659    while c.eat_word("WHEN") {
660        let when = read_expr(plan, c)?;
661        c.expect_word("THEN")?;
662        let then = read_expr(plan, c)?;
663        arms.push(Arm { when, then });
664    }
665    let otherwise = if c.eat_word("ELSE") { Some(read_expr(plan, c)?) } else { None };
666    c.expect_word("END")?;
667    let arms = plan.add_arms(&arms);
668    Ok(Expr::Case { arms, otherwise })
669}
670
671/// A function name followed immediately by its opening parenthesis, or nothing consumed.
672///
673/// The parenthesis has to be adjacent, which is what separates a call from the bare words `NULL`,
674/// `TRUE` and `FALSE`. It is also why a function called `cast` is printed quoted: an unquoted
675/// `CAST(` is syntax and is checked before this runs, and `"cast"(` reaches here.
676fn try_call_name(c: &mut Cursor<'_>) -> Option<String> {
677    let start = c.at;
678    let Ok(name) = read_identifier(c) else {
679        c.at = start;
680        return None;
681    };
682    if c.peek() == Some('(') {
683        c.at += 1;
684        Some(name)
685    } else {
686        c.at = start;
687        None
688    }
689}
690
691fn read_identifier(c: &mut Cursor<'_>) -> Result<String> {
692    c.skip_space();
693    if c.eat("\"") {
694        let mut name = String::new();
695        loop {
696            let Some(character) = c.peek() else {
697                return Err(c.error("a quoted name is not closed"));
698            };
699            c.at += character.len_utf8();
700            if character == '"' {
701                if c.peek() == Some('"') {
702                    name.push('"');
703                    c.at += 1;
704                    continue;
705                }
706                break;
707            }
708            name.push(character);
709        }
710        return Ok(name);
711    }
712    let start = c.at;
713    while c.peek().is_some_and(|ch| ch.is_ascii_alphanumeric() || ch == '_') {
714        c.at += 1;
715    }
716    if c.at == start {
717        return Err(c.error("expected a name"));
718    }
719    Ok(c.text[start..c.at].to_string())
720}
721
722// Constants, read with their type already known.
723
724fn read_value(text: &str, ty: &LogicalType) -> Result<Value> {
725    let text = text.trim();
726    if text.eq_ignore_ascii_case("NULL") {
727        return Ok(Value::Null);
728    }
729    let whole = |what: &str| Error::parser(format!("{text} is not {what}"));
730    match ty {
731        LogicalType::Boolean => match text {
732            "TRUE" => Ok(Value::Boolean(true)),
733            "FALSE" => Ok(Value::Boolean(false)),
734            _ => Err(whole("TRUE or FALSE")),
735        },
736        LogicalType::TinyInt => text.parse().map(Value::TinyInt).map_err(|_| whole("a TINYINT")),
737        LogicalType::SmallInt => text.parse().map(Value::SmallInt).map_err(|_| whole("a SMALLINT")),
738        LogicalType::Integer => text.parse().map(Value::Integer).map_err(|_| whole("an INTEGER")),
739        LogicalType::BigInt => text.parse().map(Value::BigInt).map_err(|_| whole("a BIGINT")),
740        LogicalType::HugeInt => text.parse().map(Value::HugeInt).map_err(|_| whole("a HUGEINT")),
741        LogicalType::UTinyInt => text.parse().map(Value::UTinyInt).map_err(|_| whole("a UTINYINT")),
742        LogicalType::USmallInt => {
743            text.parse().map(Value::USmallInt).map_err(|_| whole("a USMALLINT"))
744        }
745        LogicalType::UInteger => text.parse().map(Value::UInteger).map_err(|_| whole("a UINTEGER")),
746        LogicalType::UBigInt => text.parse().map(Value::UBigInt).map_err(|_| whole("a UBIGINT")),
747        LogicalType::UHugeInt => text.parse().map(Value::UHugeInt).map_err(|_| whole("a UHUGEINT")),
748        LogicalType::Float => text.parse().map(Value::Float).map_err(|_| whole("a FLOAT")),
749        LogicalType::Double => text.parse().map(Value::Double).map_err(|_| whole("a DOUBLE")),
750        LogicalType::Decimal { width, scale } => read_decimal(text, *width, *scale),
751        LogicalType::Varchar => read_string(text).map(Value::Varchar),
752        LogicalType::Blob => read_blob(text).map(Value::Blob),
753        LogicalType::Date => text.parse().map(Value::Date).map_err(|_| whole("a day number")),
754        LogicalType::Time => {
755            text.parse().map(Value::Time).map_err(|_| whole("a microsecond count"))
756        }
757        LogicalType::TimeTz => {
758            text.parse().map(Value::TimeTz).map_err(|_| whole("a microsecond count"))
759        }
760        LogicalType::Timestamp => {
761            text.parse().map(Value::Timestamp).map_err(|_| whole("a microsecond count"))
762        }
763        LogicalType::TimestampTz => {
764            text.parse().map(Value::TimestampTz).map_err(|_| whole("a microsecond count"))
765        }
766        LogicalType::Interval => {
767            let parts = read_braced(text)?;
768            let [months, days, micros] = parts.as_slice() else {
769                return Err(whole("an interval, which is three numbers in braces"));
770            };
771            Ok(Value::Interval {
772                months: months.trim().parse().map_err(|_| whole("an interval"))?,
773                days: days.trim().parse().map_err(|_| whole("an interval"))?,
774                micros: micros.trim().parse().map_err(|_| whole("an interval"))?,
775            })
776        }
777        LogicalType::List(element) => {
778            let values = read_braced(text)?
779                .into_iter()
780                .map(|part| read_value(part, element))
781                .collect::<Result<Vec<_>>>()?;
782            Ok(Value::List { element: element.as_ref().clone(), values })
783        }
784        LogicalType::Struct(fields) => {
785            let parts = read_braced(text)?;
786            if parts.len() != fields.len() {
787                return Err(whole(&format!("a struct of {} fields", fields.len())));
788            }
789            let mut held = Vec::with_capacity(parts.len());
790            for (part, field) in parts.into_iter().zip(fields) {
791                held.push((field.name.clone(), read_value(part, &field.ty)?));
792            }
793            Ok(Value::Struct(held))
794        }
795        // Everything left is a type rudb_common::Value cannot hold, so a constant of that type
796        // cannot have been printed and cannot be built here either. A typed null of one of them is
797        // fine and was handled above.
798        other => Err(Error::not_implemented(format!(
799            "a constant of type {other} has no value representation yet"
800        ))),
801    }
802}
803
804fn read_decimal(text: &str, width: u8, scale: u8) -> Result<Value> {
805    let bad = || Error::parser(format!("{text} is not a DECIMAL({width},{scale})"));
806    let negative = text.starts_with('-');
807    let body = text.strip_prefix(['-', '+']).unwrap_or(text);
808    let digits = if scale == 0 {
809        if body.contains('.') {
810            return Err(bad());
811        }
812        body.to_string()
813    } else {
814        let (whole, fraction) = body.split_once('.').ok_or_else(bad)?;
815        if fraction.len() != usize::from(scale) {
816            return Err(bad());
817        }
818        format!("{whole}{fraction}")
819    };
820    if digits.is_empty() || !digits.bytes().all(|byte| byte.is_ascii_digit()) {
821        return Err(bad());
822    }
823    let signed = if negative { format!("-{digits}") } else { digits };
824    let unscaled: i128 = signed.parse().map_err(|_| bad())?;
825    Ok(Value::Decimal { unscaled, width, scale })
826}
827
828fn read_string(text: &str) -> Result<String> {
829    let inner = text
830        .strip_prefix('\'')
831        .and_then(|rest| rest.strip_suffix('\''))
832        .ok_or_else(|| Error::parser(format!("{text} is not a quoted string")))?;
833    let mut out = String::new();
834    let mut characters = inner.chars();
835    while let Some(character) = characters.next() {
836        match character {
837            '\'' => {
838                if characters.next() != Some('\'') {
839                    return Err(Error::parser(format!("{text} has a quote that is not doubled")));
840                }
841                out.push('\'');
842            }
843            '\\' => match characters.next() {
844                Some('\\') => out.push('\\'),
845                Some('x') => {
846                    let high = characters.next().unwrap_or(' ');
847                    let low = characters.next().unwrap_or(' ');
848                    let code = u8::from_str_radix(&format!("{high}{low}"), 16)
849                        .map_err(|_| Error::parser(format!("{text} has a bad escape")))?;
850                    out.push(char::from(code));
851                }
852                _ => return Err(Error::parser(format!("{text} has a bad escape"))),
853            },
854            other => out.push(other),
855        }
856    }
857    Ok(out)
858}
859
860fn read_blob(text: &str) -> Result<Vec<u8>> {
861    let bad = || Error::parser(format!("{text} is not a hex blob"));
862    let inner = text
863        .strip_prefix(['X', 'x'])
864        .and_then(|rest| rest.strip_prefix('\''))
865        .and_then(|rest| rest.strip_suffix('\''))
866        .ok_or_else(bad)?;
867    if inner.len() % 2 != 0 {
868        return Err(bad());
869    }
870    let mut bytes = Vec::with_capacity(inner.len() / 2);
871    for pair in inner.as_bytes().chunks(2) {
872        let pair = std::str::from_utf8(pair).map_err(|_| bad())?;
873        bytes.push(u8::from_str_radix(pair, 16).map_err(|_| bad())?);
874    }
875    Ok(bytes)
876}
877
878/// The comma separated parts of a braced list, at brace depth one.
879fn read_braced(text: &str) -> Result<Vec<&str>> {
880    let inner = text
881        .strip_prefix('{')
882        .and_then(|rest| rest.strip_suffix('}'))
883        .ok_or_else(|| Error::parser(format!("{text} is not a braced list")))?;
884    if inner.trim().is_empty() {
885        return Ok(Vec::new());
886    }
887    let mut parts = Vec::new();
888    let mut depth = 0usize;
889    let mut start = 0usize;
890    let bytes = inner.as_bytes();
891    let mut at = 0usize;
892    while at < bytes.len() {
893        match bytes[at] {
894            b'\'' => at = skip_string(inner, at) - 1,
895            b'{' => depth += 1,
896            b'}' => depth = depth.saturating_sub(1),
897            b',' if depth == 0 => {
898                parts.push(inner[start..at].trim());
899                start = at + 1;
900            }
901            _ => {}
902        }
903        at += 1;
904    }
905    parts.push(inner[start..].trim());
906    Ok(parts)
907}
908
909// Extents. Both of these answer the same question from two directions: how much of this text
910// belongs to the thing that starts here. They are byte scans rather than parses because the answer
911// is handed to a real parser afterwards, and a second grammar that has to agree with the first is
912// a second grammar that will not.
913
914/// How far a type annotation starting at `from` runs.
915///
916/// A type is a name, then an optional balanced parenthesis group, then optionally `WITH TIME ZONE`,
917/// then any number of balanced bracket groups. The time zone suffix comes before the brackets and
918/// not after, because a list of them prints as `TIME WITH TIME ZONE[]`: the suffix belongs to the
919/// element type and the brackets are the list wrapped around it. Every type
920/// [`LogicalType`](rudb_common::LogicalType) prints fits that, which `print::prints_readably`
921/// asserts over the whole type set.
922pub(crate) fn type_extent(text: &str, from: usize) -> usize {
923    let bytes = text.as_bytes();
924    let mut at = if bytes.get(from) == Some(&b'"') {
925        skip_quoted_name(text, from)
926    } else {
927        let mut at = from;
928        while at < bytes.len() && (bytes[at].is_ascii_alphanumeric() || bytes[at] == b'_') {
929            at += 1;
930        }
931        at
932    };
933    at = balanced(text, at, b'(', b')');
934    for suffix in [" WITH TIME ZONE", " WITHOUT TIME ZONE"] {
935        if starts_with_ignoring_case(&text[at..], suffix) {
936            at += suffix.len();
937            break;
938        }
939    }
940    loop {
941        let next = balanced(text, at, b'[', b']');
942        if next == at {
943            break;
944        }
945        at = next;
946    }
947    at
948}
949
950/// How far a constant starting at `from` runs, which is up to the `::` that types it.
951///
952/// Braces nest and single quotes hide everything inside them, so a string holding a colon pair and
953/// a list of structs both come out in one piece.
954fn literal_extent(text: &str, from: usize) -> usize {
955    let bytes = text.as_bytes();
956    let mut at = from;
957    let mut depth = 0usize;
958    while at < bytes.len() {
959        match bytes[at] {
960            b'\'' => {
961                at = skip_string(text, at);
962                continue;
963            }
964            b'{' => depth += 1,
965            b'}' => depth = depth.saturating_sub(1),
966            b':' if depth == 0 && bytes.get(at + 1) == Some(&b':') => return at,
967            _ => {}
968        }
969        at += 1;
970    }
971    at
972}
973
974/// Past a balanced group starting at `at`, or `at` unchanged if no group starts there.
975fn balanced(text: &str, at: usize, open: u8, close: u8) -> usize {
976    let bytes = text.as_bytes();
977    if bytes.get(at) != Some(&open) {
978        return at;
979    }
980    let mut depth = 0usize;
981    let mut here = at;
982    while here < bytes.len() {
983        match bytes[here] {
984            b'"' => {
985                here = skip_quoted_name(text, here);
986                continue;
987            }
988            b'\'' => {
989                here = skip_string(text, here);
990                continue;
991            }
992            byte if byte == open => depth += 1,
993            byte if byte == close => {
994                depth -= 1;
995                if depth == 0 {
996                    return here + 1;
997                }
998            }
999            _ => {}
1000        }
1001        here += 1;
1002    }
1003    // Unbalanced. Handing the rest of the line to the type parser gets a message naming the text
1004    // that is wrong, which is more useful than one naming the character where counting stopped.
1005    text.len()
1006}
1007
1008/// Past a single quoted string starting at `at`, doubled quotes included.
1009fn skip_string(text: &str, at: usize) -> usize {
1010    let bytes = text.as_bytes();
1011    let mut here = at + 1;
1012    while here < bytes.len() {
1013        if bytes[here] == b'\'' {
1014            if bytes.get(here + 1) == Some(&b'\'') {
1015                here += 2;
1016                continue;
1017            }
1018            return here + 1;
1019        }
1020        here += 1;
1021    }
1022    text.len()
1023}
1024
1025/// Past a double quoted name starting at `at`, doubled quotes included.
1026fn skip_quoted_name(text: &str, at: usize) -> usize {
1027    let bytes = text.as_bytes();
1028    let mut here = at + 1;
1029    while here < bytes.len() {
1030        if bytes[here] == b'"' {
1031            if bytes.get(here + 1) == Some(&b'"') {
1032                here += 2;
1033                continue;
1034            }
1035            return here + 1;
1036        }
1037        here += 1;
1038    }
1039    text.len()
1040}
1041
1042fn starts_with_ignoring_case(text: &str, prefix: &str) -> bool {
1043    text.len() >= prefix.len()
1044        && text.as_bytes()[..prefix.len()].eq_ignore_ascii_case(prefix.as_bytes())
1045}
1046
1047/// A position in one operator's argument text.
1048struct Cursor<'a> {
1049    text: &'a str,
1050    at: usize,
1051}
1052
1053impl<'a> Cursor<'a> {
1054    fn new(text: &'a str) -> Self {
1055        Self { text, at: 0 }
1056    }
1057
1058    fn rest(&self) -> &'a str {
1059        &self.text[self.at..]
1060    }
1061
1062    fn peek(&self) -> Option<char> {
1063        self.rest().chars().next()
1064    }
1065
1066    fn skip_space(&mut self) {
1067        while self.rest().starts_with(' ') {
1068            self.at += 1;
1069        }
1070    }
1071
1072    fn eat(&mut self, token: &str) -> bool {
1073        if self.rest().starts_with(token) {
1074            self.at += token.len();
1075            true
1076        } else {
1077            false
1078        }
1079    }
1080
1081    /// Eats a keyword, case insensitively, only if a whole word is there.
1082    ///
1083    /// Without the boundary check `ALL` would match the front of a column called `ALLOWED` and the
1084    /// error would land on whatever came after it.
1085    fn eat_word(&mut self, word: &str) -> bool {
1086        let start = self.at;
1087        self.skip_space();
1088        if !starts_with_ignoring_case(self.rest(), word) {
1089            self.at = start;
1090            return false;
1091        }
1092        let after = self.text[self.at + word.len()..].chars().next();
1093        if after.is_some_and(|ch| ch.is_ascii_alphanumeric() || ch == '_') {
1094            self.at = start;
1095            return false;
1096        }
1097        self.at += word.len();
1098        true
1099    }
1100
1101    /// Eats a keyword that stands where an argument could stand.
1102    ///
1103    /// `DISTINCT` and `FILTER` sit just inside an aggregate's parentheses, which is exactly where a
1104    /// call to a function of the same name could be, and [`Cursor::eat_word`] would take
1105    /// `filter(x)` for the keyword followed by a parenthesised expression. The printed form always
1106    /// puts a space after these two and a call always has its parenthesis hard against the name,
1107    /// so requiring the space is what tells them apart.
1108    fn eat_keyword_before_argument(&mut self, word: &str) -> bool {
1109        let start = self.at;
1110        self.skip_space();
1111        if starts_with_ignoring_case(self.rest(), word)
1112            && self.text[self.at + word.len()..].starts_with(' ')
1113        {
1114            self.at += word.len();
1115            return true;
1116        }
1117        self.at = start;
1118        false
1119    }
1120
1121    /// Eats a token after any spaces, leaving the cursor alone if the token is not there.
1122    fn eat_space_then(&mut self, token: &str) -> bool {
1123        let start = self.at;
1124        self.skip_space();
1125        if self.eat(token) {
1126            true
1127        } else {
1128            self.at = start;
1129            false
1130        }
1131    }
1132
1133    fn expect(&mut self, token: &str) -> Result<()> {
1134        if self.eat_space_then(token) {
1135            Ok(())
1136        } else {
1137            Err(self.error(&format!("expected \"{token}\"")))
1138        }
1139    }
1140
1141    fn expect_word(&mut self, word: &str) -> Result<()> {
1142        if self.eat_word(word) { Ok(()) } else { Err(self.error(&format!("expected \"{word}\""))) }
1143    }
1144
1145    fn done(&mut self) -> bool {
1146        self.skip_space();
1147        self.at >= self.text.len()
1148    }
1149
1150    fn error(&self, what: &str) -> Error {
1151        Error::parser(format!("{what} at column {}, reading \"{}\"", self.at + 1, self.text.trim()))
1152    }
1153}
1154
1155#[cfg(test)]
1156mod tests {
1157    use super::*;
1158    use crate::print::{RESERVED, is_plain_identifier};
1159
1160    #[test]
1161    fn a_type_annotation_ends_where_the_type_does() {
1162        for (text, expected) in [
1163            ("INTEGER DESC", "INTEGER"),
1164            ("INTEGER, x", "INTEGER"),
1165            ("INTEGER)", "INTEGER"),
1166            ("DECIMAL(18,3) AS a", "DECIMAL(18,3)"),
1167            ("INTEGER[] AS a", "INTEGER[]"),
1168            ("INTEGER[3][] ", "INTEGER[3][]"),
1169            ("MAP(VARCHAR, INTEGER)[]", "MAP(VARCHAR, INTEGER)[]"),
1170            ("STRUCT(a INTEGER, b VARCHAR) AS s", "STRUCT(a INTEGER, b VARCHAR)"),
1171            ("TIMESTAMP WITH TIME ZONE, x", "TIMESTAMP WITH TIME ZONE"),
1172            ("TIME WITH TIME ZONE]", "TIME WITH TIME ZONE"),
1173            ("TIME WITH TIME ZONE[], x", "TIME WITH TIME ZONE[]"),
1174            ("TIMESTAMP WITH TIME ZONE[4] AS t", "TIMESTAMP WITH TIME ZONE[4]"),
1175            ("TIMESTAMP DESC", "TIMESTAMP"),
1176            ("\"NULL\" AS n", "\"NULL\""),
1177        ] {
1178            let end = type_extent(text, 0);
1179            assert_eq!(&text[..end], expected, "wrong extent in {text}");
1180            LogicalType::parse(expected)
1181                .unwrap_or_else(|e| panic!("{expected} does not parse: {e}"));
1182        }
1183    }
1184
1185    /// A field name inside a struct that needs quoting can hold a bracket, and the extent scanner
1186    /// counting it would end the type in the middle of itself.
1187    #[test]
1188    fn a_quoted_field_name_does_not_end_the_type_early() {
1189        let text = "STRUCT(\"a)b\" INTEGER) AS s";
1190        let end = type_extent(text, 0);
1191        assert_eq!(&text[..end], "STRUCT(\"a)b\" INTEGER)");
1192    }
1193
1194    #[test]
1195    fn a_constant_ends_at_the_colon_pair_that_types_it() {
1196        for (text, expected) in [
1197            ("5::INTEGER", "5"),
1198            ("-5::INTEGER", "-5"),
1199            ("''::VARCHAR", "''"),
1200            ("'a::b'::VARCHAR", "'a::b'"),
1201            ("'it''s'::VARCHAR", "'it''s'"),
1202            ("{1, 2}::INTEGER[]", "{1, 2}"),
1203            ("{{1}, {2}}::INTEGER[][]", "{{1}, {2}}"),
1204            ("X'00ff'::BLOB", "X'00ff'"),
1205            ("NULL::VARCHAR", "NULL"),
1206        ] {
1207            let end = literal_extent(text, 0);
1208            assert_eq!(&text[..end], expected, "wrong extent in {text}");
1209        }
1210    }
1211
1212    #[test]
1213    fn a_decimal_reads_back_at_its_own_scale() {
1214        assert_eq!(
1215            read_decimal("12.34", 6, 2).unwrap(),
1216            Value::Decimal { unscaled: 1234, width: 6, scale: 2 }
1217        );
1218        assert_eq!(
1219            read_decimal("-0.005", 6, 3).unwrap(),
1220            Value::Decimal { unscaled: -5, width: 6, scale: 3 }
1221        );
1222        assert_eq!(
1223            read_decimal("1234", 6, 0).unwrap(),
1224            Value::Decimal { unscaled: 1234, width: 6, scale: 0 }
1225        );
1226    }
1227
1228    /// The digits after the point have to be exactly the scale. `1.5::DECIMAL(6,2)` is a hundred
1229    /// times off from `1.50::DECIMAL(6,2)` and there is no reading of it that is obviously right,
1230    /// so it is rejected rather than guessed at.
1231    #[test]
1232    fn a_decimal_with_the_wrong_number_of_digits_is_rejected() {
1233        assert!(read_decimal("1.5", 6, 2).is_err());
1234        assert!(read_decimal("1.500", 6, 2).is_err());
1235        assert!(read_decimal("15", 6, 2).is_err());
1236        assert!(read_decimal("1.5", 6, 0).is_err());
1237    }
1238
1239    #[test]
1240    fn a_string_gives_its_quotes_and_escapes_back() {
1241        assert_eq!(read_string("''").unwrap(), "");
1242        assert_eq!(read_string("'it''s'").unwrap(), "it's");
1243        assert_eq!(read_string("'a\\\\b'").unwrap(), "a\\b");
1244        assert_eq!(read_string("'one\\x0atwo'").unwrap(), "one\ntwo");
1245        assert!(read_string("'").is_err());
1246        assert!(read_string("no quotes").is_err());
1247    }
1248
1249    #[test]
1250    fn a_blob_is_pairs_of_hex_digits() {
1251        assert_eq!(read_blob("X''").unwrap(), Vec::<u8>::new());
1252        assert_eq!(read_blob("X'00ff10'").unwrap(), vec![0x00, 0xff, 0x10]);
1253        assert!(read_blob("X'0'").is_err(), "an odd number of digits is not a byte string");
1254        assert!(read_blob("X'zz'").is_err());
1255    }
1256
1257    #[test]
1258    fn a_braced_list_splits_at_the_top_level_only() {
1259        assert_eq!(read_braced("{}").unwrap(), Vec::<&str>::new());
1260        assert_eq!(read_braced("{1}").unwrap(), vec!["1"]);
1261        assert_eq!(read_braced("{1, 2}").unwrap(), vec!["1", "2"]);
1262        assert_eq!(read_braced("{{1, 2}, {3}}").unwrap(), vec!["{1, 2}", "{3}"]);
1263        assert_eq!(read_braced("{'a,b', 'c'}").unwrap(), vec!["'a,b'", "'c'"]);
1264        assert!(read_braced("1, 2").is_err());
1265    }
1266
1267    #[test]
1268    fn a_keyword_needs_a_word_boundary() {
1269        let mut c = Cursor::new(" ALLOWED");
1270        assert!(!c.eat_word("ALL"), "ALL should not match the front of ALLOWED");
1271        assert!(c.eat_word("ALLOWED"));
1272        let mut c = Cursor::new(" all ");
1273        assert!(c.eat_word("ALL"), "keywords are case insensitive");
1274    }
1275
1276    #[test]
1277    fn an_odd_indent_is_an_error_and_says_so() {
1278        let message = Plan::parse("Filter x\n   Dummy\n").unwrap_err().to_string();
1279        assert!(message.contains("whole number of levels"), "unhelpful message: {message}");
1280    }
1281
1282    #[test]
1283    fn a_tab_is_an_error_rather_than_a_guess() {
1284        let message = Plan::parse("Filter x\n\tDummy\n").unwrap_err().to_string();
1285        assert!(message.contains("tab"), "unhelpful message: {message}");
1286    }
1287
1288    #[test]
1289    fn an_unknown_operator_names_itself() {
1290        let message = Plan::parse("Frobnicate\n").unwrap_err().to_string();
1291        assert!(message.contains("Frobnicate"), "unhelpful message: {message}");
1292    }
1293
1294    #[test]
1295    fn a_missing_child_is_an_error_rather_than_a_plan_with_a_hole() {
1296        let message = Plan::parse("Filter TRUE::BOOLEAN\n").unwrap_err().to_string();
1297        assert!(message.contains("expected an operator"), "unhelpful message: {message}");
1298    }
1299
1300    #[test]
1301    fn a_second_root_is_an_error() {
1302        let message = Plan::parse("Dummy\nDummy\n").unwrap_err().to_string();
1303        assert!(message.contains("past the end"), "unhelpful message: {message}");
1304    }
1305
1306    #[test]
1307    fn text_left_over_on_a_line_is_an_error() {
1308        let message = Plan::parse("Dummy nonsense\n").unwrap_err().to_string();
1309        assert!(message.contains("left over"), "unhelpful message: {message}");
1310    }
1311
1312    #[test]
1313    fn an_empty_plan_is_not_a_plan() {
1314        assert!(Plan::parse("").is_err());
1315        assert!(Plan::parse("\n\n  \n").is_err());
1316    }
1317
1318    /// The reader has to reject what the validator rejects, or a hand-written dump becomes a way
1319    /// to build a plan nobody could build through the arena.
1320    #[test]
1321    fn the_reader_runs_the_plan_invariant() {
1322        let message = Plan::parse("Filter 1::INTEGER\n  Dummy\n").unwrap_err().to_string();
1323        assert!(message.contains("BOOLEAN"), "unhelpful message: {message}");
1324    }
1325
1326    /// The printer quotes a function whose name is a reserved word. If that quoting did not buy a
1327    /// different reading here it would be quoting for nothing, and a function called `cast` would
1328    /// come back as a cast with the wrong number of operands.
1329    #[test]
1330    fn a_quoted_reserved_word_is_a_function_and_not_syntax() {
1331        for reserved in RESERVED {
1332            let text = format!("Project #1 [\"{reserved}\"(1::INTEGER)::INTEGER AS a]\n  Dummy\n");
1333            let plan = Plan::parse(&text).unwrap_or_else(|e| panic!("{text} does not read: {e}"));
1334            assert!(matches!(plan.expr(0), Expr::Constant(_)));
1335            assert!(matches!(plan.expr(1), Expr::Function { .. }), "{reserved} became syntax");
1336            assert_eq!(plan.to_string(), text);
1337        }
1338    }
1339
1340    #[test]
1341    fn an_unquoted_cast_is_syntax_and_not_a_function() {
1342        let text = "Project #1 [CAST(1::INTEGER)::BIGINT AS a]\n  Dummy\n";
1343        let plan = Plan::parse(text).expect("a cast reads back");
1344        assert!(matches!(plan.expr(1), Expr::Cast { try_cast: false, .. }));
1345        assert_eq!(plan.to_string(), text);
1346    }
1347
1348    /// `FILTER` and `DISTINCT` stand where an argument could stand, so a function of the same name
1349    /// in argument position is the case that tells whether the keyword check is precise enough.
1350    #[test]
1351    fn a_function_called_filter_in_argument_position_is_a_call() {
1352        let text = "Aggregate #1 groups=[] aggregates=[sum(filter(1::INTEGER)::INTEGER)::HUGEINT]\n  Dummy\n";
1353        let plan = Plan::parse(text).expect("an argument called filter reads back");
1354        assert_eq!(plan.to_string(), text);
1355    }
1356
1357    #[test]
1358    fn an_aggregate_with_no_arguments_can_still_have_a_filter() {
1359        let text = "Aggregate #1 groups=[] aggregates=[count_star(FILTER TRUE::BOOLEAN)::BIGINT]\n  Dummy\n";
1360        let plan = Plan::parse(text).expect("count(*) FILTER (WHERE p) is a real query");
1361        assert_eq!(plan.to_string(), text);
1362    }
1363
1364    #[test]
1365    fn a_name_that_is_not_a_plain_identifier_is_quoted_by_the_printer_and_read_here() {
1366        let mut c = Cursor::new("\"say \"\"hi\"\"\" rest");
1367        assert_eq!(read_identifier(&mut c).unwrap(), "say \"hi\"");
1368        assert_eq!(c.rest(), " rest");
1369        assert!(!is_plain_identifier("say \"hi\""));
1370    }
1371}