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