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