1use std::fmt::{self};
2
3use chrono::{DateTime, NaiveDate};
4use expr::RexType;
5use substrait::proto::expression::field_reference::{ReferenceType, RootReference, RootType};
6use substrait::proto::expression::literal::LiteralType;
7use substrait::proto::expression::{
8 Cast, FieldReference, IfThen, ReferenceSegment, ScalarFunction, cast, reference_segment,
9};
10use substrait::proto::function_argument::ArgType;
11use substrait::proto::{
12 AggregateFunction, Expression, FunctionArgument, FunctionOption, expression as expr,
13};
14
15use super::{PlanError, Scope, Textify, Visibility};
16use crate::extensions::simple::ExtensionKind;
17use crate::textify::types::{Name, NamedAnchor, OutputType, escaped};
18
19pub fn textify_binary<S: Scope, W: fmt::Write>(items: &[u8], ctx: &S, w: &mut W) -> fmt::Result {
32 if ctx.options().show_literal_binaries {
33 write!(w, "0x")?;
34 for &n in items {
35 write!(w, "{n:02x}")?;
36 }
37 } else {
38 write!(w, "{{binary}}")?;
39 }
40 Ok(())
41}
42
43fn unimplemented_literal<S: Scope, W: fmt::Write>(
45 variant: &'static str,
46 ctx: &S,
47 w: &mut W,
48) -> fmt::Result {
49 write!(
50 w,
51 "{}",
52 ctx.failure(PlanError::unimplemented(
53 "LiteralType",
54 Some(variant),
55 format!("{variant} literal textification not implemented"),
56 ))
57 )
58}
59
60pub fn textify_enum<S: Scope, W: fmt::Write>(s: &str, _ctx: &S, w: &mut W) -> fmt::Result {
63 write!(w, "&{}", Name(s))
64}
65
66pub fn timestamp_to_string(t: i64) -> String {
67 let ts = chrono::DateTime::from_timestamp_nanos(t);
68 ts.to_rfc3339()
69}
70
71fn days_to_date_string(days: i32) -> String {
73 let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
74 let date = epoch + chrono::Duration::days(days as i64);
75 date.format("%Y-%m-%d").to_string()
76}
77
78fn microseconds_to_time_string(microseconds: i64) -> String {
80 let total_seconds = microseconds / 1_000_000;
81 let remaining_microseconds = microseconds % 1_000_000;
82
83 let hours = total_seconds / 3600;
84 let minutes = (total_seconds % 3600) / 60;
85 let seconds = total_seconds % 60;
86
87 if remaining_microseconds == 0 {
88 format!("{hours:02}:{minutes:02}:{seconds:02}")
89 } else {
90 let fractional = remaining_microseconds as f64 / 1_000_000.0;
92 format!("{hours:02}:{minutes:02}:{seconds:02}{fractional:.6}")
93 .trim_end_matches('0')
94 .trim_end_matches('.')
95 .to_string()
96 }
97}
98
99fn microseconds_to_timestamp_string(microseconds: i64) -> String {
101 let epoch = DateTime::from_timestamp(0, 0).unwrap().naive_utc();
102 let duration = chrono::Duration::microseconds(microseconds);
103 let datetime = epoch + duration;
104
105 let formatted = datetime.format("%Y-%m-%dT%H:%M:%S%.f").to_string();
107
108 if formatted.contains('.') {
110 formatted
111 .trim_end_matches('0')
112 .trim_end_matches('.')
113 .to_string()
114 } else {
115 formatted
116 }
117}
118
119fn write_literal_value<S: Scope, W: fmt::Write>(
124 lit: &LiteralType,
125 ctx: &S,
126 w: &mut W,
127) -> fmt::Result {
128 match lit {
129 LiteralType::Boolean(b) => write!(w, "{b}"),
130 LiteralType::I8(i) | LiteralType::I16(i) | LiteralType::I32(i) => write!(w, "{i}"),
131 LiteralType::I64(i) => write!(w, "{i}"),
132 LiteralType::Fp32(f) => write!(w, "{f}"),
133 LiteralType::Fp64(f) => write!(w, "{f}"),
134 LiteralType::String(s) => write!(w, "'{}'", s.escape_debug()),
135 LiteralType::Binary(items) => textify_binary(items, ctx, w),
136 LiteralType::Date(days) => {
137 write!(w, "'{}'", escaped(&days_to_date_string(*days)))
138 }
139 #[allow(deprecated)]
140 LiteralType::Time(microseconds) => {
141 write!(
142 w,
143 "'{}'",
144 escaped(µseconds_to_time_string(*microseconds))
145 )
146 }
147 #[allow(deprecated)]
148 LiteralType::Timestamp(microseconds) => {
149 write!(
150 w,
151 "'{}'",
152 escaped(µseconds_to_timestamp_string(*microseconds))
153 )
154 }
155 LiteralType::IntervalYearToMonth(_) => unimplemented_literal("IntervalYearToMonth", ctx, w),
156 LiteralType::IntervalDayToSecond(_) => unimplemented_literal("IntervalDayToSecond", ctx, w),
157 LiteralType::IntervalCompound(_) => unimplemented_literal("IntervalCompound", ctx, w),
158 LiteralType::FixedChar(_) => unimplemented_literal("FixedChar", ctx, w),
159 LiteralType::VarChar(_) => unimplemented_literal("VarChar", ctx, w),
160 LiteralType::FixedBinary(_) => unimplemented_literal("FixedBinary", ctx, w),
161 LiteralType::Decimal(_) => unimplemented_literal("Decimal", ctx, w),
162 LiteralType::PrecisionTime(_) => unimplemented_literal("PrecisionTime", ctx, w),
163 LiteralType::PrecisionTimestamp(_) => unimplemented_literal("PrecisionTimestamp", ctx, w),
164 LiteralType::PrecisionTimestampTz(_) => {
165 unimplemented_literal("PrecisionTimestampTz", ctx, w)
166 }
167 LiteralType::Struct(_) => unimplemented_literal("Struct", ctx, w),
168 LiteralType::Map(_) => unimplemented_literal("Map", ctx, w),
169 #[allow(deprecated)]
170 LiteralType::TimestampTz(_) => unimplemented_literal("TimestampTz", ctx, w),
171 LiteralType::Uuid(_) => unimplemented_literal("Uuid", ctx, w),
172 LiteralType::Null(_) => unimplemented_literal("Null", ctx, w),
173 LiteralType::List(_) => unimplemented_literal("List", ctx, w),
174 LiteralType::EmptyList(_) => unimplemented_literal("EmptyList", ctx, w),
175 LiteralType::EmptyMap(_) => unimplemented_literal("EmptyMap", ctx, w),
176 LiteralType::UserDefined(_) => unimplemented_literal("UserDefined", ctx, w),
177 }
178}
179
180fn literal_type_suffix(lit: &LiteralType) -> Option<&'static str> {
185 match lit {
186 LiteralType::Boolean(_) => Some("boolean"),
187 LiteralType::I8(_) => Some("i8"),
188 LiteralType::I16(_) => Some("i16"),
189 LiteralType::I32(_) => Some("i32"),
190 LiteralType::I64(_) => Some("i64"),
191 LiteralType::Fp32(_) => Some("fp32"),
192 LiteralType::Fp64(_) => Some("fp64"),
193 LiteralType::String(_) => Some("string"),
194 LiteralType::Binary(_) => Some("binary"),
195 LiteralType::Date(_) => Some("date"),
196 #[allow(deprecated)]
197 LiteralType::Time(_) => Some("time"),
198 #[allow(deprecated)]
199 LiteralType::Timestamp(_) => Some("timestamp"),
200 _ => None,
201 }
202}
203
204fn is_default_for_syntax(lit: &LiteralType) -> bool {
217 matches!(
218 lit,
219 LiteralType::Boolean(_)
220 | LiteralType::String(_)
221 | LiteralType::Binary(_)
222 | LiteralType::I64(_)
223 | LiteralType::Fp64(_)
224 )
225}
226
227impl Textify for expr::Literal {
228 fn name() -> &'static str {
229 "Literal"
230 }
231
232 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
233 let Some(lit) = self.literal_type.as_ref() else {
234 return write!(
235 w,
236 "{}",
237 ctx.failure(PlanError::invalid(
238 "Literal",
239 Some("literal_type"),
240 "missing literal_type",
241 ))
242 );
243 };
244 write_literal_value(lit, ctx, w)?;
245 let show_suffix = match ctx.options().literal_types {
246 Visibility::Never => false,
247 Visibility::Always => true,
248 Visibility::Required => self.nullable || !is_default_for_syntax(lit),
249 };
250 if show_suffix {
251 if let Some(suffix) = literal_type_suffix(lit) {
252 write!(w, ":{suffix}")?;
253 }
254 if self.nullable {
255 write!(w, "?")?;
256 }
257 }
258 Ok(())
259 }
260}
261
262pub struct Reference(pub i32);
263
264impl fmt::Display for Reference {
265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266 write!(f, "${}", self.0)
267 }
268}
269
270impl From<Reference> for Expression {
271 fn from(r: Reference) -> Self {
272 Expression {
275 rex_type: Some(RexType::Selection(Box::new(FieldReference {
276 reference_type: Some(ReferenceType::DirectReference(ReferenceSegment {
277 reference_type: Some(reference_segment::ReferenceType::StructField(Box::new(
278 reference_segment::StructField {
279 field: r.0,
280 child: None,
281 },
282 ))),
283 })),
284 root_type: Some(RootType::RootReference(RootReference {})),
285 }))),
286 }
287 }
288}
289
290impl Textify for Reference {
291 fn name() -> &'static str {
292 "Reference"
293 }
294
295 fn textify<S: Scope, W: fmt::Write>(&self, _ctx: &S, w: &mut W) -> fmt::Result {
296 write!(w, "{self}")
297 }
298}
299
300impl Textify for FieldReference {
301 fn name() -> &'static str {
302 "FieldReference"
303 }
304
305 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
306 match &self.root_type {
307 Some(RootType::RootReference(_)) => {}
308 None => {
309 return write!(
310 w,
311 "{}",
312 ctx.failure(PlanError::invalid(
313 "FieldReference",
314 Some("root_type"),
315 "Required field root_type is missing",
316 ))
317 );
318 }
319 Some(RootType::Expression(_)) => {
320 return write!(
321 w,
322 "{}",
323 ctx.failure(PlanError::unimplemented(
324 "FieldReference",
325 Some("root_type"),
326 "FieldReference textification not implemented for Expression root_type",
327 ))
328 );
329 }
330 Some(RootType::OuterReference(_)) => {
331 return write!(
332 w,
333 "{}",
334 ctx.failure(PlanError::unimplemented(
335 "FieldReference",
336 Some("root_type"),
337 "FieldReference textification not implemented for OuterReference root_type",
338 ))
339 );
340 }
341 Some(RootType::LambdaParameterReference(_)) => {
342 return write!(
343 w,
344 "{}",
345 ctx.failure(PlanError::unimplemented(
346 "FieldReference",
347 Some("root_type"),
348 "FieldReference textification not implemented for LambdaParameterReference root_type",
349 ))
350 );
351 }
352 }
353
354 let ref_type = match &self.reference_type {
355 None => {
356 return write!(
357 w,
358 "{}",
359 ctx.failure(PlanError::invalid(
360 "FieldReference",
361 Some("reference_type"),
362 "Required field reference_type is missing",
363 ))
364 );
365 }
366 Some(ReferenceType::DirectReference(r)) => r,
367 _ => {
368 return write!(
369 w,
370 "{}",
371 ctx.failure(PlanError::unimplemented(
372 "FieldReference",
373 Some("FieldReference"),
374 "FieldReference textification implemented only for StructField",
375 ))
376 );
377 }
378 };
379
380 match &ref_type.reference_type {
381 Some(reference_segment::ReferenceType::StructField(s)) => {
382 write!(w, "{}", Reference(s.field))
383 }
384 None => write!(
385 w,
386 "{}",
387 ctx.failure(PlanError::invalid(
388 "ReferenceSegment",
389 Some("reference_type"),
390 "Required field reference_type is missing",
391 ))
392 ),
393 _ => write!(
394 w,
395 "{}",
396 ctx.failure(PlanError::unimplemented(
397 "ReferenceSegment",
398 Some("reference_type"),
399 "ReferenceSegment textification implemented only for StructField",
400 ))
401 ),
402 }
403 }
404}
405
406impl Textify for ScalarFunction {
407 fn name() -> &'static str {
408 "ScalarFunction"
409 }
410
411 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
412 let name_and_anchor =
413 NamedAnchor::lookup(ctx, ExtensionKind::Function, self.function_reference);
414 let name_and_anchor = ctx.display(&name_and_anchor);
415
416 let args = ctx.separated(&self.arguments, ", ");
417 let options = ctx.separated(&self.options, ", ");
418 let between = if self.arguments.is_empty() || self.options.is_empty() {
419 ""
420 } else {
421 ", "
422 };
423
424 let output = OutputType(self.output_type.as_ref());
425 let output_type = ctx.optional(&output, ctx.options().fn_types);
426
427 write!(
428 w,
429 "{name_and_anchor}({args}{between}{options}){output_type}"
430 )?;
431 Ok(())
432 }
433}
434
435impl Textify for FunctionOption {
436 fn name() -> &'static str {
437 "FunctionOption"
438 }
439
440 fn textify<S: Scope, W: fmt::Write>(&self, _ctx: &S, w: &mut W) -> fmt::Result {
441 write!(w, "{}⇒[", self.name)?;
442 let mut first = true;
443 for pref in self.preference.iter() {
444 if !first {
445 write!(w, ", ")?;
446 } else {
447 first = false;
448 }
449 write!(w, "{pref}")?;
450 }
451 write!(w, "]")?;
452 Ok(())
453 }
454}
455
456impl Textify for FunctionArgument {
457 fn name() -> &'static str {
458 "FunctionArgument"
459 }
460
461 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
462 write!(w, "{}", ctx.expect(self.arg_type.as_ref()))
463 }
464}
465
466impl Textify for ArgType {
467 fn name() -> &'static str {
468 "ArgType"
469 }
470
471 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
472 match self {
473 ArgType::Type(t) => t.textify(ctx, w),
474 ArgType::Value(v) => v.textify(ctx, w),
475 ArgType::Enum(e) => textify_enum(e, ctx, w),
476 }
477 }
478}
479
480impl Textify for Cast {
481 fn name() -> &'static str {
482 "Cast"
483 }
484
485 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
486 let failure_err;
487 let fb: &dyn fmt::Display = match cast::FailureBehavior::try_from(self.failure_behavior) {
488 Ok(cast::FailureBehavior::Unspecified) => &"",
489 Ok(cast::FailureBehavior::ReturnNull) => &"?",
490 Ok(cast::FailureBehavior::ThrowException) => &"!",
491 Err(_) => {
492 failure_err = ctx.failure(PlanError::invalid(
493 "Cast",
494 Some("failure_behavior"),
495 format!("Unknown failure_behavior value: {}", self.failure_behavior),
496 ));
497 &failure_err
498 }
499 };
500 let input = ctx.expect(self.input.as_deref());
501 let target_type = ctx.expect(self.r#type.as_ref());
502 write!(w, "({input})::{fb}{target_type}")
503 }
504}
505
506impl Textify for IfThen {
507 fn name() -> &'static str {
508 "IfThen"
509 }
510
511 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
515 write!(w, "if_then(")?;
516 for clause in &self.ifs {
517 let if_expr = ctx.expect(clause.r#if.as_ref());
518 let then_expr = ctx.expect(clause.then.as_ref());
519 write!(w, "{if_expr} -> {then_expr}, ")?;
520 }
521 let else_expr = ctx.expect(self.r#else.as_deref());
522 write!(w, "_ -> {else_expr})")
523 }
524}
525
526impl Textify for RexType {
527 fn name() -> &'static str {
528 "RexType"
529 }
530
531 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
532 match self {
533 RexType::Literal(literal) => literal.textify(ctx, w),
534 RexType::Selection(f) => f.textify(ctx, w),
535 RexType::ScalarFunction(s) => s.textify(ctx, w),
536 RexType::WindowFunction(_w) => write!(
537 w,
538 "{}",
539 ctx.failure(PlanError::unimplemented(
540 "RexType",
541 Some("WindowFunction"),
542 "WindowFunction textification not implemented",
543 ))
544 ),
545 RexType::IfThen(i) => i.textify(ctx, w),
546 RexType::SwitchExpression(_s) => write!(
547 w,
548 "{}",
549 ctx.failure(PlanError::unimplemented(
550 "RexType",
551 Some("SwitchExpression"),
552 "SwitchExpression textification not implemented",
553 ))
554 ),
555 RexType::SingularOrList(_s) => write!(
556 w,
557 "{}",
558 ctx.failure(PlanError::unimplemented(
559 "RexType",
560 Some("SingularOrList"),
561 "SingularOrList textification not implemented",
562 ))
563 ),
564 RexType::MultiOrList(_m) => write!(
565 w,
566 "{}",
567 ctx.failure(PlanError::unimplemented(
568 "RexType",
569 Some("MultiOrList"),
570 "MultiOrList textification not implemented",
571 ))
572 ),
573 RexType::Cast(c) => c.textify(ctx, w),
574 RexType::Subquery(_s) => write!(
575 w,
576 "{}",
577 ctx.failure(PlanError::unimplemented(
578 "RexType",
579 Some("Subquery"),
580 "Subquery textification not implemented",
581 ))
582 ),
583 RexType::Nested(_n) => write!(
584 w,
585 "{}",
586 ctx.failure(PlanError::unimplemented(
587 "RexType",
588 Some("Nested"),
589 "Nested textification not implemented",
590 ))
591 ),
592 RexType::DynamicParameter(_d) => write!(
593 w,
594 "{}",
595 ctx.failure(PlanError::unimplemented(
596 "RexType",
597 Some("DynamicParameter"),
598 "DynamicParameter textification not implemented",
599 ))
600 ),
601 #[allow(deprecated)]
602 RexType::Enum(_) => write!(
603 w,
604 "{}",
605 ctx.failure(PlanError::unimplemented(
606 "RexType",
607 Some("Enum"),
608 "Enum textification not implemented",
609 ))
610 ),
611 RexType::Lambda(_) => write!(
612 w,
613 "{}",
614 ctx.failure(PlanError::unimplemented(
615 "RexType",
616 Some("Lambda"),
617 "Lambda textification not implemented",
618 ))
619 ),
620 RexType::LambdaInvocation(_) => write!(
621 w,
622 "{}",
623 ctx.failure(PlanError::unimplemented(
624 "RexType",
625 Some("LambdaInvocation"),
626 "LambdaInvocation textification not implemented",
627 ))
628 ),
629 }
630 }
631}
632
633impl Textify for Expression {
634 fn name() -> &'static str {
635 "Expression"
636 }
637
638 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
639 write!(w, "{}", ctx.expect(self.rex_type.as_ref()))
640 }
641}
642
643impl Textify for AggregateFunction {
644 fn name() -> &'static str {
645 "AggregateFunction"
646 }
647
648 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
649 let name_and_anchor =
651 NamedAnchor::lookup(ctx, ExtensionKind::Function, self.function_reference);
652 let name_and_anchor = ctx.display(&name_and_anchor);
653
654 let args = ctx.separated(&self.arguments, ", ");
655 let options = ctx.separated(&self.options, ", ");
656 let between = if self.arguments.is_empty() || self.options.is_empty() {
657 ""
658 } else {
659 ", "
660 };
661
662 let output = OutputType(self.output_type.as_ref());
663 let output_type = ctx.optional(&output, ctx.options().fn_types);
664
665 write!(
666 w,
667 "{name_and_anchor}({args}{between}{options}){output_type}"
668 )
669 }
670}
671
672#[cfg(test)]
673mod tests {
674 use substrait::proto::Type;
675 use substrait::proto::expression::{cast, if_then};
676 use substrait::proto::r#type::{I16, I32, Kind, Nullability};
677
678 use super::*;
679 use crate::extensions::simple::{ExtensionKind, MissingReference};
680 use crate::fixtures::TestContext;
681 use crate::textify::foundation::{FormatError, FormatErrorType};
682
683 fn literal_bool(value: bool) -> Expression {
684 Expression {
685 rex_type: Some(RexType::Literal(expr::Literal {
686 nullable: false,
687 type_variation_reference: 0,
688 literal_type: Some(expr::literal::LiteralType::Boolean(value)),
689 })),
690 }
691 }
692
693 fn non_nullable_literal(lit: expr::literal::LiteralType) -> expr::Literal {
694 expr::Literal {
695 nullable: false,
696 type_variation_reference: 0,
697 literal_type: Some(lit),
698 }
699 }
700
701 #[test]
702 fn test_literal_textify() {
703 let ctx = TestContext::new();
704
705 let literal = non_nullable_literal(LiteralType::Boolean(true));
706 assert_eq!(ctx.textify_no_errors(&literal), "true");
707 }
708
709 fn nullable_literal(lit: expr::literal::LiteralType) -> expr::Literal {
710 expr::Literal {
711 nullable: true,
712 type_variation_reference: 0,
713 literal_type: Some(lit),
714 }
715 }
716
717 #[test]
718 fn test_nullable_boolean_literal_textify() {
719 let ctx = TestContext::new();
720 assert_eq!(
721 ctx.textify_no_errors(&nullable_literal(expr::literal::LiteralType::Boolean(true))),
722 "true:boolean?"
723 );
724 assert_eq!(
725 ctx.textify_no_errors(&nullable_literal(expr::literal::LiteralType::Boolean(
726 false
727 ))),
728 "false:boolean?"
729 );
730 }
731
732 #[test]
733 fn test_nullable_integer_literal_textify() {
734 let ctx = TestContext::new();
735 assert_eq!(
736 ctx.textify_no_errors(&nullable_literal(expr::literal::LiteralType::I32(78))),
737 "78:i32?"
738 );
739 assert_eq!(
740 ctx.textify_no_errors(&nullable_literal(expr::literal::LiteralType::I64(42))),
741 "42:i64?"
742 );
743 }
744
745 #[test]
746 fn test_nullable_float_literal_textify() {
747 let ctx = TestContext::new();
748 assert_eq!(
749 ctx.textify_no_errors(&nullable_literal(expr::literal::LiteralType::Fp32(2.5))),
750 "2.5:fp32?"
751 );
752 assert_eq!(
753 ctx.textify_no_errors(&nullable_literal(expr::literal::LiteralType::Fp64(3.19))),
754 "3.19:fp64?"
755 );
756 }
757
758 #[test]
759 fn test_expression_textify() {
760 let ctx = TestContext::new();
761
762 let expr_empty = Expression { rex_type: None }; let (s, errs) = ctx.textify(&expr_empty);
765 assert!(!errs.is_empty());
766 assert_eq!(s, "!{RexType}");
767
768 let expr_lit = Expression {
770 rex_type: Some(RexType::Literal(expr::Literal {
771 nullable: false,
772 type_variation_reference: 0,
773 literal_type: Some(expr::literal::LiteralType::Boolean(true)),
774 })),
775 };
776 assert_eq!(ctx.textify_no_errors(&expr_lit), "true");
777 }
778
779 #[test]
780 fn test_rextype_textify() {
781 let ctx = TestContext::new();
782
783 let func = RexType::ScalarFunction(ScalarFunction {
784 function_reference: 1000, arguments: vec![],
786 options: vec![],
787 output_type: None,
788 #[allow(deprecated)]
789 args: vec![],
790 });
791 let (s, errq) = ctx.textify(&func);
796 let errs: Vec<_> = errq.0;
797 match errs[0] {
798 FormatError::Lookup(MissingReference::MissingAnchor(k, a)) => {
799 assert_eq!(k, ExtensionKind::Function);
800 assert_eq!(a, 1000);
801 }
802 _ => panic!("Expected Lookup MissingAnchor: {}", errs[0]),
803 }
804 assert_eq!(s, "!{function}#1000()");
805
806 let ctx = ctx.with_urn(1, "first").with_function(1, 100, "first");
807 let func = RexType::ScalarFunction(ScalarFunction {
808 function_reference: 100,
809 arguments: vec![],
810 options: vec![],
811 output_type: None,
812 #[allow(deprecated)]
813 args: vec![],
814 });
815 let s = ctx.textify_no_errors(&func);
816 assert_eq!(s, "first()");
817
818 let options_show_anchor = Default::default();
820
821 let ctx = TestContext::new()
822 .with_options(options_show_anchor)
823 .with_urn(1, "somewhere_on_the_internet")
824 .with_urn(2, "somewhere_else")
825 .with_function(1, 231, "duplicated")
826 .with_function(2, 232, "duplicated");
827
828 let rex_dup = RexType::ScalarFunction(ScalarFunction {
829 function_reference: 231,
830 arguments: vec![FunctionArgument {
831 arg_type: Some(ArgType::Value(Expression {
832 rex_type: Some(RexType::Literal(expr::Literal {
833 nullable: false,
834 type_variation_reference: 0,
835 literal_type: Some(expr::literal::LiteralType::Boolean(true)),
836 })),
837 })),
838 }],
839 options: vec![],
840 output_type: None,
841 #[allow(deprecated)]
842 args: vec![],
843 });
844 let s = ctx.textify_no_errors(&rex_dup);
845 assert_eq!(s, "duplicated#231(true)");
846 }
847
848 #[test]
849 fn test_ifthen_textify() {
850 let ctx = TestContext::new();
851
852 let if_then = IfThen {
853 ifs: vec![
854 if_then::IfClause {
855 r#if: Some(literal_bool(true)),
856 then: Some(literal_bool(false)),
857 },
858 if_then::IfClause {
859 r#if: Some(literal_bool(false)),
860 then: Some(literal_bool(true)),
861 },
862 ],
863 r#else: Some(Box::new(literal_bool(true))),
864 };
865
866 let s = ctx.textify_no_errors(&if_then);
867 assert_eq!(s, "if_then(true -> false, false -> true, _ -> true)");
868 }
869
870 #[test]
871 fn test_ifthen_textify_missing_else() {
872 let ctx = TestContext::new();
873
874 let if_then = IfThen {
875 ifs: vec![if_then::IfClause {
876 r#if: Some(literal_bool(true)),
877 then: Some(literal_bool(false)),
878 }],
879 r#else: None,
880 };
881
882 let (s, errs) = ctx.textify(&if_then);
883 assert_eq!(s, "if_then(true -> false, _ -> !{Expression})");
884 assert_eq!(errs.0.len(), 1);
885 }
886
887 fn make_i32_type() -> Type {
888 Type {
889 kind: Some(Kind::I32(I32 {
890 nullability: Nullability::Required as i32,
891 type_variation_reference: 0,
892 })),
893 }
894 }
895
896 fn make_i16_type() -> Type {
897 Type {
898 kind: Some(Kind::I16(I16 {
899 nullability: Nullability::Required as i32,
900 type_variation_reference: 0,
901 })),
902 }
903 }
904
905 fn literal_i32(value: i32) -> Expression {
906 Expression {
907 rex_type: Some(RexType::Literal(expr::Literal {
908 nullable: false,
909 type_variation_reference: 0,
910 literal_type: Some(expr::literal::LiteralType::I32(value)),
911 })),
912 }
913 }
914
915 #[test]
916 fn test_cast_textify() {
917 let ctx = TestContext::new();
918 let cast = Cast {
919 r#type: Some(make_i16_type()),
920 input: Some(Box::new(literal_i32(78))),
921 failure_behavior: 0,
922 };
923 assert_eq!(ctx.textify_no_errors(&cast), "(78:i32)::i16");
924 }
925
926 #[test]
927 fn test_cast_textify_via_rextype() {
928 let ctx = TestContext::new();
929 let rex = RexType::Cast(Box::new(Cast {
930 r#type: Some(make_i16_type()),
931 input: Some(Box::new(literal_i32(78))),
932 failure_behavior: 0,
933 }));
934 assert_eq!(ctx.textify_no_errors(&rex), "(78:i32)::i16");
935 }
936
937 #[test]
938 fn test_cast_textify_nested() {
939 let ctx = TestContext::new();
941 let inner_cast = Expression {
942 rex_type: Some(RexType::Cast(Box::new(Cast {
943 r#type: Some(make_i16_type()),
944 input: Some(Box::new(literal_i32(78))),
945 failure_behavior: 0,
946 }))),
947 };
948 let outer_cast = Cast {
949 r#type: Some(make_i32_type()),
950 input: Some(Box::new(inner_cast)),
951 failure_behavior: 0,
952 };
953 assert_eq!(ctx.textify_no_errors(&outer_cast), "((78:i32)::i16)::i32");
954 }
955
956 #[test]
957 fn test_cast_textify_return_null() {
958 let ctx = TestContext::new();
959 let cast = Cast {
960 r#type: Some(make_i16_type()),
961 input: Some(Box::new(literal_i32(78))),
962 failure_behavior: cast::FailureBehavior::ReturnNull as i32,
963 };
964 assert_eq!(ctx.textify_no_errors(&cast), "(78:i32)::?i16");
965 }
966
967 #[test]
968 fn test_cast_textify_throw_exception() {
969 let ctx = TestContext::new();
970 let cast = Cast {
971 r#type: Some(make_i16_type()),
972 input: Some(Box::new(literal_i32(78))),
973 failure_behavior: cast::FailureBehavior::ThrowException as i32,
974 };
975 assert_eq!(ctx.textify_no_errors(&cast), "(78:i32)::!i16");
976 }
977
978 #[test]
979 fn test_cast_textify_missing_input() {
980 let ctx = TestContext::new();
981 let cast = Cast {
982 r#type: Some(make_i16_type()),
983 input: None,
984 failure_behavior: 0,
985 };
986 let (s, errs) = ctx.textify(&cast);
987 assert_eq!(s, "(!{Expression})::i16");
988 match &errs.0[0] {
989 FormatError::Format(e) => {
990 assert_eq!(e.message, "Expression");
991 assert_eq!(e.error_type, FormatErrorType::InvalidValue);
992 }
993 other => panic!("Expected Format(InvalidValue) for missing input, got: {other}"),
994 }
995 }
996
997 #[test]
998 fn test_cast_textify_missing_type() {
999 let ctx = TestContext::new();
1000 let cast = Cast {
1001 r#type: None,
1002 input: Some(Box::new(literal_i32(78))),
1003 failure_behavior: 0,
1004 };
1005 let (s, errs) = ctx.textify(&cast);
1006 assert_eq!(s, "(78:i32)::!{Type}");
1007 match &errs.0[0] {
1008 FormatError::Format(e) => {
1009 assert_eq!(e.message, "Type");
1010 assert_eq!(e.error_type, FormatErrorType::InvalidValue);
1011 }
1012 other => panic!("Expected Format(InvalidValue) for missing type, got: {other}"),
1013 }
1014 }
1015
1016 fn struct_field_reference(field: i32) -> FieldReference {
1017 FieldReference {
1018 reference_type: Some(ReferenceType::DirectReference(ReferenceSegment {
1019 reference_type: Some(reference_segment::ReferenceType::StructField(Box::new(
1020 reference_segment::StructField { field, child: None },
1021 ))),
1022 })),
1023 root_type: Some(RootType::RootReference(RootReference {})),
1024 }
1025 }
1026
1027 #[test]
1028 fn test_field_reference_missing_root_type() {
1029 let ctx = TestContext::new();
1030 let mut fr = struct_field_reference(3);
1031 fr.root_type = None;
1032 let (s, errs) = ctx.textify(&fr);
1033 assert_eq!(s, "!{FieldReference}");
1034 match &errs.0[0] {
1035 FormatError::Format(e) => {
1036 assert_eq!(e.message, "FieldReference");
1037 assert_eq!(e.error_type, FormatErrorType::InvalidValue);
1038 }
1039 other => panic!("Expected Format(InvalidValue) for missing root_type, got: {other}"),
1040 }
1041 }
1042
1043 #[test]
1044 fn test_field_reference_root_reference() {
1045 let ctx = TestContext::new();
1046 let fr = struct_field_reference(3);
1047 assert_eq!(ctx.textify_no_errors(&fr), "$3");
1048 }
1049
1050 #[test]
1051 fn test_field_reference_outer_reference_unimplemented() {
1052 use substrait::proto::expression::field_reference;
1053
1054 let ctx = TestContext::new();
1055 let mut fr = struct_field_reference(3);
1056 fr.root_type = Some(RootType::OuterReference(field_reference::OuterReference {
1057 steps_out: 1,
1058 }));
1059 let (s, errs) = ctx.textify(&fr);
1060 assert_eq!(s, "!{FieldReference}");
1061 match &errs.0[0] {
1062 FormatError::Format(e) => {
1063 assert_eq!(e.message, "FieldReference");
1064 assert_eq!(e.error_type, FormatErrorType::Unimplemented);
1065 }
1066 other => panic!("Expected Format(Unimplemented) for OuterReference, got: {other}"),
1067 }
1068 }
1069
1070 #[test]
1071 fn test_field_reference_expression_unimplemented() {
1072 let ctx = TestContext::new();
1073 let mut fr = struct_field_reference(3);
1074 fr.root_type = Some(RootType::Expression(Box::new(literal_bool(true))));
1075 let (s, errs) = ctx.textify(&fr);
1076 assert_eq!(s, "!{FieldReference}");
1077 match &errs.0[0] {
1078 FormatError::Format(e) => {
1079 assert_eq!(e.message, "FieldReference");
1080 assert_eq!(e.error_type, FormatErrorType::Unimplemented);
1081 }
1082 other => panic!("Expected Format(Unimplemented) for Expression, got: {other}"),
1083 }
1084 }
1085
1086 #[test]
1087 fn test_cast_textify_invalid_failure_behavior() {
1088 let ctx = TestContext::new();
1089 let cast = Cast {
1090 r#type: Some(make_i16_type()),
1091 input: Some(Box::new(literal_i32(78))),
1092 failure_behavior: 99,
1093 };
1094 let (s, errs) = ctx.textify(&cast);
1095 assert_eq!(s, "(78:i32)::!{Cast}i16");
1097 match &errs.0[0] {
1098 FormatError::Format(e) => {
1099 assert_eq!(e.message, "Cast");
1100 assert_eq!(e.error_type, FormatErrorType::InvalidValue);
1101 }
1102 other => {
1103 panic!("Expected Format(InvalidValue) for invalid failure_behavior, got: {other}")
1104 }
1105 }
1106 }
1107}