iceberg/transform/
truncate.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::sync::Arc;
19
20use arrow_array::ArrayRef;
21use arrow_schema::DataType;
22
23use super::TransformFunction;
24use crate::Error;
25use crate::spec::{Datum, PrimitiveLiteral};
26
27#[derive(Debug)]
28pub struct Truncate {
29    width: u32,
30}
31
32impl Truncate {
33    pub fn new(width: u32) -> Self {
34        Self { width }
35    }
36
37    #[inline]
38    fn truncate_str(s: &str, width: usize) -> &str {
39        match s.char_indices().nth(width) {
40            None => s,
41            Some((idx, _)) => &s[..idx],
42        }
43    }
44
45    #[inline]
46    fn truncate_binary(s: &[u8], width: usize) -> &[u8] {
47        if s.len() > width { &s[0..width] } else { s }
48    }
49
50    #[inline]
51    fn truncate_i32(v: i32, width: i32) -> i32 {
52        v - v.rem_euclid(width)
53    }
54
55    #[inline]
56    fn truncate_i64(v: i64, width: i64) -> i64 {
57        v - (((v % width) + width) % width)
58    }
59
60    #[inline]
61    fn truncate_decimal_i128(v: i128, width: i128) -> i128 {
62        v - (((v % width) + width) % width)
63    }
64}
65
66impl TransformFunction for Truncate {
67    fn transform(&self, input: ArrayRef) -> crate::Result<ArrayRef> {
68        match input.data_type() {
69            DataType::Int32 => {
70                let width: i32 = self.width.try_into().map_err(|_| {
71                    Error::new(
72                        crate::ErrorKind::DataInvalid,
73                        "width is failed to convert to i32 when truncate Int32Array",
74                    )
75                })?;
76                let res: arrow_array::Int32Array = input
77                    .as_any()
78                    .downcast_ref::<arrow_array::Int32Array>()
79                    .unwrap()
80                    .unary(|v| Self::truncate_i32(v, width));
81                Ok(Arc::new(res))
82            }
83            DataType::Int64 => {
84                let width = self.width as i64;
85                let res: arrow_array::Int64Array = input
86                    .as_any()
87                    .downcast_ref::<arrow_array::Int64Array>()
88                    .unwrap()
89                    .unary(|v| Self::truncate_i64(v, width));
90                Ok(Arc::new(res))
91            }
92            DataType::Decimal128(precision, scale) => {
93                let width = self.width as i128;
94                let res: arrow_array::Decimal128Array = input
95                    .as_any()
96                    .downcast_ref::<arrow_array::Decimal128Array>()
97                    .unwrap()
98                    .unary(|v| Self::truncate_decimal_i128(v, width))
99                    .with_precision_and_scale(*precision, *scale)
100                    .map_err(|err| Error::new(crate::ErrorKind::Unexpected, format!("{err}")))?;
101                Ok(Arc::new(res))
102            }
103            DataType::Utf8 => {
104                let len = self.width as usize;
105                let res: arrow_array::StringArray = arrow_array::StringArray::from_iter(
106                    input
107                        .as_any()
108                        .downcast_ref::<arrow_array::StringArray>()
109                        .unwrap()
110                        .iter()
111                        .map(|v| v.map(|v| Self::truncate_str(v, len))),
112                );
113                Ok(Arc::new(res))
114            }
115            DataType::LargeUtf8 => {
116                let len = self.width as usize;
117                let res: arrow_array::LargeStringArray = arrow_array::LargeStringArray::from_iter(
118                    input
119                        .as_any()
120                        .downcast_ref::<arrow_array::LargeStringArray>()
121                        .unwrap()
122                        .iter()
123                        .map(|v| v.map(|v| Self::truncate_str(v, len))),
124                );
125                Ok(Arc::new(res))
126            }
127            DataType::Binary => {
128                let len = self.width as usize;
129                let res: arrow_array::BinaryArray = arrow_array::BinaryArray::from_iter(
130                    input
131                        .as_any()
132                        .downcast_ref::<arrow_array::BinaryArray>()
133                        .unwrap()
134                        .iter()
135                        .map(|v| v.map(|v| Self::truncate_binary(v, len))),
136                );
137                Ok(Arc::new(res))
138            }
139            _ => Err(crate::Error::new(
140                crate::ErrorKind::FeatureUnsupported,
141                format!(
142                    "Unsupported data type for truncate transform: {:?}",
143                    input.data_type()
144                ),
145            )),
146        }
147    }
148
149    fn transform_literal(&self, input: &Datum) -> crate::Result<Option<Datum>> {
150        match input.literal() {
151            PrimitiveLiteral::Int(v) => Ok(Some({
152                let width: i32 = self.width.try_into().map_err(|_| {
153                    Error::new(
154                        crate::ErrorKind::DataInvalid,
155                        "width is failed to convert to i32 when truncate Int32Array",
156                    )
157                })?;
158                Datum::int(Self::truncate_i32(*v, width))
159            })),
160            PrimitiveLiteral::Long(v) => Ok(Some({
161                let width = self.width as i64;
162                Datum::long(Self::truncate_i64(*v, width))
163            })),
164            PrimitiveLiteral::Int128(v) => Ok(Some({
165                let width = self.width as i128;
166                Datum::decimal(Self::truncate_decimal_i128(*v, width))?
167            })),
168            PrimitiveLiteral::String(v) => Ok(Some({
169                let len = self.width as usize;
170                Datum::string(Self::truncate_str(v, len).to_string())
171            })),
172            _ => Err(crate::Error::new(
173                crate::ErrorKind::FeatureUnsupported,
174                format!(
175                    "Unsupported data type for truncate transform: {:?}",
176                    input.data_type()
177                ),
178            )),
179        }
180    }
181}
182
183#[cfg(test)]
184mod test {
185    use std::sync::Arc;
186
187    use arrow_array::builder::PrimitiveBuilder;
188    use arrow_array::types::Decimal128Type;
189    use arrow_array::{Decimal128Array, Int32Array, Int64Array};
190
191    use crate::Result;
192    use crate::expr::PredicateOperator;
193    use crate::spec::PrimitiveType::{
194        Binary, Date, Decimal, Fixed, Int, Long, String as StringType, Time, Timestamp,
195        TimestampNs, Timestamptz, TimestamptzNs, Uuid,
196    };
197    use crate::spec::Type::{Primitive, Struct};
198    use crate::spec::{Datum, NestedField, PrimitiveType, StructType, Transform, Type};
199    use crate::transform::TransformFunction;
200    use crate::transform::test::{TestProjectionFixture, TestTransformFixture};
201
202    #[test]
203    fn test_truncate_transform() {
204        let trans = Transform::Truncate(4);
205
206        let fixture = TestTransformFixture {
207            display: "truncate[4]".to_string(),
208            json: r#""truncate[4]""#.to_string(),
209            dedup_name: "truncate[4]".to_string(),
210            preserves_order: true,
211            satisfies_order_of: vec![
212                (Transform::Truncate(4), true),
213                (Transform::Truncate(2), false),
214                (Transform::Bucket(4), false),
215                (Transform::Void, false),
216                (Transform::Day, false),
217            ],
218            trans_types: vec![
219                (Primitive(Binary), Some(Primitive(Binary))),
220                (Primitive(Date), None),
221                (
222                    Primitive(Decimal {
223                        precision: 8,
224                        scale: 5,
225                    }),
226                    Some(Primitive(Decimal {
227                        precision: 8,
228                        scale: 5,
229                    })),
230                ),
231                (Primitive(Fixed(8)), None),
232                (Primitive(Int), Some(Primitive(Int))),
233                (Primitive(Long), Some(Primitive(Long))),
234                (Primitive(StringType), Some(Primitive(StringType))),
235                (Primitive(Uuid), None),
236                (Primitive(Time), None),
237                (Primitive(Timestamp), None),
238                (Primitive(Timestamptz), None),
239                (Primitive(TimestampNs), None),
240                (Primitive(TimestamptzNs), None),
241                (
242                    Struct(StructType::new(vec![
243                        NestedField::optional(1, "a", Primitive(Timestamp)).into(),
244                    ])),
245                    None,
246                ),
247            ],
248        };
249
250        fixture.assert_transform(trans);
251    }
252
253    #[test]
254    fn test_projection_truncate_string_rewrite_op() -> Result<()> {
255        let value = "abcde";
256
257        let fixture = TestProjectionFixture::new(
258            Transform::Truncate(5),
259            "name",
260            NestedField::required(1, "value", Type::Primitive(PrimitiveType::String)),
261        );
262
263        fixture.assert_projection(
264            &fixture.binary_predicate(PredicateOperator::StartsWith, Datum::string(value)),
265            Some(r#"name = "abcde""#),
266        )?;
267
268        fixture.assert_projection(
269            &fixture.binary_predicate(PredicateOperator::NotStartsWith, Datum::string(value)),
270            Some(r#"name != "abcde""#),
271        )?;
272
273        let value = "abcdefg";
274        fixture.assert_projection(
275            &fixture.binary_predicate(PredicateOperator::StartsWith, Datum::string(value)),
276            Some(r#"name STARTS WITH "abcde""#),
277        )?;
278
279        fixture.assert_projection(
280            &fixture.binary_predicate(PredicateOperator::NotStartsWith, Datum::string(value)),
281            None,
282        )?;
283
284        Ok(())
285    }
286
287    #[test]
288    fn test_projection_truncate_string() -> Result<()> {
289        let value = "abcdefg";
290
291        let fixture = TestProjectionFixture::new(
292            Transform::Truncate(5),
293            "name",
294            NestedField::required(1, "value", Type::Primitive(PrimitiveType::String)),
295        );
296
297        fixture.assert_projection(
298            &fixture.binary_predicate(PredicateOperator::LessThan, Datum::string(value)),
299            Some(r#"name <= "abcde""#),
300        )?;
301
302        fixture.assert_projection(
303            &fixture.binary_predicate(PredicateOperator::LessThanOrEq, Datum::string(value)),
304            Some(r#"name <= "abcde""#),
305        )?;
306
307        fixture.assert_projection(
308            &fixture.binary_predicate(PredicateOperator::GreaterThan, Datum::string(value)),
309            Some(r#"name >= "abcde""#),
310        )?;
311
312        fixture.assert_projection(
313            &fixture.binary_predicate(PredicateOperator::GreaterThanOrEq, Datum::string(value)),
314            Some(r#"name >= "abcde""#),
315        )?;
316
317        fixture.assert_projection(
318            &fixture.binary_predicate(PredicateOperator::Eq, Datum::string(value)),
319            Some(r#"name = "abcde""#),
320        )?;
321
322        fixture.assert_projection(
323            &fixture.set_predicate(PredicateOperator::In, vec![
324                Datum::string(value),
325                Datum::string(format!("{value}abc")),
326            ]),
327            Some(r#"name IN ("abcde")"#),
328        )?;
329
330        fixture.assert_projection(
331            &fixture.set_predicate(PredicateOperator::NotIn, vec![
332                Datum::string(value),
333                Datum::string(format!("{value}abc")),
334            ]),
335            None,
336        )?;
337
338        Ok(())
339    }
340
341    #[test]
342    fn test_projection_truncate_upper_bound_decimal() -> Result<()> {
343        let prev = "98.99";
344        let curr = "99.99";
345        let next = "100.99";
346
347        let fixture = TestProjectionFixture::new(
348            Transform::Truncate(10),
349            "name",
350            NestedField::required(
351                1,
352                "value",
353                Type::Primitive(PrimitiveType::Decimal {
354                    precision: 9,
355                    scale: 2,
356                }),
357            ),
358        );
359
360        fixture.assert_projection(
361            &fixture.binary_predicate(PredicateOperator::LessThan, Datum::decimal_from_str(curr)?),
362            Some("name <= 9990"),
363        )?;
364
365        fixture.assert_projection(
366            &fixture.binary_predicate(
367                PredicateOperator::LessThanOrEq,
368                Datum::decimal_from_str(curr)?,
369            ),
370            Some("name <= 9990"),
371        )?;
372
373        fixture.assert_projection(
374            &fixture.binary_predicate(
375                PredicateOperator::GreaterThanOrEq,
376                Datum::decimal_from_str(curr)?,
377            ),
378            Some("name >= 9990"),
379        )?;
380
381        fixture.assert_projection(
382            &fixture.binary_predicate(PredicateOperator::Eq, Datum::decimal_from_str(curr)?),
383            Some("name = 9990"),
384        )?;
385
386        fixture.assert_projection(
387            &fixture.binary_predicate(PredicateOperator::NotEq, Datum::decimal_from_str(curr)?),
388            None,
389        )?;
390
391        fixture.assert_projection(
392            &fixture.set_predicate(PredicateOperator::In, vec![
393                Datum::decimal_from_str(prev)?,
394                Datum::decimal_from_str(curr)?,
395                Datum::decimal_from_str(next)?,
396            ]),
397            Some("name IN (9890, 9990, 10090)"),
398        )?;
399
400        fixture.assert_projection(
401            &fixture.set_predicate(PredicateOperator::NotIn, vec![
402                Datum::decimal_from_str(curr)?,
403                Datum::decimal_from_str(next)?,
404            ]),
405            None,
406        )?;
407
408        Ok(())
409    }
410
411    #[test]
412    fn test_projection_truncate_lower_bound_decimal() -> Result<()> {
413        let prev = "99.00";
414        let curr = "100.00";
415        let next = "101.00";
416
417        let fixture = TestProjectionFixture::new(
418            Transform::Truncate(10),
419            "name",
420            NestedField::required(
421                1,
422                "value",
423                Type::Primitive(PrimitiveType::Decimal {
424                    precision: 9,
425                    scale: 2,
426                }),
427            ),
428        );
429
430        fixture.assert_projection(
431            &fixture.binary_predicate(PredicateOperator::LessThan, Datum::decimal_from_str(curr)?),
432            Some("name <= 9990"),
433        )?;
434
435        fixture.assert_projection(
436            &fixture.binary_predicate(
437                PredicateOperator::LessThanOrEq,
438                Datum::decimal_from_str(curr)?,
439            ),
440            Some("name <= 10000"),
441        )?;
442
443        fixture.assert_projection(
444            &fixture.binary_predicate(
445                PredicateOperator::GreaterThanOrEq,
446                Datum::decimal_from_str(curr)?,
447            ),
448            Some("name >= 10000"),
449        )?;
450
451        fixture.assert_projection(
452            &fixture.binary_predicate(PredicateOperator::Eq, Datum::decimal_from_str(curr)?),
453            Some("name = 10000"),
454        )?;
455
456        fixture.assert_projection(
457            &fixture.binary_predicate(PredicateOperator::NotEq, Datum::decimal_from_str(curr)?),
458            None,
459        )?;
460
461        fixture.assert_projection(
462            &fixture.set_predicate(PredicateOperator::In, vec![
463                Datum::decimal_from_str(prev)?,
464                Datum::decimal_from_str(curr)?,
465                Datum::decimal_from_str(next)?,
466            ]),
467            Some("name IN (10000, 10100, 9900)"),
468        )?;
469
470        fixture.assert_projection(
471            &fixture.set_predicate(PredicateOperator::NotIn, vec![
472                Datum::decimal_from_str(curr)?,
473                Datum::decimal_from_str(next)?,
474            ]),
475            None,
476        )?;
477
478        Ok(())
479    }
480
481    #[test]
482    fn test_projection_truncate_upper_bound_long() -> Result<()> {
483        let value = 99i64;
484
485        let fixture = TestProjectionFixture::new(
486            Transform::Truncate(10),
487            "name",
488            NestedField::required(1, "value", Type::Primitive(PrimitiveType::Long)),
489        );
490
491        fixture.assert_projection(
492            &fixture.binary_predicate(PredicateOperator::LessThan, Datum::long(value)),
493            Some("name <= 90"),
494        )?;
495
496        fixture.assert_projection(
497            &fixture.binary_predicate(PredicateOperator::LessThanOrEq, Datum::long(value)),
498            Some("name <= 90"),
499        )?;
500
501        fixture.assert_projection(
502            &fixture.binary_predicate(PredicateOperator::GreaterThanOrEq, Datum::long(value)),
503            Some("name >= 90"),
504        )?;
505
506        fixture.assert_projection(
507            &fixture.binary_predicate(PredicateOperator::Eq, Datum::long(value)),
508            Some("name = 90"),
509        )?;
510
511        fixture.assert_projection(
512            &fixture.binary_predicate(PredicateOperator::NotEq, Datum::long(value)),
513            None,
514        )?;
515
516        fixture.assert_projection(
517            &fixture.set_predicate(PredicateOperator::In, vec![
518                Datum::long(value - 1),
519                Datum::long(value),
520                Datum::long(value + 1),
521            ]),
522            Some("name IN (100, 90)"),
523        )?;
524
525        fixture.assert_projection(
526            &fixture.set_predicate(PredicateOperator::NotIn, vec![
527                Datum::long(value),
528                Datum::long(value + 1),
529            ]),
530            None,
531        )?;
532
533        Ok(())
534    }
535
536    #[test]
537    fn test_projection_truncate_lower_bound_long() -> Result<()> {
538        let value = 100i64;
539
540        let fixture = TestProjectionFixture::new(
541            Transform::Truncate(10),
542            "name",
543            NestedField::required(1, "value", Type::Primitive(PrimitiveType::Long)),
544        );
545
546        fixture.assert_projection(
547            &fixture.binary_predicate(PredicateOperator::LessThan, Datum::long(value)),
548            Some("name <= 90"),
549        )?;
550
551        fixture.assert_projection(
552            &fixture.binary_predicate(PredicateOperator::LessThanOrEq, Datum::long(value)),
553            Some("name <= 100"),
554        )?;
555
556        fixture.assert_projection(
557            &fixture.binary_predicate(PredicateOperator::GreaterThanOrEq, Datum::long(value)),
558            Some("name >= 100"),
559        )?;
560
561        fixture.assert_projection(
562            &fixture.binary_predicate(PredicateOperator::Eq, Datum::long(value)),
563            Some("name = 100"),
564        )?;
565
566        fixture.assert_projection(
567            &fixture.binary_predicate(PredicateOperator::NotEq, Datum::long(value)),
568            None,
569        )?;
570
571        fixture.assert_projection(
572            &fixture.set_predicate(PredicateOperator::In, vec![
573                Datum::long(value - 1),
574                Datum::long(value),
575                Datum::long(value + 1),
576            ]),
577            Some("name IN (100, 90)"),
578        )?;
579
580        fixture.assert_projection(
581            &fixture.set_predicate(PredicateOperator::NotIn, vec![
582                Datum::long(value),
583                Datum::long(value + 1),
584            ]),
585            None,
586        )?;
587
588        Ok(())
589    }
590
591    #[test]
592    fn test_projection_truncate_upper_bound_integer() -> Result<()> {
593        let value = 99;
594
595        let fixture = TestProjectionFixture::new(
596            Transform::Truncate(10),
597            "name",
598            NestedField::required(1, "value", Type::Primitive(PrimitiveType::Int)),
599        );
600
601        fixture.assert_projection(
602            &fixture.binary_predicate(PredicateOperator::LessThan, Datum::int(value)),
603            Some("name <= 90"),
604        )?;
605
606        fixture.assert_projection(
607            &fixture.binary_predicate(PredicateOperator::LessThanOrEq, Datum::int(value)),
608            Some("name <= 90"),
609        )?;
610
611        fixture.assert_projection(
612            &fixture.binary_predicate(PredicateOperator::GreaterThanOrEq, Datum::int(value)),
613            Some("name >= 90"),
614        )?;
615
616        fixture.assert_projection(
617            &fixture.binary_predicate(PredicateOperator::Eq, Datum::int(value)),
618            Some("name = 90"),
619        )?;
620
621        fixture.assert_projection(
622            &fixture.binary_predicate(PredicateOperator::NotEq, Datum::int(value)),
623            None,
624        )?;
625
626        fixture.assert_projection(
627            &fixture.set_predicate(PredicateOperator::In, vec![
628                Datum::int(value - 1),
629                Datum::int(value),
630                Datum::int(value + 1),
631            ]),
632            Some("name IN (100, 90)"),
633        )?;
634
635        fixture.assert_projection(
636            &fixture.set_predicate(PredicateOperator::NotIn, vec![
637                Datum::int(value),
638                Datum::int(value + 1),
639            ]),
640            None,
641        )?;
642
643        Ok(())
644    }
645
646    #[test]
647    fn test_projection_truncate_lower_bound_integer() -> Result<()> {
648        let value = 100;
649
650        let fixture = TestProjectionFixture::new(
651            Transform::Truncate(10),
652            "name",
653            NestedField::required(1, "value", Type::Primitive(PrimitiveType::Int)),
654        );
655
656        fixture.assert_projection(
657            &fixture.binary_predicate(PredicateOperator::LessThan, Datum::int(value)),
658            Some("name <= 90"),
659        )?;
660
661        fixture.assert_projection(
662            &fixture.binary_predicate(PredicateOperator::LessThanOrEq, Datum::int(value)),
663            Some("name <= 100"),
664        )?;
665
666        fixture.assert_projection(
667            &fixture.binary_predicate(PredicateOperator::GreaterThanOrEq, Datum::int(value)),
668            Some("name >= 100"),
669        )?;
670
671        fixture.assert_projection(
672            &fixture.binary_predicate(PredicateOperator::Eq, Datum::int(value)),
673            Some("name = 100"),
674        )?;
675
676        fixture.assert_projection(
677            &fixture.binary_predicate(PredicateOperator::NotEq, Datum::int(value)),
678            None,
679        )?;
680
681        fixture.assert_projection(
682            &fixture.set_predicate(PredicateOperator::In, vec![
683                Datum::int(value - 1),
684                Datum::int(value),
685                Datum::int(value + 1),
686            ]),
687            Some("name IN (100, 90)"),
688        )?;
689
690        fixture.assert_projection(
691            &fixture.set_predicate(PredicateOperator::NotIn, vec![
692                Datum::int(value),
693                Datum::int(value + 1),
694            ]),
695            None,
696        )?;
697
698        Ok(())
699    }
700
701    // Test case ref from: https://iceberg.apache.org/spec/#truncate-transform-details
702    #[test]
703    fn test_truncate_simple() {
704        // test truncate int
705        let input = Arc::new(Int32Array::from(vec![1, -1]));
706        let res = super::Truncate::new(10).transform(input).unwrap();
707        assert_eq!(
708            res.as_any().downcast_ref::<Int32Array>().unwrap().value(0),
709            0
710        );
711        assert_eq!(
712            res.as_any().downcast_ref::<Int32Array>().unwrap().value(1),
713            -10
714        );
715
716        // test truncate long
717        let input = Arc::new(Int64Array::from(vec![1, -1]));
718        let res = super::Truncate::new(10).transform(input).unwrap();
719        assert_eq!(
720            res.as_any().downcast_ref::<Int64Array>().unwrap().value(0),
721            0
722        );
723        assert_eq!(
724            res.as_any().downcast_ref::<Int64Array>().unwrap().value(1),
725            -10
726        );
727
728        // test decimal
729        let mut builder = PrimitiveBuilder::<Decimal128Type>::new()
730            .with_precision_and_scale(20, 2)
731            .unwrap();
732        builder.append_value(1065);
733        let input = Arc::new(builder.finish());
734        let res = super::Truncate::new(50).transform(input).unwrap();
735        assert_eq!(
736            res.as_any()
737                .downcast_ref::<Decimal128Array>()
738                .unwrap()
739                .value(0),
740            1050
741        );
742
743        // test string
744        let input = Arc::new(arrow_array::StringArray::from(vec!["iceberg"]));
745        let res = super::Truncate::new(3).transform(input).unwrap();
746        assert_eq!(
747            res.as_any()
748                .downcast_ref::<arrow_array::StringArray>()
749                .unwrap()
750                .value(0),
751            "ice"
752        );
753
754        // test large string
755        let input = Arc::new(arrow_array::LargeStringArray::from(vec!["iceberg"]));
756        let res = super::Truncate::new(3).transform(input).unwrap();
757        assert_eq!(
758            res.as_any()
759                .downcast_ref::<arrow_array::LargeStringArray>()
760                .unwrap()
761                .value(0),
762            "ice"
763        );
764
765        // test binary
766        let input = Arc::new(arrow_array::BinaryArray::from_vec(vec![b"iceberg"]));
767        let res = super::Truncate::new(3).transform(input).unwrap();
768        assert_eq!(
769            res.as_any()
770                .downcast_ref::<arrow_array::BinaryArray>()
771                .unwrap()
772                .value(0),
773            b"ice"
774        );
775    }
776
777    #[test]
778    fn test_string_truncate() {
779        let test1 = "イロハニホヘト";
780        let test1_2_expected = "イロ";
781        assert_eq!(super::Truncate::truncate_str(test1, 2), test1_2_expected);
782
783        let test1_3_expected = "イロハ";
784        assert_eq!(super::Truncate::truncate_str(test1, 3), test1_3_expected);
785
786        let test2 = "щщаεはчωいにπάほхεろへσκζ";
787        let test2_7_expected = "щщаεはчω";
788        assert_eq!(super::Truncate::truncate_str(test2, 7), test2_7_expected);
789
790        let test3 = "\u{FFFF}\u{FFFF}";
791        assert_eq!(super::Truncate::truncate_str(test3, 2), test3);
792
793        let test4 = "\u{10000}\u{10000}";
794        let test4_1_expected = "\u{10000}";
795        assert_eq!(super::Truncate::truncate_str(test4, 1), test4_1_expected);
796    }
797
798    #[test]
799    fn test_literal_int() {
800        let input = Datum::int(1);
801        let res = super::Truncate::new(10)
802            .transform_literal(&input)
803            .unwrap()
804            .unwrap();
805        assert_eq!(res, Datum::int(0),);
806
807        let input = Datum::int(-1);
808        let res = super::Truncate::new(10)
809            .transform_literal(&input)
810            .unwrap()
811            .unwrap();
812        assert_eq!(res, Datum::int(-10),);
813    }
814
815    #[test]
816    fn test_literal_long() {
817        let input = Datum::long(1);
818        let res = super::Truncate::new(10)
819            .transform_literal(&input)
820            .unwrap()
821            .unwrap();
822        assert_eq!(res, Datum::long(0),);
823
824        let input = Datum::long(-1);
825        let res = super::Truncate::new(10)
826            .transform_literal(&input)
827            .unwrap()
828            .unwrap();
829        assert_eq!(res, Datum::long(-10),);
830    }
831
832    #[test]
833    fn test_decimal_literal() {
834        let input = Datum::decimal(1065).unwrap();
835        let res = super::Truncate::new(50)
836            .transform_literal(&input)
837            .unwrap()
838            .unwrap();
839        assert_eq!(res, Datum::decimal(1050).unwrap(),);
840    }
841
842    #[test]
843    fn test_string_literal() {
844        let input = Datum::string("iceberg".to_string());
845        let res = super::Truncate::new(3)
846            .transform_literal(&input)
847            .unwrap()
848            .unwrap();
849        assert_eq!(res, Datum::string("ice".to_string()),);
850    }
851}