1use std::fmt;
2use std::str::FromStr;
3
4use pest::iterators::Pair;
5use substrait::proto::rel::RelType;
6use substrait::proto::{
7 Expression, ExtensionLeafRel, ExtensionMultiRel, ExtensionSingleRel, Rel, Type,
8};
9use thiserror::Error;
10
11use super::{
12 ErrorKind, ExpressionParser, MessageParseError, ParsePair, Rule, RuleIter, ScopedParsePair,
13 unescape_string, unwrap_single_pair,
14};
15use crate::extensions::any::Any;
16use crate::extensions::simple::{self, ExtensionKind};
17use crate::extensions::{
18 AddendumKind, ExtensionArgs, ExtensionColumn, ExtensionValue, InsertError, SimpleExtensions,
19 TupleValue,
20};
21use crate::parser::expressions::{FieldIndex, Name};
22use crate::parser::structural::IndentedLine;
23use crate::textify::expressions::Reference;
24
25#[derive(Debug, Clone, Error)]
26pub enum ExtensionParseError {
27 #[error("Unexpected line, expected {0}")]
28 UnexpectedLine(ExpectedExtensionLine),
29 #[error("Error adding extension: {0}")]
30 ExtensionError(#[from] InsertError),
31 #[error("Error parsing message: {0}")]
32 Message(#[from] super::MessageParseError),
33}
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub enum ExpectedExtensionLine {
41 Extensions,
44 ExtensionUrns,
47 ExtensionDeclarations(ExtensionKind),
50}
51
52impl fmt::Display for ExpectedExtensionLine {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 match self {
55 ExpectedExtensionLine::Extensions => write!(f, "Subsection Header, e.g. 'URNs:'"),
56 ExpectedExtensionLine::ExtensionUrns => write!(f, "Extension URNs"),
57 ExpectedExtensionLine::ExtensionDeclarations(kind) => {
58 write!(f, "Extension Declaration for {kind}")
59 }
60 }
61 }
62}
63
64#[derive(Debug)]
71pub struct ExtensionParser {
72 state: ExpectedExtensionLine,
73 extensions: SimpleExtensions,
74}
75
76impl Default for ExtensionParser {
77 fn default() -> Self {
78 Self {
79 state: ExpectedExtensionLine::Extensions,
80 extensions: SimpleExtensions::new(),
81 }
82 }
83}
84
85impl ExtensionParser {
86 pub fn parse_line(&mut self, line: IndentedLine) -> Result<(), ExtensionParseError> {
87 if line.1.is_empty() {
88 self.state = ExpectedExtensionLine::Extensions;
91 return Ok(());
92 }
93
94 match self.state {
95 ExpectedExtensionLine::Extensions => self.parse_subsection(line),
96 ExpectedExtensionLine::ExtensionUrns => self.parse_extension_urns(line),
97 ExpectedExtensionLine::ExtensionDeclarations(extension_kind) => {
98 self.parse_declarations(line, extension_kind)
99 }
100 }
101 }
102
103 fn parse_subsection(&mut self, line: IndentedLine) -> Result<(), ExtensionParseError> {
104 match line {
105 IndentedLine(0, simple::EXTENSION_URNS_HEADER) => {
106 self.state = ExpectedExtensionLine::ExtensionUrns;
107 Ok(())
108 }
109 IndentedLine(0, simple::EXTENSION_FUNCTIONS_HEADER) => {
110 self.state = ExpectedExtensionLine::ExtensionDeclarations(ExtensionKind::Function);
111 Ok(())
112 }
113 IndentedLine(0, simple::EXTENSION_TYPES_HEADER) => {
114 self.state = ExpectedExtensionLine::ExtensionDeclarations(ExtensionKind::Type);
115 Ok(())
116 }
117 IndentedLine(0, simple::EXTENSION_TYPE_VARIATIONS_HEADER) => {
118 self.state =
119 ExpectedExtensionLine::ExtensionDeclarations(ExtensionKind::TypeVariation);
120 Ok(())
121 }
122 _ => Err(ExtensionParseError::UnexpectedLine(self.state)),
123 }
124 }
125
126 fn parse_extension_urns(&mut self, line: IndentedLine) -> Result<(), ExtensionParseError> {
127 match line {
128 IndentedLine(0, _s) => self.parse_subsection(line), IndentedLine(1, s) => {
130 let urn =
131 URNExtensionDeclaration::from_str(s).map_err(ExtensionParseError::Message)?;
132 self.extensions.add_extension_urn(urn.urn, urn.anchor)?;
133 Ok(())
134 }
135 _ => Err(ExtensionParseError::UnexpectedLine(self.state)),
136 }
137 }
138
139 fn parse_declarations(
140 &mut self,
141 line: IndentedLine,
142 extension_kind: ExtensionKind,
143 ) -> Result<(), ExtensionParseError> {
144 match line {
145 IndentedLine(0, _s) => self.parse_subsection(line), IndentedLine(1, s) => {
147 let decl = SimpleExtensionDeclaration::parse_from_kind(s, extension_kind)?;
148 self.extensions.add_extension(
149 extension_kind,
150 decl.urn_anchor,
151 decl.anchor,
152 decl.name,
153 )?;
154 Ok(())
155 }
156 _ => Err(ExtensionParseError::UnexpectedLine(self.state)),
157 }
158 }
159
160 pub fn extensions(&self) -> &SimpleExtensions {
161 &self.extensions
162 }
163
164 #[cfg(test)]
165 pub(crate) fn state(&self) -> ExpectedExtensionLine {
166 self.state
167 }
168}
169
170#[derive(Debug, Clone, PartialEq)]
171pub struct URNExtensionDeclaration {
172 pub anchor: u32,
173 pub urn: String,
174}
175
176#[derive(Debug, Clone, PartialEq)]
177pub struct SimpleExtensionDeclaration {
178 pub anchor: u32,
179 pub urn_anchor: u32,
180 pub name: String,
181}
182
183impl ParsePair for URNExtensionDeclaration {
184 fn rule() -> Rule {
185 Rule::extension_urn_declaration
186 }
187
188 fn message() -> &'static str {
189 "URNExtensionDeclaration"
190 }
191
192 fn parse_pair(pair: Pair<Rule>) -> Self {
193 assert_eq!(pair.as_rule(), Self::rule());
194
195 let mut iter = RuleIter::from(pair.into_inner());
196 let anchor_pair = iter.pop(Rule::urn_anchor);
197 let anchor = unwrap_single_pair(anchor_pair)
198 .as_str()
199 .parse::<u32>()
200 .unwrap();
201 let urn = iter.pop(Rule::urn).as_str().to_string();
202 iter.done();
203
204 URNExtensionDeclaration { anchor, urn }
205 }
206}
207
208impl FromStr for URNExtensionDeclaration {
209 type Err = super::MessageParseError;
210
211 fn from_str(s: &str) -> Result<Self, Self::Err> {
212 Self::parse_str(s)
213 }
214}
215
216impl SimpleExtensionDeclaration {
217 fn parse_from_kind(s: &str, kind: ExtensionKind) -> Result<Self, MessageParseError> {
218 let mut pairs = <ExpressionParser as pest::Parser<Rule>>::parse(Rule::simple_extension, s)
219 .map_err(|e| {
220 MessageParseError::new("SimpleExtensionDeclaration", ErrorKind::Syntax, Box::new(e))
221 })?;
222 assert_eq!(pairs.as_str(), s);
223 let pair = pairs.next().unwrap();
224 let mut iter = RuleIter::from(pair.into_inner());
225
226 let anchor = unwrap_single_pair(iter.pop(Rule::anchor))
227 .as_str()
228 .parse::<u32>()
229 .unwrap();
230 let urn_anchor = unwrap_single_pair(iter.pop(Rule::urn_anchor))
231 .as_str()
232 .parse::<u32>()
233 .unwrap();
234 let name_pair = iter.pop(Rule::simple_extension_name);
235 let name_span = name_pair.as_span();
236 let name = name_pair.as_str();
237
238 if kind != ExtensionKind::Type && name.starts_with("u!") {
239 return Err(MessageParseError::invalid(
240 "simple_extension_name",
241 name_span,
242 format!("'u!' prefix is only valid for type declarations, not {kind}"),
243 ));
244 }
245 if matches!(kind, ExtensionKind::Type | ExtensionKind::TypeVariation) && name.contains(':')
246 {
247 return Err(MessageParseError::invalid(
248 "simple_extension_name",
249 name_span,
250 format!(
251 "type/type-variation names must not include a signature suffix, got '{name}'"
252 ),
253 ));
254 }
255 iter.done();
256
257 Ok(SimpleExtensionDeclaration {
258 anchor,
259 urn_anchor,
260 name: name.to_string(),
261 })
262 }
263}
264
265impl ScopedParsePair for ExtensionValue {
269 fn rule() -> Rule {
270 Rule::extension_argument
271 }
272
273 fn message() -> &'static str {
274 "ExtensionValue"
275 }
276
277 fn parse_pair(
278 extensions: &SimpleExtensions,
279 pair: Pair<Rule>,
280 ) -> Result<Self, MessageParseError> {
281 assert_eq!(pair.as_rule(), Self::rule());
282
283 let inner = unwrap_single_pair(pair); Ok(match inner.as_rule() {
286 Rule::enum_value => {
287 let s = inner.as_str().trim_start_matches('&').to_string();
289 ExtensionValue::Enum(s)
290 }
291 Rule::reference => {
292 let field_index = FieldIndex::parse_pair(inner);
294 ExtensionValue::from(Reference(field_index.0))
295 }
296 Rule::untyped_literal => {
297 let value_pair = unwrap_single_pair(inner);
299 match value_pair.as_rule() {
300 Rule::string_literal => ExtensionValue::String(unescape_string(value_pair)),
301 Rule::integer => {
302 ExtensionValue::Integer(value_pair.as_str().parse::<i64>().unwrap())
303 }
304 Rule::float => {
305 ExtensionValue::Float(value_pair.as_str().parse::<f64>().unwrap())
306 }
307 Rule::boolean => ExtensionValue::Boolean(value_pair.as_str() == "true"),
308 _ => panic!(
309 "Unexpected extension scalar literal type: {:?}",
310 value_pair.as_rule()
311 ),
312 }
313 }
314 Rule::tuple => {
315 let tv = inner
316 .into_inner()
317 .map(|pair| ExtensionValue::parse_pair(extensions, pair))
318 .collect::<Result<TupleValue, MessageParseError>>()?;
319 ExtensionValue::Tuple(tv)
320 }
321 Rule::expression => {
322 let expr = Expression::parse_pair(extensions, inner)?;
323 ExtensionValue::from(expr)
324 }
325 _ => panic!("Unexpected extension argument type: {:?}", inner.as_rule()),
326 })
327 }
328}
329
330impl ScopedParsePair for ExtensionColumn {
331 fn rule() -> Rule {
332 Rule::extension_column
333 }
334
335 fn message() -> &'static str {
336 "ExtensionColumn"
337 }
338
339 fn parse_pair(
340 extensions: &SimpleExtensions,
341 pair: Pair<Rule>,
342 ) -> Result<Self, MessageParseError> {
343 assert_eq!(pair.as_rule(), Self::rule());
344
345 let inner = unwrap_single_pair(pair); Ok(match inner.as_rule() {
348 Rule::named_column => {
349 let mut iter = inner.into_inner();
350 let name_pair = iter.next().unwrap(); let type_pair = iter.next().unwrap(); let name = Name::parse_pair(name_pair).0.to_string(); let ty = Type::parse_pair(extensions, type_pair)?;
355
356 ExtensionColumn::Named { name, r#type: ty }
357 }
358 Rule::reference => {
359 let field_index = FieldIndex::parse_pair(inner);
361 ExtensionColumn::Expr(Reference(field_index.0).into())
362 }
363 Rule::expression => {
364 let expr = Expression::parse_pair(extensions, inner)?;
365 ExtensionColumn::Expr(expr.into())
366 }
367 _ => panic!("Unexpected extension column type: {:?}", inner.as_rule()),
368 })
369 }
370}
371
372#[derive(Debug, Clone, Copy, PartialEq, Eq)]
375pub(crate) enum ExtensionRelationKind {
376 Leaf,
377 Single,
378 Multi,
379}
380
381impl FromStr for ExtensionRelationKind {
382 type Err = String;
383
384 fn from_str(s: &str) -> Result<Self, Self::Err> {
385 match s {
386 "ExtensionLeaf" => Ok(ExtensionRelationKind::Leaf),
387 "ExtensionSingle" => Ok(ExtensionRelationKind::Single),
388 "ExtensionMulti" => Ok(ExtensionRelationKind::Multi),
389 _ => Err(format!("Unknown extension relation type: {s}")),
390 }
391 }
392}
393
394impl ExtensionRelationKind {
395 pub(crate) fn validate_child_count(self, child_count: usize) -> Result<(), String> {
396 match self {
397 ExtensionRelationKind::Leaf => {
398 if child_count == 0 {
399 Ok(())
400 } else {
401 Err(format!(
402 "ExtensionLeaf should have no input children, got {child_count}"
403 ))
404 }
405 }
406 ExtensionRelationKind::Single => {
407 if child_count == 1 {
408 Ok(())
409 } else {
410 Err(format!(
411 "ExtensionSingle should have exactly 1 input child, got {child_count}"
412 ))
413 }
414 }
415 ExtensionRelationKind::Multi => Ok(()),
416 }
417 }
418
419 pub(crate) fn create_rel(self, detail: Option<Any>, children: Vec<Rel>) -> Rel {
421 let rel_type = match self {
422 ExtensionRelationKind::Leaf => RelType::ExtensionLeaf(ExtensionLeafRel {
423 common: None,
424 detail: detail.map(Into::into),
425 }),
426 ExtensionRelationKind::Single => {
427 let input = children.into_iter().next();
428 RelType::ExtensionSingle(Box::new(ExtensionSingleRel {
429 common: None,
430 detail: detail.map(Into::into),
431 input: input.map(Box::new),
432 }))
433 }
434 ExtensionRelationKind::Multi => RelType::ExtensionMulti(ExtensionMultiRel {
435 common: None,
436 detail: detail.map(Into::into),
437 inputs: children,
438 }),
439 };
440
441 Rel {
442 rel_type: Some(rel_type),
443 }
444 }
445}
446
447#[derive(Debug, Clone)]
450pub(crate) struct ExtensionInvocation {
451 pub(crate) relation_kind: ExtensionRelationKind,
452 pub(crate) name: String,
453 pub(crate) args: ExtensionArgs,
454}
455
456impl ScopedParsePair for ExtensionInvocation {
457 fn rule() -> Rule {
458 Rule::extension_relation
459 }
460
461 fn message() -> &'static str {
462 "ExtensionInvocation"
463 }
464
465 fn parse_pair(
466 extensions: &SimpleExtensions,
467 pair: Pair<Rule>,
468 ) -> Result<Self, MessageParseError> {
469 assert_eq!(pair.as_rule(), Self::rule());
470
471 let mut iter = pair.into_inner();
472
473 let extension_name_pair = iter.next().unwrap(); let full_extension_name = extension_name_pair.as_str();
476
477 let (relation_type_str, custom_name) = if full_extension_name.contains(':') {
480 let parts: Vec<&str> = full_extension_name.splitn(2, ':').collect();
481 (parts[0], parts[1].to_string())
482 } else {
483 (full_extension_name, "UnknownExtension".to_string())
484 };
485
486 let relation_kind = ExtensionRelationKind::from_str(relation_type_str).unwrap();
487 let mut args = ExtensionArgs::default();
488
489 let ext_arguments = iter.next().unwrap();
491 match ext_arguments.as_rule() {
492 Rule::arguments => {
493 arguments_rule_parsing(extensions, ext_arguments, &mut args)?;
494 }
495 r => unreachable!("Unexpected rule in ExtensionArgs: {:?}", r),
496 }
497
498 let extension_columns = iter.next();
500 if let Some(value) = extension_columns {
501 match value.as_rule() {
502 Rule::extension_columns => {
503 for col_pair in value.into_inner() {
504 if col_pair.as_rule() == Rule::extension_column {
505 let column = ExtensionColumn::parse_pair(extensions, col_pair)?;
506 args.output_columns.push(column);
507 }
508 }
509 }
510 r => unreachable!("Unexpected rule in ExtensionArgs: {:?}", r),
511 }
512 }
513
514 Ok(ExtensionInvocation {
515 relation_kind,
516 name: custom_name,
517 args,
518 })
519 }
520}
521
522#[derive(Debug, Clone)]
524pub(crate) struct AddendumInvocation {
525 pub(crate) kind: AddendumKind,
526 pub(crate) name: String,
527 pub(crate) args: ExtensionArgs,
528}
529
530impl ScopedParsePair for AddendumInvocation {
531 fn rule() -> Rule {
532 Rule::addendum
533 }
534
535 fn message() -> &'static str {
536 "AddendumInvocation"
537 }
538
539 fn parse_pair(
540 extensions: &SimpleExtensions,
541 pair: Pair<Rule>,
542 ) -> Result<Self, MessageParseError> {
543 assert_eq!(pair.as_rule(), Self::rule());
544
545 let mut iter = pair.into_inner();
546
547 let type_pair = iter.next().unwrap(); let kind = match type_pair.as_str() {
550 "Enh" => AddendumKind::Enhancement,
551 "Opt" => AddendumKind::Optimization,
552 "Ext" => AddendumKind::ExtensionTable,
553 other => unreachable!("Unexpected addendum_type: {other}"),
554 };
555
556 let name_pair = iter.next().unwrap();
558 let name = Name::parse_pair(name_pair).0.to_string();
559
560 let mut args = ExtensionArgs::default();
562
563 let arguments_pair = iter.next().unwrap();
564 match arguments_pair.as_rule() {
565 Rule::arguments => {
566 arguments_rule_parsing(extensions, arguments_pair, &mut args)?;
567 }
568 r => unreachable!("Unexpected rule in AddendumInvocation args: {r:?}"),
569 }
570
571 Ok(AddendumInvocation { kind, name, args })
572 }
573}
574
575fn arguments_rule_parsing(
576 extensions: &SimpleExtensions,
577 inner_pair: Pair<'_, Rule>,
578 args: &mut ExtensionArgs,
579) -> Result<(), MessageParseError> {
580 for arg in inner_pair.into_inner() {
581 match arg.as_rule() {
582 Rule::extension_arguments => {
583 for arg_pair in arg.into_inner() {
584 assert_eq!(arg_pair.as_rule(), Rule::extension_argument);
585 args.push(ExtensionValue::parse_pair(extensions, arg_pair)?);
586 }
587 }
588 Rule::extension_named_arguments => {
589 for arg_pair in arg.into_inner() {
590 assert_eq!(arg_pair.as_rule(), Rule::extension_named_argument);
591 let mut arg_iter = arg_pair.into_inner();
592 let name_p = arg_iter.next().unwrap();
593 let value_p = arg_iter.next().unwrap();
594 let key = Name::parse_pair(name_p).0.to_string();
595 let val = ExtensionValue::parse_pair(extensions, value_p)?;
596 args.insert(key, val);
597 }
598 }
599 Rule::empty => {}
600 r => unreachable!("Unexpected rule in extension args: {r:?}"),
601 }
602 }
603 Ok(())
604}
605
606#[cfg(test)]
607mod tests {
608 use substrait::proto;
609 use substrait::proto::expression::RexType;
610 use substrait::proto::expression::literal::LiteralType;
611
612 use super::*;
613 use crate::extensions::{Expr, ExtensionValue};
614 use crate::fixtures::TestContext;
615 use crate::parser::common::test_support::ScopedParse;
616 use crate::parser::{ParseError, Parser};
617 use crate::{OutputOptions, format};
618
619 fn parse_extension_value(text: &str) -> ExtensionValue {
620 ExtensionValue::parse(&SimpleExtensions::default(), text).unwrap()
621 }
622
623 #[test]
624 fn test_parse_urn_extension_declaration() {
625 let line = "@1: /my/urn1";
626 let urn = URNExtensionDeclaration::parse_str(line).unwrap();
627 assert_eq!(urn.anchor, 1);
628 assert_eq!(urn.urn, "/my/urn1");
629 }
630
631 #[test]
632 fn test_parse_simple_extension_declaration() {
633 let line = "#5@2: my_function_name";
634 let decl =
635 SimpleExtensionDeclaration::parse_from_kind(line, ExtensionKind::Function).unwrap();
636 assert_eq!(decl.anchor, 5);
637 assert_eq!(decl.urn_anchor, 2);
638 assert_eq!(decl.name, "my_function_name");
639
640 let line2 = "#10 @200: another_ext_123";
641 let decl =
642 SimpleExtensionDeclaration::parse_from_kind(line2, ExtensionKind::Function).unwrap();
643 assert_eq!(decl.anchor, 10);
644 assert_eq!(decl.urn_anchor, 200);
645 assert_eq!(decl.name, "another_ext_123");
646 }
647
648 #[test]
649 fn test_parse_urn_extension_declaration_str() {
650 let line = "@1: /my/urn1";
651 let urn = URNExtensionDeclaration::parse_str(line).unwrap();
652 assert_eq!(urn.anchor, 1);
653 assert_eq!(urn.urn, "/my/urn1");
654 }
655
656 #[test]
657 fn test_extensions_round_trip_plan() {
658 let input = r#"
659=== Extensions
660URNs:
661 @ 1: /urn/common
662 @ 2: /urn/specific_funcs
663Functions:
664 # 10 @ 1: func_a
665 # 11 @ 2: func_b_special
666Types:
667 # 20 @ 1: SomeType
668Type Variations:
669 # 30 @ 2: VarX
670"#
671 .trim_start();
672
673 let plan = Parser::parse(input).unwrap();
675
676 assert_eq!(plan.extension_urns.len(), 2);
678 assert_eq!(plan.extensions.len(), 4);
679
680 let (extensions, errors) =
682 SimpleExtensions::from_extensions(&plan.extension_urns, &plan.extensions);
683
684 assert!(errors.is_empty());
685 let output = extensions.to_string(" ");
687
688 assert_eq!(output, input);
690 }
691
692 #[test]
693 fn test_parse_simple_extension_declaration_compound_name() {
694 let line = "#1 @2: equal:any_any";
696 let decl =
697 SimpleExtensionDeclaration::parse_from_kind(line, ExtensionKind::Function).unwrap();
698 assert_eq!(decl.anchor, 1);
699 assert_eq!(decl.urn_anchor, 2);
700 assert_eq!(decl.name, "equal:any_any");
701 }
702
703 #[test]
704 fn test_parse_simple_extension_declaration_compound_name_multi_segment() {
705 let line = "#3 @1: regexp_match_substring:str_str_i64";
706 let decl =
707 SimpleExtensionDeclaration::parse_from_kind(line, ExtensionKind::Function).unwrap();
708 assert_eq!(decl.anchor, 3);
709 assert_eq!(decl.urn_anchor, 1);
710 assert_eq!(decl.name, "regexp_match_substring:str_str_i64");
711 }
712
713 #[test]
714 fn test_parse_simple_extension_declaration_u_prefix_function_with_u_signature() {
715 let line = "#5 @2: json_extract_path:u!json_str";
718 let decl =
719 SimpleExtensionDeclaration::parse_from_kind(line, ExtensionKind::Function).unwrap();
720 assert_eq!(decl.anchor, 5);
721 assert_eq!(decl.urn_anchor, 2);
722 assert_eq!(decl.name, "json_extract_path:u!json_str");
723 }
724
725 #[test]
726 fn test_u_prefix_type_declaration_accepted() {
727 let plan_text = "\
729=== Extensions
730URNs:
731 @ 1: https://example.com/types
732Types:
733 # 11 @ 1: u!point
734=== Plan
735Root[result]
736 Project[$0]
737 Read[data => p:point#11]";
738 let plan = Parser::parse(plan_text).unwrap();
739 let (text, errors) = format(&plan);
740 assert!(errors.is_empty(), "unexpected errors: {errors:?}");
741 assert!(
742 text.contains(" # 11 @ 1: point"),
743 "declaration line must use bare name"
744 );
745 assert!(
746 !text.contains("u!point"),
747 "u! prefix should be stripped in output"
748 );
749 }
750
751 #[test]
752 fn test_u_prefix_type_variation_declaration_rejected() {
753 let plan_text = "\
755=== Extensions
756URNs:
757 @ 1: https://example.com/types
758Type Variations:
759 # 30 @ 1: u!myvar
760=== Plan
761Root[result]
762 Read[data => x:i64]";
763 assert!(
764 Parser::parse(plan_text).is_err(),
765 "u! prefix on a type variation name should be rejected"
766 );
767 }
768
769 #[test]
770 fn test_u_prefix_function_declaration_rejected() {
771 let plan_text = "\
773=== Extensions
774URNs:
775 @ 1: https://example.com/funcs
776Functions:
777 # 21 @ 1: u!json_get
778=== Plan
779Root[result]
780 Read[data => x:i64]";
781 assert!(
782 Parser::parse(plan_text).is_err(),
783 "u! prefix on a function name should be rejected"
784 );
785 }
786
787 #[test]
788 fn test_signature_on_type_declaration_rejected() {
789 let plan_text = "\
791=== Extensions
792URNs:
793 @ 1: https://example.com/types
794Types:
795 # 10 @ 1: mytype:i64_i64
796=== Plan
797Root[result]
798 Read[data => x:i64]";
799 assert!(
800 Parser::parse(plan_text).is_err(),
801 "function signature suffix on a type declaration should be rejected"
802 );
803 }
804
805 #[test]
806 fn test_u_prefix_function_rejected_at_parser_level() {
807 let plan_text = "\
809=== Extensions
810URNs:
811 @ 1: https://example.com/funcs
812Functions:
813 # 21 @ 1: u!bad_func
814=== Plan
815Root[result]
816 Read[data => x:i64]";
817 let err = Parser::parse(plan_text).unwrap_err();
818 assert!(
819 matches!(
820 err,
821 ParseError::Extension(_, ExtensionParseError::Message(_))
822 ),
823 "expected parser-level MessageParseError, got: {err}"
824 );
825 }
826
827 #[test]
828 fn test_extensions_round_trip_plan_with_compound_names() {
829 let input = r#"=== Extensions
830URNs:
831 @ 1: extension:io.substrait:functions_string
832 @ 2: extension:io.substrait:functions_comparison
833Functions:
834 # 1 @ 2: equal:any_any
835 # 2 @ 1: regexp_match_substring:str_str
836 # 3 @ 1: regexp_match_substring:str_str_i64
837"#;
838 let plan = Parser::parse(input).unwrap();
839 let (extensions, errors) =
840 SimpleExtensions::from_extensions(&plan.extension_urns, &plan.extensions);
841 assert!(errors.is_empty());
842 assert_eq!(
844 extensions
845 .find_by_anchor(ExtensionKind::Function, 1)
846 .unwrap()
847 .1
848 .full(),
849 "equal:any_any"
850 );
851 assert_eq!(
852 extensions
853 .find_by_anchor(ExtensionKind::Function, 3)
854 .unwrap()
855 .1
856 .full(),
857 "regexp_match_substring:str_str_i64"
858 );
859 assert_eq!(extensions.to_string(" "), input);
861 }
862
863 #[test]
864 fn test_tuple_mixed_types_parses() {
865 let val = parse_extension_value("(&HASH, 8, 'hello')");
867 let ExtensionValue::Tuple(items) = val else {
868 panic!("expected Tuple, got {val:?}");
869 };
870 assert_eq!(items.len(), 3);
871 let items: Vec<&ExtensionValue> = items.iter().collect();
872 assert!(matches!(items[0], ExtensionValue::Enum(s) if s == "HASH"));
873 assert_eq!(i64::try_from(items[1]).unwrap(), 8);
874 assert_eq!(<&str>::try_from(items[2]).unwrap(), "hello");
875 }
876
877 #[test]
878 fn test_empty_tuple_parses() {
879 let val = parse_extension_value("()");
880 let ExtensionValue::Tuple(items) = val else {
881 panic!("expected Tuple, got {val:?}");
882 };
883 assert!(items.is_empty());
884 }
885
886 #[test]
887 fn test_nested_tuple_parses() {
888 let val = parse_extension_value("((&HASH, &RANGE), 8)");
889 let ExtensionValue::Tuple(outer) = val else {
890 panic!("expected Tuple, got {val:?}");
891 };
892 assert_eq!(outer.len(), 2);
893 let ExtensionValue::Tuple(inner) = outer.iter().next().unwrap() else {
894 panic!("expected inner Tuple");
895 };
896 assert_eq!(inner.len(), 2);
897 assert!(matches!(inner.iter().next().unwrap(), ExtensionValue::Enum(s) if s == "HASH"));
898 assert_eq!(i64::try_from(outer.iter().nth(1).unwrap()).unwrap(), 8);
899 }
900
901 #[test]
902 fn test_tuple_in_addendum_parses() {
903 let inv = AddendumInvocation::parse(
904 &SimpleExtensions::default(),
905 "+ Enh:Foo[(&HASH, &RANGE), count=8]",
906 )
907 .unwrap();
908 assert_eq!(inv.kind, AddendumKind::Enhancement);
909 assert_eq!(inv.name, "Foo");
910 assert_eq!(inv.args.positional.len(), 1);
911 let ExtensionValue::Tuple(items) = &inv.args.positional[0] else {
912 panic!("expected Tuple positional arg");
913 };
914 assert_eq!(items.len(), 2);
915 let items: Vec<&ExtensionValue> = items.iter().collect();
916 assert!(matches!(items[0], ExtensionValue::Enum(s) if s == "HASH"));
917 assert!(matches!(items[1], ExtensionValue::Enum(s) if s == "RANGE"));
918 assert_eq!(inv.args.named.len(), 1);
919 }
920
921 #[test]
922 fn extension_relation_kind_parses_text_prefixes() {
923 assert_eq!(
924 ExtensionRelationKind::from_str("ExtensionLeaf").unwrap(),
925 ExtensionRelationKind::Leaf
926 );
927 assert_eq!(
928 ExtensionRelationKind::from_str("ExtensionSingle").unwrap(),
929 ExtensionRelationKind::Single
930 );
931 assert_eq!(
932 ExtensionRelationKind::from_str("ExtensionMulti").unwrap(),
933 ExtensionRelationKind::Multi
934 );
935 }
936
937 #[test]
938 fn extension_multi_allows_any_child_count() {
939 assert!(ExtensionRelationKind::Multi.validate_child_count(0).is_ok());
940 assert!(ExtensionRelationKind::Multi.validate_child_count(1).is_ok());
941 assert!(ExtensionRelationKind::Multi.validate_child_count(3).is_ok());
942 }
943
944 #[test]
945 fn extension_single_rejects_wrong_child_counts() {
946 assert!(
947 ExtensionRelationKind::Single
948 .validate_child_count(0)
949 .is_err()
950 );
951 assert!(
952 ExtensionRelationKind::Single
953 .validate_child_count(2)
954 .is_err()
955 );
956 }
957
958 #[test]
959 fn test_tuple_textify_roundtrip() {
960 let ctx = TestContext::new();
961 for text in &[
962 "(&HASH, &RANGE)",
963 "(&HASH, 8, 'hello')",
964 "()",
965 "(&HASH,)",
966 "((&HASH, &RANGE), 8)",
967 ] {
968 let val = parse_extension_value(text);
969 let rendered = ctx.textify_no_errors(&val);
970 assert_eq!(&rendered, text, "roundtrip failed for {text}");
971 }
972 }
973
974 #[test]
975 fn test_literal_expression_value_textifies_to_canonical_literal() {
976 let expr = proto::Expression {
977 rex_type: Some(RexType::Literal(proto::expression::Literal {
978 literal_type: Some(LiteralType::I64(42)),
979 nullable: false,
980 type_variation_reference: 0,
981 })),
982 };
983 let value = ExtensionValue::from(expr.clone());
984 let ctx = TestContext::new();
985
986 let rendered = ctx.textify_no_errors(&value);
987 assert_eq!(rendered, "42");
988
989 let parsed = parse_extension_value(&rendered);
990 let parsed_expr = Expr::try_from(&parsed).unwrap();
991 assert_eq!(parsed_expr.as_proto(), &expr);
992 }
993
994 #[test]
995 fn test_extension_scalar_literals_stay_scalar_in_verbose_output() {
996 let ctx = TestContext::new().with_options(OutputOptions::verbose());
997
998 let scalar = ExtensionValue::from(42_i64);
999 assert_eq!(ctx.textify_no_errors(&scalar), "42");
1000
1001 let expression = ExtensionValue::from(Expr::from(42_i64));
1002 assert_eq!(ctx.textify_no_errors(&expression), "42:i64");
1003 }
1004
1005 #[test]
1006 fn test_typed_extension_literal_parses_as_expression() {
1007 let value = parse_extension_value("42:i16");
1008 assert!(i64::try_from(&value).is_err());
1009
1010 let expr = Expr::try_from(&value).unwrap();
1011 assert_eq!(ctx_text(&expr), "42:i16");
1012 }
1013
1014 fn ctx_text(value: &Expr) -> String {
1015 TestContext::new().textify_no_errors(value)
1016 }
1017}