1use 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 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#[derive(Debug)]
56struct Line<'a> {
57 depth: usize,
59 text: &'a str,
61 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 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 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 "Filter" => {
191 let predicate = read_expr(plan, c)?;
192 Ok(Built::unary(move |input| Node::Filter { input, predicate }))
193 }
194 "Project" => {
195 let index = read_table_index(c)?;
196 let mut exprs = Vec::new();
197 let mut names = Vec::new();
198 c.expect("[")?;
199 if !c.eat_space_then("]") {
200 loop {
201 exprs.push(read_expr(plan, c)?);
202 c.expect_word("AS")?;
203 names.push(read_name(plan, c)?);
204 if !c.eat_space_then(",") {
205 break;
206 }
207 }
208 c.expect("]")?;
209 }
210 let exprs = plan.add_expr_list(&exprs);
211 let names = plan.add_name_list(&names);
212 Ok(Built::unary(move |input| Node::Project { input, index, exprs, names }))
213 }
214 "Aggregate" => {
215 let index = read_table_index(c)?;
216 c.expect_word("groups")?;
217 c.expect("=")?;
218 let groups = read_expr_list(plan, c)?;
219 c.expect_word("aggregates")?;
220 c.expect("=")?;
221 let aggregates = read_aggregate_list(plan, c)?;
222 Ok(Built::unary(move |input| Node::Aggregate { input, index, groups, aggregates }))
223 }
224 "Sort" => {
225 let keys = read_sort_keys(plan, c)?;
226 Ok(Built::unary(move |input| Node::Sort { input, keys }))
227 }
228 "Limit" => {
229 let count = if c.eat_word("ALL") { None } else { Some(read_count(c)?) };
230 c.expect_word("offset")?;
231 let offset = read_count(c)?;
232 Ok(Built::unary(move |input| Node::Limit { input, count, offset }))
233 }
234 "TopN" => {
235 let count = read_count(c)?;
236 c.expect_word("offset")?;
237 let offset = read_count(c)?;
238 let keys = read_sort_keys(plan, c)?;
239 Ok(Built::unary(move |input| Node::TopN { input, keys, count, offset }))
240 }
241 "Distinct" => {
242 c.expect_word("on")?;
243 c.expect("=")?;
244 let on = read_expr_list(plan, c)?;
245 Ok(Built::unary(move |input| Node::Distinct { input, on }))
246 }
247 "Join" => {
248 let kind = read_keyword(c, &JoinKind::ALL, JoinKind::keyword, "a join kind")?;
249 c.expect_word("on")?;
250 c.expect("=")?;
251 let conditions = read_expr_list(plan, c)?;
252 Ok(Built {
253 arity: 2,
254 assemble: Box::new(move |left, right| Node::Join {
255 left,
256 right,
257 kind,
258 conditions,
259 }),
260 })
261 }
262 "SetOp" => {
263 let kind = read_keyword(c, &SetOpKind::ALL, SetOpKind::keyword, "a set operation")?;
264 let all = if c.eat_word("ALL") {
265 true
266 } else if c.eat_word("DISTINCT") {
267 false
268 } else {
269 return Err(c.error("expected ALL or DISTINCT"));
270 };
271 let index = read_table_index(c)?;
272 Ok(Built {
273 arity: 2,
274 assemble: Box::new(move |left, right| Node::SetOp {
275 left,
276 right,
277 kind,
278 all,
279 index,
280 }),
281 })
282 }
283 other => Err(Error::parser(format!("\"{other}\" is not an operator"))),
284 }
285 }
286}
287
288struct Built {
290 arity: usize,
291 assemble: Box<dyn FnOnce(NodeRef, NodeRef) -> Node>,
292}
293
294impl Built {
295 fn leaf(node: Node) -> Self {
296 Self { arity: 0, assemble: Box::new(move |_, _| node) }
297 }
298
299 fn unary(make: impl FnOnce(NodeRef) -> Node + 'static) -> Self {
300 Self { arity: 1, assemble: Box::new(move |input, _| make(input)) }
301 }
302}
303
304fn read_table_index(c: &mut Cursor<'_>) -> Result<u32> {
307 c.expect("#")?;
308 read_number(c)
309}
310
311fn read_count(c: &mut Cursor<'_>) -> Result<u64> {
312 c.skip_space();
313 let start = c.at;
314 while c.peek().is_some_and(|ch| ch.is_ascii_digit()) {
315 c.at += 1;
316 }
317 if c.at == start {
318 return Err(c.error("expected a row count"));
319 }
320 c.text[start..c.at].parse().map_err(|_| c.error("that row count does not fit in 64 bits"))
321}
322
323fn read_number(c: &mut Cursor<'_>) -> Result<u32> {
324 c.skip_space();
325 let start = c.at;
326 while c.peek().is_some_and(|ch| ch.is_ascii_digit()) {
327 c.at += 1;
328 }
329 if c.at == start {
330 return Err(c.error("expected a number"));
331 }
332 c.text[start..c.at].parse().map_err(|_| c.error("that number does not fit in 32 bits"))
333}
334
335fn read_keyword<T: Copy>(
338 c: &mut Cursor<'_>,
339 all: &[T],
340 spell: impl Fn(T) -> &'static str,
341 what: &str,
342) -> Result<T> {
343 let mut candidates: Vec<T> = all.to_vec();
344 candidates.sort_by_key(|&kind| std::cmp::Reverse(spell(kind).len()));
345 for candidate in candidates {
346 if c.eat_word(spell(candidate)) {
347 return Ok(candidate);
348 }
349 }
350 Err(c.error(&format!("expected {what}")))
351}
352
353fn read_name(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<u32> {
354 let name = read_identifier(c)?;
355 Ok(plan.intern(&name))
356}
357
358fn read_schema(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Slice> {
360 let mut fields = Vec::new();
361 c.expect("[")?;
362 if !c.eat_space_then("]") {
363 loop {
364 let name = read_identifier(c)?;
365 c.expect("::")?;
366 let ty = read_type(c)?;
367 fields.push(Field::new(name, ty));
368 if !c.eat_space_then(",") {
369 break;
370 }
371 }
372 c.expect("]")?;
373 }
374 Ok(plan.add_fields(&fields))
375}
376
377fn read_options(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<(Slice, Slice)> {
382 if !c.eat_space_then("options=[") {
383 return Ok((Slice::EMPTY, Slice::EMPTY));
384 }
385 let mut names = Vec::new();
386 let mut settings = Vec::new();
387 if !c.eat_space_then("]") {
388 loop {
389 names.push(read_name(plan, c)?);
390 c.expect("=")?;
391 settings.push(read_expr(plan, c)?);
392 if !c.eat_space_then(",") {
393 break;
394 }
395 }
396 c.expect("]")?;
397 }
398 Ok((plan.add_name_list(&names), plan.add_expr_list(&settings)))
399}
400
401fn read_expr_list(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Slice> {
402 let mut exprs = Vec::new();
403 c.expect("[")?;
404 if !c.eat_space_then("]") {
405 loop {
406 exprs.push(read_expr(plan, c)?);
407 if !c.eat_space_then(",") {
408 break;
409 }
410 }
411 c.expect("]")?;
412 }
413 Ok(plan.add_expr_list(&exprs))
414}
415
416fn read_aggregate_list(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Slice> {
422 let mut exprs = Vec::new();
423 c.expect("[")?;
424 if !c.eat_space_then("]") {
425 loop {
426 exprs.push(read_aggregate(plan, c)?);
427 if !c.eat_space_then(",") {
428 break;
429 }
430 }
431 c.expect("]")?;
432 }
433 Ok(plan.add_expr_list(&exprs))
434}
435
436fn read_aggregate(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<ExprRef> {
437 c.skip_space();
438 let Some(name) = try_call_name(c) else {
439 return Err(c.error("expected an aggregate call"));
440 };
441 let name = plan.intern(&name);
442 let distinct = c.eat_keyword_before_argument("DISTINCT");
443 let mut args = Vec::new();
444 let mut filter = None;
445 if !c.eat_space_then(")") {
446 let mut filtered = c.eat_keyword_before_argument("FILTER");
449 if !filtered {
450 loop {
451 args.push(read_expr(plan, c)?);
452 if !c.eat_space_then(",") {
453 break;
454 }
455 }
456 filtered = c.eat_keyword_before_argument("FILTER");
457 }
458 if filtered {
459 filter = Some(read_expr(plan, c)?);
460 }
461 c.expect(")")?;
462 }
463 let args = plan.add_expr_list(&args);
464 let ty = read_annotation(c)?;
465 Ok(plan.add_expr(Expr::Aggregate { name, args, distinct, filter }, ty))
466}
467
468fn read_sort_keys(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Slice> {
470 let mut keys = Vec::new();
471 c.expect("[")?;
472 if !c.eat_space_then("]") {
473 loop {
474 keys.push(read_sort_key(plan, c)?);
475 if !c.eat_space_then(",") {
476 break;
477 }
478 }
479 c.expect("]")?;
480 }
481 Ok(plan.add_sort_keys(&keys))
482}
483
484fn read_sort_key(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<SortKey> {
485 let expr = read_expr(plan, c)?;
486 let descending = if c.eat_word("DESC") {
487 true
488 } else if c.eat_word("ASC") {
489 false
490 } else {
491 return Err(c.error("expected ASC or DESC"));
492 };
493 c.expect_word("NULLS")?;
494 let nulls_first = if c.eat_word("FIRST") {
495 true
496 } else if c.eat_word("LAST") {
497 false
498 } else {
499 return Err(c.error("expected FIRST or LAST"));
500 };
501 Ok(SortKey { expr, descending, nulls_first })
502}
503
504enum Form {
512 Done(Expr),
513 Constant(Range<usize>),
514}
515
516fn read_expr(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<ExprRef> {
517 let form = read_form(plan, c)?;
518 let ty = read_annotation(c)?;
519 let expr = match form {
520 Form::Done(expr) => expr,
521 Form::Constant(span) => {
522 let text = &c.text[span];
523 let value = read_value(text, &ty)
524 .map_err(|error| c.error(&format!("{text} is not a {ty}: {}", error.message())))?;
525 Expr::Constant(plan.add_value(value))
526 }
527 };
528 Ok(plan.add_expr(expr, ty))
529}
530
531fn read_annotation(c: &mut Cursor<'_>) -> Result<LogicalType> {
532 c.expect("::")?;
533 read_type(c)
534}
535
536fn read_type(c: &mut Cursor<'_>) -> Result<LogicalType> {
537 c.skip_space();
538 let end = type_extent(c.text, c.at);
539 if end == c.at {
540 return Err(c.error("expected a type"));
541 }
542 let text = &c.text[c.at..end];
543 c.at = end;
544 LogicalType::parse(text)
545}
546
547fn read_form(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Form> {
548 c.skip_space();
549 if c.eat("#") {
550 let table = read_number(c)?;
551 c.expect(".")?;
552 let column = read_number(c)?;
553 return Ok(Form::Done(Expr::Column(ColumnBinding::new(table, column))));
554 }
555 if c.eat("(") {
556 return read_bracketed(plan, c).map(Form::Done);
557 }
558 if c.eat_word("CASE") {
559 return read_case(plan, c).map(Form::Done);
560 }
561 for (word, try_cast) in [("TRY_CAST", true), ("CAST", false)] {
562 if c.eat_word(word) {
563 c.expect("(")?;
564 let input = read_expr(plan, c)?;
565 c.expect(")")?;
566 return Ok(Form::Done(Expr::Cast { input, try_cast }));
567 }
568 }
569 if let Some(name) = try_call_name(c) {
570 let name = plan.intern(&name);
571 let mut args = Vec::new();
572 if !c.eat_space_then(")") {
573 loop {
574 args.push(read_expr(plan, c)?);
575 if !c.eat_space_then(",") {
576 break;
577 }
578 }
579 c.expect(")")?;
580 }
581 let args = plan.add_expr_list(&args);
582 return Ok(Form::Done(Expr::Function { name, args }));
583 }
584
585 let end = literal_extent(c.text, c.at);
586 if end == c.at {
587 return Err(c.error("expected an expression"));
588 }
589 let span = c.at..end;
590 c.at = end;
591 Ok(Form::Constant(span))
592}
593
594fn read_bracketed(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Expr> {
600 let left = read_expr(plan, c)?;
601 for op in [ConjunctionOp::And, ConjunctionOp::Or] {
602 if c.eat_word(op.keyword()) {
603 let mut children = vec![left];
604 loop {
605 children.push(read_expr(plan, c)?);
606 if !c.eat_word(op.keyword()) {
607 break;
608 }
609 }
610 c.expect(")")?;
611 let children = plan.add_expr_list(&children);
612 return Ok(Expr::Conjunction { op, children });
613 }
614 }
615 c.skip_space();
616 let found = CompareOp::SPELLINGS.into_iter().find(|op| c.eat(op.symbol()));
617 let Some(op) = found else {
618 return Err(c.error("expected a comparison, AND or OR"));
619 };
620 let right = read_expr(plan, c)?;
621 c.expect(")")?;
622 Ok(Expr::Compare { op, left, right })
623}
624
625fn read_case(plan: &mut Plan, c: &mut Cursor<'_>) -> Result<Expr> {
626 let mut arms = Vec::new();
627 while c.eat_word("WHEN") {
628 let when = read_expr(plan, c)?;
629 c.expect_word("THEN")?;
630 let then = read_expr(plan, c)?;
631 arms.push(Arm { when, then });
632 }
633 let otherwise = if c.eat_word("ELSE") { Some(read_expr(plan, c)?) } else { None };
634 c.expect_word("END")?;
635 let arms = plan.add_arms(&arms);
636 Ok(Expr::Case { arms, otherwise })
637}
638
639fn try_call_name(c: &mut Cursor<'_>) -> Option<String> {
645 let start = c.at;
646 let Ok(name) = read_identifier(c) else {
647 c.at = start;
648 return None;
649 };
650 if c.peek() == Some('(') {
651 c.at += 1;
652 Some(name)
653 } else {
654 c.at = start;
655 None
656 }
657}
658
659fn read_identifier(c: &mut Cursor<'_>) -> Result<String> {
660 c.skip_space();
661 if c.eat("\"") {
662 let mut name = String::new();
663 loop {
664 let Some(character) = c.peek() else {
665 return Err(c.error("a quoted name is not closed"));
666 };
667 c.at += character.len_utf8();
668 if character == '"' {
669 if c.peek() == Some('"') {
670 name.push('"');
671 c.at += 1;
672 continue;
673 }
674 break;
675 }
676 name.push(character);
677 }
678 return Ok(name);
679 }
680 let start = c.at;
681 while c.peek().is_some_and(|ch| ch.is_ascii_alphanumeric() || ch == '_') {
682 c.at += 1;
683 }
684 if c.at == start {
685 return Err(c.error("expected a name"));
686 }
687 Ok(c.text[start..c.at].to_string())
688}
689
690fn read_value(text: &str, ty: &LogicalType) -> Result<Value> {
693 let text = text.trim();
694 if text.eq_ignore_ascii_case("NULL") {
695 return Ok(Value::Null);
696 }
697 let whole = |what: &str| Error::parser(format!("{text} is not {what}"));
698 match ty {
699 LogicalType::Boolean => match text {
700 "TRUE" => Ok(Value::Boolean(true)),
701 "FALSE" => Ok(Value::Boolean(false)),
702 _ => Err(whole("TRUE or FALSE")),
703 },
704 LogicalType::TinyInt => text.parse().map(Value::TinyInt).map_err(|_| whole("a TINYINT")),
705 LogicalType::SmallInt => text.parse().map(Value::SmallInt).map_err(|_| whole("a SMALLINT")),
706 LogicalType::Integer => text.parse().map(Value::Integer).map_err(|_| whole("an INTEGER")),
707 LogicalType::BigInt => text.parse().map(Value::BigInt).map_err(|_| whole("a BIGINT")),
708 LogicalType::HugeInt => text.parse().map(Value::HugeInt).map_err(|_| whole("a HUGEINT")),
709 LogicalType::UTinyInt => text.parse().map(Value::UTinyInt).map_err(|_| whole("a UTINYINT")),
710 LogicalType::USmallInt => {
711 text.parse().map(Value::USmallInt).map_err(|_| whole("a USMALLINT"))
712 }
713 LogicalType::UInteger => text.parse().map(Value::UInteger).map_err(|_| whole("a UINTEGER")),
714 LogicalType::UBigInt => text.parse().map(Value::UBigInt).map_err(|_| whole("a UBIGINT")),
715 LogicalType::UHugeInt => text.parse().map(Value::UHugeInt).map_err(|_| whole("a UHUGEINT")),
716 LogicalType::Float => text.parse().map(Value::Float).map_err(|_| whole("a FLOAT")),
717 LogicalType::Double => text.parse().map(Value::Double).map_err(|_| whole("a DOUBLE")),
718 LogicalType::Decimal { width, scale } => read_decimal(text, *width, *scale),
719 LogicalType::Varchar => read_string(text).map(Value::Varchar),
720 LogicalType::Blob => read_blob(text).map(Value::Blob),
721 LogicalType::Date => text.parse().map(Value::Date).map_err(|_| whole("a day number")),
722 LogicalType::Time => {
723 text.parse().map(Value::Time).map_err(|_| whole("a microsecond count"))
724 }
725 LogicalType::Timestamp => {
726 text.parse().map(Value::Timestamp).map_err(|_| whole("a microsecond count"))
727 }
728 LogicalType::Interval => {
729 let parts = read_braced(text)?;
730 let [months, days, micros] = parts.as_slice() else {
731 return Err(whole("an interval, which is three numbers in braces"));
732 };
733 Ok(Value::Interval {
734 months: months.trim().parse().map_err(|_| whole("an interval"))?,
735 days: days.trim().parse().map_err(|_| whole("an interval"))?,
736 micros: micros.trim().parse().map_err(|_| whole("an interval"))?,
737 })
738 }
739 LogicalType::List(element) => {
740 let values = read_braced(text)?
741 .into_iter()
742 .map(|part| read_value(part, element))
743 .collect::<Result<Vec<_>>>()?;
744 Ok(Value::List { element: element.as_ref().clone(), values })
745 }
746 LogicalType::Struct(fields) => {
747 let parts = read_braced(text)?;
748 if parts.len() != fields.len() {
749 return Err(whole(&format!("a struct of {} fields", fields.len())));
750 }
751 let mut held = Vec::with_capacity(parts.len());
752 for (part, field) in parts.into_iter().zip(fields) {
753 held.push((field.name.clone(), read_value(part, &field.ty)?));
754 }
755 Ok(Value::Struct(held))
756 }
757 other => Err(Error::not_implemented(format!(
761 "a constant of type {other} has no value representation yet"
762 ))),
763 }
764}
765
766fn read_decimal(text: &str, width: u8, scale: u8) -> Result<Value> {
767 let bad = || Error::parser(format!("{text} is not a DECIMAL({width},{scale})"));
768 let negative = text.starts_with('-');
769 let body = text.strip_prefix(['-', '+']).unwrap_or(text);
770 let digits = if scale == 0 {
771 if body.contains('.') {
772 return Err(bad());
773 }
774 body.to_string()
775 } else {
776 let (whole, fraction) = body.split_once('.').ok_or_else(bad)?;
777 if fraction.len() != usize::from(scale) {
778 return Err(bad());
779 }
780 format!("{whole}{fraction}")
781 };
782 if digits.is_empty() || !digits.bytes().all(|byte| byte.is_ascii_digit()) {
783 return Err(bad());
784 }
785 let signed = if negative { format!("-{digits}") } else { digits };
786 let unscaled: i128 = signed.parse().map_err(|_| bad())?;
787 Ok(Value::Decimal { unscaled, width, scale })
788}
789
790fn read_string(text: &str) -> Result<String> {
791 let inner = text
792 .strip_prefix('\'')
793 .and_then(|rest| rest.strip_suffix('\''))
794 .ok_or_else(|| Error::parser(format!("{text} is not a quoted string")))?;
795 let mut out = String::new();
796 let mut characters = inner.chars();
797 while let Some(character) = characters.next() {
798 match character {
799 '\'' => {
800 if characters.next() != Some('\'') {
801 return Err(Error::parser(format!("{text} has a quote that is not doubled")));
802 }
803 out.push('\'');
804 }
805 '\\' => match characters.next() {
806 Some('\\') => out.push('\\'),
807 Some('x') => {
808 let high = characters.next().unwrap_or(' ');
809 let low = characters.next().unwrap_or(' ');
810 let code = u8::from_str_radix(&format!("{high}{low}"), 16)
811 .map_err(|_| Error::parser(format!("{text} has a bad escape")))?;
812 out.push(char::from(code));
813 }
814 _ => return Err(Error::parser(format!("{text} has a bad escape"))),
815 },
816 other => out.push(other),
817 }
818 }
819 Ok(out)
820}
821
822fn read_blob(text: &str) -> Result<Vec<u8>> {
823 let bad = || Error::parser(format!("{text} is not a hex blob"));
824 let inner = text
825 .strip_prefix(['X', 'x'])
826 .and_then(|rest| rest.strip_prefix('\''))
827 .and_then(|rest| rest.strip_suffix('\''))
828 .ok_or_else(bad)?;
829 if inner.len() % 2 != 0 {
830 return Err(bad());
831 }
832 let mut bytes = Vec::with_capacity(inner.len() / 2);
833 for pair in inner.as_bytes().chunks(2) {
834 let pair = std::str::from_utf8(pair).map_err(|_| bad())?;
835 bytes.push(u8::from_str_radix(pair, 16).map_err(|_| bad())?);
836 }
837 Ok(bytes)
838}
839
840fn read_braced(text: &str) -> Result<Vec<&str>> {
842 let inner = text
843 .strip_prefix('{')
844 .and_then(|rest| rest.strip_suffix('}'))
845 .ok_or_else(|| Error::parser(format!("{text} is not a braced list")))?;
846 if inner.trim().is_empty() {
847 return Ok(Vec::new());
848 }
849 let mut parts = Vec::new();
850 let mut depth = 0usize;
851 let mut start = 0usize;
852 let bytes = inner.as_bytes();
853 let mut at = 0usize;
854 while at < bytes.len() {
855 match bytes[at] {
856 b'\'' => at = skip_string(inner, at) - 1,
857 b'{' => depth += 1,
858 b'}' => depth = depth.saturating_sub(1),
859 b',' if depth == 0 => {
860 parts.push(inner[start..at].trim());
861 start = at + 1;
862 }
863 _ => {}
864 }
865 at += 1;
866 }
867 parts.push(inner[start..].trim());
868 Ok(parts)
869}
870
871pub(crate) fn type_extent(text: &str, from: usize) -> usize {
885 let bytes = text.as_bytes();
886 let mut at = if bytes.get(from) == Some(&b'"') {
887 skip_quoted_name(text, from)
888 } else {
889 let mut at = from;
890 while at < bytes.len() && (bytes[at].is_ascii_alphanumeric() || bytes[at] == b'_') {
891 at += 1;
892 }
893 at
894 };
895 at = balanced(text, at, b'(', b')');
896 for suffix in [" WITH TIME ZONE", " WITHOUT TIME ZONE"] {
897 if starts_with_ignoring_case(&text[at..], suffix) {
898 at += suffix.len();
899 break;
900 }
901 }
902 loop {
903 let next = balanced(text, at, b'[', b']');
904 if next == at {
905 break;
906 }
907 at = next;
908 }
909 at
910}
911
912fn literal_extent(text: &str, from: usize) -> usize {
917 let bytes = text.as_bytes();
918 let mut at = from;
919 let mut depth = 0usize;
920 while at < bytes.len() {
921 match bytes[at] {
922 b'\'' => {
923 at = skip_string(text, at);
924 continue;
925 }
926 b'{' => depth += 1,
927 b'}' => depth = depth.saturating_sub(1),
928 b':' if depth == 0 && bytes.get(at + 1) == Some(&b':') => return at,
929 _ => {}
930 }
931 at += 1;
932 }
933 at
934}
935
936fn balanced(text: &str, at: usize, open: u8, close: u8) -> usize {
938 let bytes = text.as_bytes();
939 if bytes.get(at) != Some(&open) {
940 return at;
941 }
942 let mut depth = 0usize;
943 let mut here = at;
944 while here < bytes.len() {
945 match bytes[here] {
946 b'"' => {
947 here = skip_quoted_name(text, here);
948 continue;
949 }
950 b'\'' => {
951 here = skip_string(text, here);
952 continue;
953 }
954 byte if byte == open => depth += 1,
955 byte if byte == close => {
956 depth -= 1;
957 if depth == 0 {
958 return here + 1;
959 }
960 }
961 _ => {}
962 }
963 here += 1;
964 }
965 text.len()
968}
969
970fn skip_string(text: &str, at: usize) -> usize {
972 let bytes = text.as_bytes();
973 let mut here = at + 1;
974 while here < bytes.len() {
975 if bytes[here] == b'\'' {
976 if bytes.get(here + 1) == Some(&b'\'') {
977 here += 2;
978 continue;
979 }
980 return here + 1;
981 }
982 here += 1;
983 }
984 text.len()
985}
986
987fn skip_quoted_name(text: &str, at: usize) -> usize {
989 let bytes = text.as_bytes();
990 let mut here = at + 1;
991 while here < bytes.len() {
992 if bytes[here] == b'"' {
993 if bytes.get(here + 1) == Some(&b'"') {
994 here += 2;
995 continue;
996 }
997 return here + 1;
998 }
999 here += 1;
1000 }
1001 text.len()
1002}
1003
1004fn starts_with_ignoring_case(text: &str, prefix: &str) -> bool {
1005 text.len() >= prefix.len()
1006 && text.as_bytes()[..prefix.len()].eq_ignore_ascii_case(prefix.as_bytes())
1007}
1008
1009struct Cursor<'a> {
1011 text: &'a str,
1012 at: usize,
1013}
1014
1015impl<'a> Cursor<'a> {
1016 fn new(text: &'a str) -> Self {
1017 Self { text, at: 0 }
1018 }
1019
1020 fn rest(&self) -> &'a str {
1021 &self.text[self.at..]
1022 }
1023
1024 fn peek(&self) -> Option<char> {
1025 self.rest().chars().next()
1026 }
1027
1028 fn skip_space(&mut self) {
1029 while self.rest().starts_with(' ') {
1030 self.at += 1;
1031 }
1032 }
1033
1034 fn eat(&mut self, token: &str) -> bool {
1035 if self.rest().starts_with(token) {
1036 self.at += token.len();
1037 true
1038 } else {
1039 false
1040 }
1041 }
1042
1043 fn eat_word(&mut self, word: &str) -> bool {
1048 let start = self.at;
1049 self.skip_space();
1050 if !starts_with_ignoring_case(self.rest(), word) {
1051 self.at = start;
1052 return false;
1053 }
1054 let after = self.text[self.at + word.len()..].chars().next();
1055 if after.is_some_and(|ch| ch.is_ascii_alphanumeric() || ch == '_') {
1056 self.at = start;
1057 return false;
1058 }
1059 self.at += word.len();
1060 true
1061 }
1062
1063 fn eat_keyword_before_argument(&mut self, word: &str) -> bool {
1071 let start = self.at;
1072 self.skip_space();
1073 if starts_with_ignoring_case(self.rest(), word)
1074 && self.text[self.at + word.len()..].starts_with(' ')
1075 {
1076 self.at += word.len();
1077 return true;
1078 }
1079 self.at = start;
1080 false
1081 }
1082
1083 fn eat_space_then(&mut self, token: &str) -> bool {
1085 let start = self.at;
1086 self.skip_space();
1087 if self.eat(token) {
1088 true
1089 } else {
1090 self.at = start;
1091 false
1092 }
1093 }
1094
1095 fn expect(&mut self, token: &str) -> Result<()> {
1096 if self.eat_space_then(token) {
1097 Ok(())
1098 } else {
1099 Err(self.error(&format!("expected \"{token}\"")))
1100 }
1101 }
1102
1103 fn expect_word(&mut self, word: &str) -> Result<()> {
1104 if self.eat_word(word) { Ok(()) } else { Err(self.error(&format!("expected \"{word}\""))) }
1105 }
1106
1107 fn done(&mut self) -> bool {
1108 self.skip_space();
1109 self.at >= self.text.len()
1110 }
1111
1112 fn error(&self, what: &str) -> Error {
1113 Error::parser(format!("{what} at column {}, reading \"{}\"", self.at + 1, self.text.trim()))
1114 }
1115}
1116
1117#[cfg(test)]
1118mod tests {
1119 use super::*;
1120 use crate::print::{RESERVED, is_plain_identifier};
1121
1122 #[test]
1123 fn a_type_annotation_ends_where_the_type_does() {
1124 for (text, expected) in [
1125 ("INTEGER DESC", "INTEGER"),
1126 ("INTEGER, x", "INTEGER"),
1127 ("INTEGER)", "INTEGER"),
1128 ("DECIMAL(18,3) AS a", "DECIMAL(18,3)"),
1129 ("INTEGER[] AS a", "INTEGER[]"),
1130 ("INTEGER[3][] ", "INTEGER[3][]"),
1131 ("MAP(VARCHAR, INTEGER)[]", "MAP(VARCHAR, INTEGER)[]"),
1132 ("STRUCT(a INTEGER, b VARCHAR) AS s", "STRUCT(a INTEGER, b VARCHAR)"),
1133 ("TIMESTAMP WITH TIME ZONE, x", "TIMESTAMP WITH TIME ZONE"),
1134 ("TIME WITH TIME ZONE]", "TIME WITH TIME ZONE"),
1135 ("TIME WITH TIME ZONE[], x", "TIME WITH TIME ZONE[]"),
1136 ("TIMESTAMP WITH TIME ZONE[4] AS t", "TIMESTAMP WITH TIME ZONE[4]"),
1137 ("TIMESTAMP DESC", "TIMESTAMP"),
1138 ("\"NULL\" AS n", "\"NULL\""),
1139 ] {
1140 let end = type_extent(text, 0);
1141 assert_eq!(&text[..end], expected, "wrong extent in {text}");
1142 LogicalType::parse(expected)
1143 .unwrap_or_else(|e| panic!("{expected} does not parse: {e}"));
1144 }
1145 }
1146
1147 #[test]
1150 fn a_quoted_field_name_does_not_end_the_type_early() {
1151 let text = "STRUCT(\"a)b\" INTEGER) AS s";
1152 let end = type_extent(text, 0);
1153 assert_eq!(&text[..end], "STRUCT(\"a)b\" INTEGER)");
1154 }
1155
1156 #[test]
1157 fn a_constant_ends_at_the_colon_pair_that_types_it() {
1158 for (text, expected) in [
1159 ("5::INTEGER", "5"),
1160 ("-5::INTEGER", "-5"),
1161 ("''::VARCHAR", "''"),
1162 ("'a::b'::VARCHAR", "'a::b'"),
1163 ("'it''s'::VARCHAR", "'it''s'"),
1164 ("{1, 2}::INTEGER[]", "{1, 2}"),
1165 ("{{1}, {2}}::INTEGER[][]", "{{1}, {2}}"),
1166 ("X'00ff'::BLOB", "X'00ff'"),
1167 ("NULL::VARCHAR", "NULL"),
1168 ] {
1169 let end = literal_extent(text, 0);
1170 assert_eq!(&text[..end], expected, "wrong extent in {text}");
1171 }
1172 }
1173
1174 #[test]
1175 fn a_decimal_reads_back_at_its_own_scale() {
1176 assert_eq!(
1177 read_decimal("12.34", 6, 2).unwrap(),
1178 Value::Decimal { unscaled: 1234, width: 6, scale: 2 }
1179 );
1180 assert_eq!(
1181 read_decimal("-0.005", 6, 3).unwrap(),
1182 Value::Decimal { unscaled: -5, width: 6, scale: 3 }
1183 );
1184 assert_eq!(
1185 read_decimal("1234", 6, 0).unwrap(),
1186 Value::Decimal { unscaled: 1234, width: 6, scale: 0 }
1187 );
1188 }
1189
1190 #[test]
1194 fn a_decimal_with_the_wrong_number_of_digits_is_rejected() {
1195 assert!(read_decimal("1.5", 6, 2).is_err());
1196 assert!(read_decimal("1.500", 6, 2).is_err());
1197 assert!(read_decimal("15", 6, 2).is_err());
1198 assert!(read_decimal("1.5", 6, 0).is_err());
1199 }
1200
1201 #[test]
1202 fn a_string_gives_its_quotes_and_escapes_back() {
1203 assert_eq!(read_string("''").unwrap(), "");
1204 assert_eq!(read_string("'it''s'").unwrap(), "it's");
1205 assert_eq!(read_string("'a\\\\b'").unwrap(), "a\\b");
1206 assert_eq!(read_string("'one\\x0atwo'").unwrap(), "one\ntwo");
1207 assert!(read_string("'").is_err());
1208 assert!(read_string("no quotes").is_err());
1209 }
1210
1211 #[test]
1212 fn a_blob_is_pairs_of_hex_digits() {
1213 assert_eq!(read_blob("X''").unwrap(), Vec::<u8>::new());
1214 assert_eq!(read_blob("X'00ff10'").unwrap(), vec![0x00, 0xff, 0x10]);
1215 assert!(read_blob("X'0'").is_err(), "an odd number of digits is not a byte string");
1216 assert!(read_blob("X'zz'").is_err());
1217 }
1218
1219 #[test]
1220 fn a_braced_list_splits_at_the_top_level_only() {
1221 assert_eq!(read_braced("{}").unwrap(), Vec::<&str>::new());
1222 assert_eq!(read_braced("{1}").unwrap(), vec!["1"]);
1223 assert_eq!(read_braced("{1, 2}").unwrap(), vec!["1", "2"]);
1224 assert_eq!(read_braced("{{1, 2}, {3}}").unwrap(), vec!["{1, 2}", "{3}"]);
1225 assert_eq!(read_braced("{'a,b', 'c'}").unwrap(), vec!["'a,b'", "'c'"]);
1226 assert!(read_braced("1, 2").is_err());
1227 }
1228
1229 #[test]
1230 fn a_keyword_needs_a_word_boundary() {
1231 let mut c = Cursor::new(" ALLOWED");
1232 assert!(!c.eat_word("ALL"), "ALL should not match the front of ALLOWED");
1233 assert!(c.eat_word("ALLOWED"));
1234 let mut c = Cursor::new(" all ");
1235 assert!(c.eat_word("ALL"), "keywords are case insensitive");
1236 }
1237
1238 #[test]
1239 fn an_odd_indent_is_an_error_and_says_so() {
1240 let message = Plan::parse("Filter x\n Dummy\n").unwrap_err().to_string();
1241 assert!(message.contains("whole number of levels"), "unhelpful message: {message}");
1242 }
1243
1244 #[test]
1245 fn a_tab_is_an_error_rather_than_a_guess() {
1246 let message = Plan::parse("Filter x\n\tDummy\n").unwrap_err().to_string();
1247 assert!(message.contains("tab"), "unhelpful message: {message}");
1248 }
1249
1250 #[test]
1251 fn an_unknown_operator_names_itself() {
1252 let message = Plan::parse("Frobnicate\n").unwrap_err().to_string();
1253 assert!(message.contains("Frobnicate"), "unhelpful message: {message}");
1254 }
1255
1256 #[test]
1257 fn a_missing_child_is_an_error_rather_than_a_plan_with_a_hole() {
1258 let message = Plan::parse("Filter TRUE::BOOLEAN\n").unwrap_err().to_string();
1259 assert!(message.contains("expected an operator"), "unhelpful message: {message}");
1260 }
1261
1262 #[test]
1263 fn a_second_root_is_an_error() {
1264 let message = Plan::parse("Dummy\nDummy\n").unwrap_err().to_string();
1265 assert!(message.contains("past the end"), "unhelpful message: {message}");
1266 }
1267
1268 #[test]
1269 fn text_left_over_on_a_line_is_an_error() {
1270 let message = Plan::parse("Dummy nonsense\n").unwrap_err().to_string();
1271 assert!(message.contains("left over"), "unhelpful message: {message}");
1272 }
1273
1274 #[test]
1275 fn an_empty_plan_is_not_a_plan() {
1276 assert!(Plan::parse("").is_err());
1277 assert!(Plan::parse("\n\n \n").is_err());
1278 }
1279
1280 #[test]
1283 fn the_reader_runs_the_plan_invariant() {
1284 let message = Plan::parse("Filter 1::INTEGER\n Dummy\n").unwrap_err().to_string();
1285 assert!(message.contains("BOOLEAN"), "unhelpful message: {message}");
1286 }
1287
1288 #[test]
1292 fn a_quoted_reserved_word_is_a_function_and_not_syntax() {
1293 for reserved in RESERVED {
1294 let text = format!("Project #1 [\"{reserved}\"(1::INTEGER)::INTEGER AS a]\n Dummy\n");
1295 let plan = Plan::parse(&text).unwrap_or_else(|e| panic!("{text} does not read: {e}"));
1296 assert!(matches!(plan.expr(0), Expr::Constant(_)));
1297 assert!(matches!(plan.expr(1), Expr::Function { .. }), "{reserved} became syntax");
1298 assert_eq!(plan.to_string(), text);
1299 }
1300 }
1301
1302 #[test]
1303 fn an_unquoted_cast_is_syntax_and_not_a_function() {
1304 let text = "Project #1 [CAST(1::INTEGER)::BIGINT AS a]\n Dummy\n";
1305 let plan = Plan::parse(text).expect("a cast reads back");
1306 assert!(matches!(plan.expr(1), Expr::Cast { try_cast: false, .. }));
1307 assert_eq!(plan.to_string(), text);
1308 }
1309
1310 #[test]
1313 fn a_function_called_filter_in_argument_position_is_a_call() {
1314 let text = "Aggregate #1 groups=[] aggregates=[sum(filter(1::INTEGER)::INTEGER)::HUGEINT]\n Dummy\n";
1315 let plan = Plan::parse(text).expect("an argument called filter reads back");
1316 assert_eq!(plan.to_string(), text);
1317 }
1318
1319 #[test]
1320 fn an_aggregate_with_no_arguments_can_still_have_a_filter() {
1321 let text = "Aggregate #1 groups=[] aggregates=[count_star(FILTER TRUE::BOOLEAN)::BIGINT]\n Dummy\n";
1322 let plan = Plan::parse(text).expect("count(*) FILTER (WHERE p) is a real query");
1323 assert_eq!(plan.to_string(), text);
1324 }
1325
1326 #[test]
1327 fn a_name_that_is_not_a_plain_identifier_is_quoted_by_the_printer_and_read_here() {
1328 let mut c = Cursor::new("\"say \"\"hi\"\"\" rest");
1329 assert_eq!(read_identifier(&mut c).unwrap(), "say \"hi\"");
1330 assert_eq!(c.rest(), " rest");
1331 assert!(!is_plain_identifier("say \"hi\""));
1332 }
1333}