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