Skip to main content

fluss/metadata/
datatype.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 crate::error::Error::IllegalArgument;
19use crate::error::Result;
20use serde::{Deserialize, Serialize};
21use std::fmt::{Display, Formatter};
22
23/// Data type for Fluss table.
24/// Impl reference: <todo: link>
25#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
26pub enum DataType {
27    Boolean(BooleanType),
28    TinyInt(TinyIntType),
29    SmallInt(SmallIntType),
30    Int(IntType),
31    BigInt(BigIntType),
32    Float(FloatType),
33    Double(DoubleType),
34    Char(CharType),
35    String(StringType),
36    Decimal(DecimalType),
37    Date(DateType),
38    Time(TimeType),
39    Timestamp(TimestampType),
40    TimestampLTz(TimestampLTzType),
41    Bytes(BytesType),
42    Binary(BinaryType),
43    Array(ArrayType),
44    Map(MapType),
45    Row(RowType),
46}
47
48impl DataType {
49    pub fn is_nullable(&self) -> bool {
50        match self {
51            DataType::Boolean(v) => v.nullable,
52            DataType::TinyInt(v) => v.nullable,
53            DataType::SmallInt(v) => v.nullable,
54            DataType::Int(v) => v.nullable,
55            DataType::BigInt(v) => v.nullable,
56            DataType::Decimal(v) => v.nullable,
57            DataType::Double(v) => v.nullable,
58            DataType::Float(v) => v.nullable,
59            DataType::Binary(v) => v.nullable,
60            DataType::Char(v) => v.nullable,
61            DataType::String(v) => v.nullable,
62            DataType::Date(v) => v.nullable,
63            DataType::TimestampLTz(v) => v.nullable,
64            DataType::Time(v) => v.nullable,
65            DataType::Timestamp(v) => v.nullable,
66            DataType::Array(v) => v.nullable,
67            DataType::Map(v) => v.nullable,
68            DataType::Row(v) => v.nullable,
69            DataType::Bytes(v) => v.nullable,
70        }
71    }
72
73    pub fn as_non_nullable(&self) -> Self {
74        match self {
75            DataType::Boolean(v) => DataType::Boolean(v.as_non_nullable()),
76            DataType::TinyInt(v) => DataType::TinyInt(v.as_non_nullable()),
77            DataType::SmallInt(v) => DataType::SmallInt(v.as_non_nullable()),
78            DataType::Int(v) => DataType::Int(v.as_non_nullable()),
79            DataType::BigInt(v) => DataType::BigInt(v.as_non_nullable()),
80            DataType::Decimal(v) => DataType::Decimal(v.as_non_nullable()),
81            DataType::Double(v) => DataType::Double(v.as_non_nullable()),
82            DataType::Float(v) => DataType::Float(v.as_non_nullable()),
83            DataType::Binary(v) => DataType::Binary(v.as_non_nullable()),
84            DataType::Char(v) => DataType::Char(v.as_non_nullable()),
85            DataType::String(v) => DataType::String(v.as_non_nullable()),
86            DataType::Date(v) => DataType::Date(v.as_non_nullable()),
87            DataType::TimestampLTz(v) => DataType::TimestampLTz(v.as_non_nullable()),
88            DataType::Time(v) => DataType::Time(v.as_non_nullable()),
89            DataType::Timestamp(v) => DataType::Timestamp(v.as_non_nullable()),
90            DataType::Array(v) => DataType::Array(v.as_non_nullable()),
91            DataType::Map(v) => DataType::Map(v.as_non_nullable()),
92            DataType::Row(v) => DataType::Row(v.as_non_nullable()),
93            DataType::Bytes(v) => DataType::Bytes(v.as_non_nullable()),
94        }
95    }
96}
97
98impl Display for DataType {
99    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
100        match self {
101            DataType::Boolean(v) => write!(f, "{v}"),
102            DataType::TinyInt(v) => write!(f, "{v}"),
103            DataType::SmallInt(v) => write!(f, "{v}"),
104            DataType::Int(v) => write!(f, "{v}"),
105            DataType::BigInt(v) => write!(f, "{v}"),
106            DataType::Float(v) => write!(f, "{v}"),
107            DataType::Double(v) => write!(f, "{v}"),
108            DataType::Char(v) => write!(f, "{v}"),
109            DataType::String(v) => write!(f, "{v}"),
110            DataType::Decimal(v) => write!(f, "{v}"),
111            DataType::Date(v) => write!(f, "{v}"),
112            DataType::Time(v) => write!(f, "{v}"),
113            DataType::Timestamp(v) => write!(f, "{v}"),
114            DataType::TimestampLTz(v) => write!(f, "{v}"),
115            DataType::Bytes(v) => write!(f, "{v}"),
116            DataType::Binary(v) => write!(f, "{v}"),
117            DataType::Array(v) => write!(f, "{v}"),
118            DataType::Map(v) => write!(f, "{v}"),
119            DataType::Row(v) => write!(f, "{v}"),
120        }
121    }
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
125pub struct BooleanType {
126    nullable: bool,
127}
128
129impl Default for BooleanType {
130    fn default() -> Self {
131        Self::new()
132    }
133}
134
135impl BooleanType {
136    pub fn new() -> Self {
137        Self::with_nullable(true)
138    }
139
140    pub fn with_nullable(nullable: bool) -> Self {
141        Self { nullable }
142    }
143
144    pub fn as_non_nullable(&self) -> Self {
145        Self::with_nullable(false)
146    }
147}
148
149impl Display for BooleanType {
150    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
151        write!(f, "BOOLEAN")?;
152        if !self.nullable {
153            write!(f, " NOT NULL")?;
154        }
155        Ok(())
156    }
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
160pub struct TinyIntType {
161    nullable: bool,
162}
163
164impl Default for TinyIntType {
165    fn default() -> Self {
166        Self::new()
167    }
168}
169
170impl TinyIntType {
171    pub fn new() -> Self {
172        Self::with_nullable(true)
173    }
174
175    pub fn with_nullable(nullable: bool) -> Self {
176        Self { nullable }
177    }
178
179    pub fn as_non_nullable(&self) -> Self {
180        Self::with_nullable(false)
181    }
182}
183
184impl Display for TinyIntType {
185    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
186        write!(f, "TINYINT")?;
187        if !self.nullable {
188            write!(f, " NOT NULL")?;
189        }
190        Ok(())
191    }
192}
193
194#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
195pub struct SmallIntType {
196    nullable: bool,
197}
198
199impl Default for SmallIntType {
200    fn default() -> Self {
201        Self::new()
202    }
203}
204
205impl SmallIntType {
206    pub fn new() -> Self {
207        Self::with_nullable(true)
208    }
209
210    pub fn with_nullable(nullable: bool) -> Self {
211        Self { nullable }
212    }
213
214    pub fn as_non_nullable(&self) -> Self {
215        Self::with_nullable(false)
216    }
217}
218
219impl Display for SmallIntType {
220    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
221        write!(f, "SMALLINT")?;
222        if !self.nullable {
223            write!(f, " NOT NULL")?;
224        }
225        Ok(())
226    }
227}
228
229#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
230pub struct IntType {
231    nullable: bool,
232}
233
234impl Default for IntType {
235    fn default() -> Self {
236        Self::new()
237    }
238}
239
240impl IntType {
241    pub fn new() -> Self {
242        Self::with_nullable(true)
243    }
244
245    pub fn with_nullable(nullable: bool) -> Self {
246        Self { nullable }
247    }
248
249    pub fn as_non_nullable(&self) -> Self {
250        Self::with_nullable(false)
251    }
252}
253
254impl Display for IntType {
255    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
256        write!(f, "INT")?;
257        if !self.nullable {
258            write!(f, " NOT NULL")?;
259        }
260        Ok(())
261    }
262}
263
264#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
265pub struct BigIntType {
266    nullable: bool,
267}
268
269impl Default for BigIntType {
270    fn default() -> Self {
271        Self::new()
272    }
273}
274
275impl BigIntType {
276    pub fn new() -> Self {
277        Self::with_nullable(true)
278    }
279
280    pub fn with_nullable(nullable: bool) -> Self {
281        Self { nullable }
282    }
283
284    pub fn as_non_nullable(&self) -> Self {
285        Self::with_nullable(false)
286    }
287}
288
289impl Display for BigIntType {
290    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
291        write!(f, "BIGINT")?;
292        if !self.nullable {
293            write!(f, " NOT NULL")?;
294        }
295        Ok(())
296    }
297}
298
299#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
300pub struct FloatType {
301    nullable: bool,
302}
303
304impl Default for FloatType {
305    fn default() -> Self {
306        Self::new()
307    }
308}
309
310impl FloatType {
311    pub fn new() -> Self {
312        Self::with_nullable(true)
313    }
314
315    pub fn with_nullable(nullable: bool) -> Self {
316        Self { nullable }
317    }
318
319    pub fn as_non_nullable(&self) -> Self {
320        Self::with_nullable(false)
321    }
322}
323
324impl Display for FloatType {
325    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
326        write!(f, "FLOAT")?;
327        if !self.nullable {
328            write!(f, " NOT NULL")?;
329        }
330        Ok(())
331    }
332}
333
334#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
335pub struct DoubleType {
336    nullable: bool,
337}
338
339impl Default for DoubleType {
340    fn default() -> Self {
341        Self::new()
342    }
343}
344
345impl DoubleType {
346    pub fn new() -> Self {
347        Self::with_nullable(true)
348    }
349
350    pub fn with_nullable(nullable: bool) -> Self {
351        Self { nullable }
352    }
353
354    pub fn as_non_nullable(&self) -> Self {
355        Self::with_nullable(false)
356    }
357}
358
359impl Display for DoubleType {
360    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
361        write!(f, "DOUBLE")?;
362        if !self.nullable {
363            write!(f, " NOT NULL")?;
364        }
365        Ok(())
366    }
367}
368
369#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
370pub struct CharType {
371    nullable: bool,
372    length: u32,
373}
374
375impl CharType {
376    pub fn new(length: u32) -> Self {
377        Self::with_nullable(length, true)
378    }
379
380    pub fn with_nullable(length: u32, nullable: bool) -> Self {
381        Self { nullable, length }
382    }
383
384    pub fn as_non_nullable(&self) -> Self {
385        Self::with_nullable(self.length, false)
386    }
387
388    pub fn length(&self) -> u32 {
389        self.length
390    }
391}
392
393impl Default for CharType {
394    fn default() -> Self {
395        Self::new(1)
396    }
397}
398
399impl Display for CharType {
400    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
401        write!(f, "CHAR({})", self.length)?;
402        if !self.nullable {
403            write!(f, " NOT NULL")?;
404        }
405        Ok(())
406    }
407}
408
409#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
410pub struct StringType {
411    nullable: bool,
412}
413
414impl Default for StringType {
415    fn default() -> Self {
416        Self::new()
417    }
418}
419
420impl StringType {
421    pub fn new() -> Self {
422        Self::with_nullable(true)
423    }
424
425    pub fn with_nullable(nullable: bool) -> Self {
426        Self { nullable }
427    }
428
429    pub fn as_non_nullable(&self) -> Self {
430        Self::with_nullable(false)
431    }
432}
433
434impl Display for StringType {
435    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
436        write!(f, "STRING")?;
437        if !self.nullable {
438            write!(f, " NOT NULL")?;
439        }
440        Ok(())
441    }
442}
443
444#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
445pub struct DecimalType {
446    nullable: bool,
447    precision: u32,
448    scale: u32,
449}
450
451impl DecimalType {
452    pub const MIN_PRECISION: u32 = 1;
453
454    pub const MAX_PRECISION: u32 = 38;
455
456    pub const DEFAULT_PRECISION: u32 = 10;
457
458    pub const MIN_SCALE: u32 = 0;
459
460    pub const DEFAULT_SCALE: u32 = 0;
461
462    pub fn new(precision: u32, scale: u32) -> Result<Self> {
463        Self::with_nullable(true, precision, scale)
464    }
465
466    /// Create a DecimalType with validation, returning an error if parameters are invalid.
467    pub fn with_nullable(nullable: bool, precision: u32, scale: u32) -> Result<Self> {
468        // Validate precision
469        if !(Self::MIN_PRECISION..=Self::MAX_PRECISION).contains(&precision) {
470            return Err(IllegalArgument {
471                message: format!(
472                    "Decimal precision must be between {} and {} (both inclusive), got: {}",
473                    Self::MIN_PRECISION,
474                    Self::MAX_PRECISION,
475                    precision
476                ),
477            });
478        }
479        // Validate scale
480        // Note: MIN_SCALE is 0, and scale is u32, so scale >= MIN_SCALE is always true
481        if scale > precision {
482            return Err(IllegalArgument {
483                message: format!(
484                    "Decimal scale must be between {} and the precision {} (both inclusive), got: {}",
485                    Self::MIN_SCALE,
486                    precision,
487                    scale
488                ),
489            });
490        }
491        Ok(DecimalType {
492            nullable,
493            precision,
494            scale,
495        })
496    }
497
498    pub fn precision(&self) -> u32 {
499        self.precision
500    }
501
502    pub fn scale(&self) -> u32 {
503        self.scale
504    }
505
506    pub fn as_non_nullable(&self) -> Self {
507        Self::with_nullable(false, self.precision, self.scale)
508            .expect("Invalid decimal precision or scale")
509    }
510}
511
512impl Default for DecimalType {
513    fn default() -> Self {
514        Self::new(Self::DEFAULT_PRECISION, Self::DEFAULT_SCALE)
515            .expect("Invalid default decimal precision or scale")
516    }
517}
518
519impl Display for DecimalType {
520    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
521        write!(f, "DECIMAL({}, {})", self.precision, self.scale)?;
522        if !self.nullable {
523            write!(f, " NOT NULL")?;
524        }
525        Ok(())
526    }
527}
528
529#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
530pub struct DateType {
531    nullable: bool,
532}
533
534impl Default for DateType {
535    fn default() -> Self {
536        Self::new()
537    }
538}
539
540impl DateType {
541    pub fn new() -> Self {
542        Self::with_nullable(true)
543    }
544
545    pub fn with_nullable(nullable: bool) -> Self {
546        Self { nullable }
547    }
548
549    pub fn as_non_nullable(&self) -> Self {
550        Self::with_nullable(false)
551    }
552}
553
554impl Display for DateType {
555    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
556        write!(f, "DATE")?;
557        if !self.nullable {
558            write!(f, " NOT NULL")?;
559        }
560        Ok(())
561    }
562}
563
564#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
565pub struct TimeType {
566    nullable: bool,
567    precision: u32,
568}
569
570impl Default for TimeType {
571    fn default() -> Self {
572        Self::new(Self::DEFAULT_PRECISION).expect("Invalid default time precision")
573    }
574}
575
576impl TimeType {
577    pub const MIN_PRECISION: u32 = 0;
578
579    pub const MAX_PRECISION: u32 = 9;
580
581    pub const DEFAULT_PRECISION: u32 = 0;
582
583    pub fn new(precision: u32) -> Result<Self> {
584        Self::with_nullable(true, precision)
585    }
586
587    /// Create a TimeType with validation, returning an error if precision is invalid.
588    pub fn with_nullable(nullable: bool, precision: u32) -> Result<Self> {
589        // Validate precision
590        if !(Self::MIN_PRECISION..=Self::MAX_PRECISION).contains(&precision) {
591            return Err(IllegalArgument {
592                message: format!(
593                    "Time precision must be between {} and {} (both inclusive), got: {}",
594                    Self::MIN_PRECISION,
595                    Self::MAX_PRECISION,
596                    precision
597                ),
598            });
599        }
600        Ok(TimeType {
601            nullable,
602            precision,
603        })
604    }
605
606    pub fn precision(&self) -> u32 {
607        self.precision
608    }
609
610    pub fn as_non_nullable(&self) -> Self {
611        Self::with_nullable(false, self.precision).expect("Invalid time precision")
612    }
613}
614
615impl Display for TimeType {
616    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
617        write!(f, "TIME({})", self.precision)?;
618        if !self.nullable {
619            write!(f, " NOT NULL")?;
620        }
621        Ok(())
622    }
623}
624
625#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
626pub struct TimestampType {
627    nullable: bool,
628    precision: u32,
629}
630
631impl Default for TimestampType {
632    fn default() -> Self {
633        Self::new(Self::DEFAULT_PRECISION).expect("Invalid default timestamp precision")
634    }
635}
636
637impl TimestampType {
638    pub const MIN_PRECISION: u32 = 0;
639
640    pub const MAX_PRECISION: u32 = 9;
641
642    pub const DEFAULT_PRECISION: u32 = 6;
643
644    pub fn new(precision: u32) -> Result<Self> {
645        Self::with_nullable(true, precision)
646    }
647
648    /// Create a TimestampType with validation, returning an error if precision is invalid.
649    pub fn with_nullable(nullable: bool, precision: u32) -> Result<Self> {
650        // Validate precision
651        if !(Self::MIN_PRECISION..=Self::MAX_PRECISION).contains(&precision) {
652            return Err(IllegalArgument {
653                message: format!(
654                    "Timestamp precision must be between {} and {} (both inclusive), got: {}",
655                    Self::MIN_PRECISION,
656                    Self::MAX_PRECISION,
657                    precision
658                ),
659            });
660        }
661        Ok(TimestampType {
662            nullable,
663            precision,
664        })
665    }
666
667    pub fn precision(&self) -> u32 {
668        self.precision
669    }
670
671    pub fn as_non_nullable(&self) -> Self {
672        Self::with_nullable(false, self.precision).expect("Invalid timestamp precision")
673    }
674}
675
676impl Display for TimestampType {
677    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
678        write!(f, "TIMESTAMP({})", self.precision)?;
679        if !self.nullable {
680            write!(f, " NOT NULL")?;
681        }
682        Ok(())
683    }
684}
685
686#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
687pub struct TimestampLTzType {
688    nullable: bool,
689    precision: u32,
690}
691
692impl Default for TimestampLTzType {
693    fn default() -> Self {
694        Self::new(Self::DEFAULT_PRECISION)
695            .expect("Invalid default timestamp with local time zone precision")
696    }
697}
698
699impl TimestampLTzType {
700    pub const MIN_PRECISION: u32 = 0;
701
702    pub const MAX_PRECISION: u32 = 9;
703
704    pub const DEFAULT_PRECISION: u32 = 6;
705
706    pub fn new(precision: u32) -> Result<Self> {
707        Self::with_nullable(true, precision)
708    }
709
710    /// Create a TimestampLTzType with validation, returning an error if precision is invalid.
711    pub fn with_nullable(nullable: bool, precision: u32) -> Result<Self> {
712        // Validate precision
713        if !(Self::MIN_PRECISION..=Self::MAX_PRECISION).contains(&precision) {
714            return Err(IllegalArgument {
715                message: format!(
716                    "Timestamp with local time zone precision must be between {} and {} (both inclusive), got: {}",
717                    Self::MIN_PRECISION,
718                    Self::MAX_PRECISION,
719                    precision
720                ),
721            });
722        }
723        Ok(TimestampLTzType {
724            nullable,
725            precision,
726        })
727    }
728
729    pub fn precision(&self) -> u32 {
730        self.precision
731    }
732
733    pub fn as_non_nullable(&self) -> Self {
734        Self::with_nullable(false, self.precision)
735            .expect("Invalid timestamp with local time zone precision")
736    }
737}
738
739impl Display for TimestampLTzType {
740    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
741        write!(f, "TIMESTAMP_LTZ({})", self.precision)?;
742        if !self.nullable {
743            write!(f, " NOT NULL")?;
744        }
745        Ok(())
746    }
747}
748
749#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
750pub struct BytesType {
751    nullable: bool,
752}
753
754impl Default for BytesType {
755    fn default() -> Self {
756        Self::new()
757    }
758}
759
760impl BytesType {
761    pub const fn new() -> Self {
762        Self::with_nullable(true)
763    }
764
765    pub const fn with_nullable(nullable: bool) -> Self {
766        Self { nullable }
767    }
768
769    pub fn as_non_nullable(&self) -> Self {
770        Self::with_nullable(false)
771    }
772}
773
774impl Display for BytesType {
775    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
776        write!(f, "BYTES")?;
777        if !self.nullable {
778            write!(f, " NOT NULL")?;
779        }
780        Ok(())
781    }
782}
783
784#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
785pub struct BinaryType {
786    nullable: bool,
787    length: usize,
788}
789
790impl BinaryType {
791    pub const MIN_LENGTH: usize = 1;
792
793    pub const MAX_LENGTH: usize = usize::MAX;
794
795    pub const DEFAULT_LENGTH: usize = 1;
796
797    pub fn new(length: usize) -> Self {
798        Self::with_nullable(true, length)
799    }
800
801    pub fn with_nullable(nullable: bool, length: usize) -> Self {
802        Self { nullable, length }
803    }
804
805    pub fn length(&self) -> usize {
806        self.length
807    }
808
809    pub fn as_non_nullable(&self) -> Self {
810        Self::with_nullable(false, self.length)
811    }
812}
813
814impl Default for BinaryType {
815    fn default() -> Self {
816        Self::new(Self::DEFAULT_LENGTH)
817    }
818}
819
820impl Display for BinaryType {
821    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
822        write!(f, "BINARY({})", self.length)?;
823        if !self.nullable {
824            write!(f, " NOT NULL")?;
825        }
826        Ok(())
827    }
828}
829
830#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
831pub struct ArrayType {
832    nullable: bool,
833    element_type: Box<DataType>,
834}
835
836impl ArrayType {
837    pub fn new(element_type: DataType) -> Self {
838        Self::with_nullable(true, element_type)
839    }
840
841    pub fn with_nullable(nullable: bool, element_type: DataType) -> Self {
842        Self {
843            nullable,
844            element_type: Box::new(element_type),
845        }
846    }
847
848    pub fn as_non_nullable(&self) -> Self {
849        Self {
850            nullable: false,
851            element_type: self.element_type.clone(),
852        }
853    }
854
855    pub fn get_element_type(&self) -> &DataType {
856        &self.element_type
857    }
858}
859
860impl Display for ArrayType {
861    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
862        write!(f, "ARRAY<{}>", self.element_type)?;
863        if !self.nullable {
864            write!(f, " NOT NULL")?;
865        }
866        Ok(())
867    }
868}
869
870#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)]
871pub struct MapType {
872    nullable: bool,
873    key_type: Box<DataType>,
874    value_type: Box<DataType>,
875}
876
877impl MapType {
878    pub fn new(key_type: DataType, value_type: DataType) -> Self {
879        Self::with_nullable(true, key_type, value_type)
880    }
881
882    pub fn with_nullable(nullable: bool, key_type: DataType, value_type: DataType) -> Self {
883        Self {
884            nullable,
885            key_type: Box::new(key_type),
886            value_type: Box::new(value_type),
887        }
888    }
889
890    pub fn as_non_nullable(&self) -> Self {
891        Self {
892            nullable: false,
893            key_type: self.key_type.clone(),
894            value_type: self.value_type.clone(),
895        }
896    }
897
898    pub fn key_type(&self) -> &DataType {
899        &self.key_type
900    }
901
902    pub fn value_type(&self) -> &DataType {
903        &self.value_type
904    }
905}
906
907impl Display for MapType {
908    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
909        write!(f, "MAP<{}, {}>", self.key_type, self.value_type)?;
910        if !self.nullable {
911            write!(f, " NOT NULL")?;
912        }
913        Ok(())
914    }
915}
916
917#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)]
918pub struct RowType {
919    nullable: bool,
920    fields: Vec<DataField>,
921}
922
923impl RowType {
924    pub const fn new(fields: Vec<DataField>) -> Self {
925        Self::with_nullable(true, fields)
926    }
927
928    pub const fn with_nullable(nullable: bool, fields: Vec<DataField>) -> Self {
929        Self { nullable, fields }
930    }
931
932    pub fn as_non_nullable(&self) -> Self {
933        Self::with_nullable(false, self.fields.clone())
934    }
935
936    pub fn fields(&self) -> &Vec<DataField> {
937        &self.fields
938    }
939
940    pub fn get_field_index(&self, field_name: &str) -> Option<usize> {
941        self.fields.iter().position(|f| f.name == field_name)
942    }
943
944    pub fn field_types(&self) -> impl Iterator<Item = &DataType> + '_ {
945        self.fields.iter().map(|f| &f.data_type)
946    }
947
948    pub fn get_field_names(&self) -> Vec<&str> {
949        self.fields.iter().map(|f| f.name.as_str()).collect()
950    }
951
952    pub fn project_with_field_names(&self, field_names: &[String]) -> Result<RowType> {
953        let indices: Vec<usize> = field_names
954            .iter()
955            .map(|name| {
956                self.get_field_index(name).ok_or_else(|| IllegalArgument {
957                    message: format!("Field '{name}' does not exist in the row type"),
958                })
959            })
960            .collect::<Result<Vec<_>>>()?;
961
962        self.project(indices.as_slice())
963    }
964
965    pub fn project(&self, project_field_positions: &[usize]) -> Result<RowType> {
966        Ok(RowType::with_nullable(
967            self.nullable,
968            project_field_positions
969                .iter()
970                .map(|pos| {
971                    self.fields
972                        .get(*pos)
973                        .cloned()
974                        .ok_or_else(|| IllegalArgument {
975                            message: format!("invalid field position: {}", *pos),
976                        })
977                })
978                .collect::<Result<Vec<_>>>()?,
979        ))
980    }
981
982    #[cfg(test)]
983    pub fn with_data_types(data_types: Vec<DataType>) -> Self {
984        let mut fields: Vec<DataField> = Vec::new();
985        data_types.iter().enumerate().for_each(|(idx, data_type)| {
986            fields.push(DataField::new(format!("f{idx}"), data_type.clone(), None));
987        });
988
989        Self::with_nullable(true, fields)
990    }
991
992    #[cfg(test)]
993    pub fn with_data_types_and_field_names(
994        data_types: Vec<DataType>,
995        field_names: Vec<&str>,
996    ) -> Self {
997        let fields = data_types
998            .into_iter()
999            .zip(field_names)
1000            .map(|(data_type, field_name)| {
1001                DataField::new(field_name.to_string(), data_type.clone(), None)
1002            })
1003            .collect::<Vec<_>>();
1004
1005        Self::with_nullable(true, fields)
1006    }
1007}
1008
1009impl Display for RowType {
1010    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1011        write!(f, "ROW<")?;
1012        for (i, field) in self.fields.iter().enumerate() {
1013            if i > 0 {
1014                write!(f, ", ")?;
1015            }
1016            write!(f, "{field}")?;
1017        }
1018        write!(f, ">")?;
1019        if !self.nullable {
1020            write!(f, " NOT NULL")?;
1021        }
1022        Ok(())
1023    }
1024}
1025
1026pub struct DataTypes;
1027
1028impl DataTypes {
1029    pub fn binary(length: usize) -> DataType {
1030        DataType::Binary(BinaryType::new(length))
1031    }
1032
1033    pub const fn bytes() -> DataType {
1034        DataType::Bytes(BytesType::new())
1035    }
1036
1037    pub fn boolean() -> DataType {
1038        DataType::Boolean(BooleanType::new())
1039    }
1040
1041    pub fn int() -> DataType {
1042        DataType::Int(IntType::new())
1043    }
1044
1045    /// Data type of a 1-byte signed integer with values from -128 to 127.
1046    pub fn tinyint() -> DataType {
1047        DataType::TinyInt(TinyIntType::new())
1048    }
1049
1050    /// Data type of a 2-byte signed integer with values from -32,768 to 32,767.
1051    pub fn smallint() -> DataType {
1052        DataType::SmallInt(SmallIntType::new())
1053    }
1054
1055    pub fn bigint() -> DataType {
1056        DataType::BigInt(BigIntType::new())
1057    }
1058
1059    /// Data type of a 4-byte single precision floating point number.
1060    pub fn float() -> DataType {
1061        DataType::Float(FloatType::new())
1062    }
1063
1064    /// Data type of an 8-byte double precision floating point number.
1065    pub fn double() -> DataType {
1066        DataType::Double(DoubleType::new())
1067    }
1068
1069    pub fn char(length: u32) -> DataType {
1070        DataType::Char(CharType::new(length))
1071    }
1072
1073    /// Data type of a variable-length character string.
1074    pub fn string() -> DataType {
1075        DataType::String(StringType::new())
1076    }
1077
1078    /// Data type of a decimal number with fixed precision and scale `DECIMAL(p, s)` where
1079    /// `p` is the number of digits in a number (=precision) and `s` is the number of
1080    /// digits to the right of the decimal point in a number (=scale). `p` must have a value
1081    /// between 1 and 38 (both inclusive). `s` must have a value between 0 and `p` (both inclusive).
1082    pub fn decimal(precision: u32, scale: u32) -> DataType {
1083        DataType::Decimal(DecimalType::new(precision, scale).expect("Invalid decimal parameters"))
1084    }
1085
1086    pub fn date() -> DataType {
1087        DataType::Date(DateType::new())
1088    }
1089
1090    /// Data type of a time WITHOUT time zone `TIME` with no fractional seconds by default.
1091    pub fn time() -> DataType {
1092        DataType::Time(TimeType::default())
1093    }
1094
1095    /// Data type of a time WITHOUT time zone `TIME(p)` where `p` is the number of digits
1096    /// of fractional seconds (=precision). `p` must have a value between 0 and 9 (both inclusive).
1097    pub fn time_with_precision(precision: u32) -> DataType {
1098        DataType::Time(TimeType::new(precision).expect("Invalid time precision"))
1099    }
1100
1101    /// Data type of a timestamp WITHOUT time zone `TIMESTAMP` with 6 digits of fractional
1102    /// seconds by default.
1103    pub fn timestamp() -> DataType {
1104        DataType::Timestamp(TimestampType::default())
1105    }
1106
1107    /// Data type of a timestamp WITHOUT time zone `TIMESTAMP(p)` where `p` is the number
1108    /// of digits of fractional seconds (=precision). `p` must have a value between 0 and 9
1109    /// (both inclusive).
1110    pub fn timestamp_with_precision(precision: u32) -> DataType {
1111        DataType::Timestamp(TimestampType::new(precision).expect("Invalid timestamp precision"))
1112    }
1113
1114    /// Data type of a timestamp WITH time zone `TIMESTAMP WITH TIME ZONE` with 6 digits of
1115    /// fractional seconds by default.
1116    pub fn timestamp_ltz() -> DataType {
1117        DataType::TimestampLTz(TimestampLTzType::default())
1118    }
1119
1120    /// Data type of a timestamp WITH time zone `TIMESTAMP WITH TIME ZONE(p)` where `p` is the number
1121    /// of digits of fractional seconds (=precision). `p` must have a value between 0 and 9 (both inclusive).
1122    pub fn timestamp_ltz_with_precision(precision: u32) -> DataType {
1123        DataType::TimestampLTz(
1124            TimestampLTzType::new(precision)
1125                .expect("Invalid timestamp with local time zone precision"),
1126        )
1127    }
1128
1129    /// Data type of an array of elements with same subtype.
1130    pub fn array(element: DataType) -> DataType {
1131        DataType::Array(ArrayType::new(element))
1132    }
1133
1134    /// Data type of an associative array that maps keys to values.
1135    pub fn map(key_type: DataType, value_type: DataType) -> DataType {
1136        DataType::Map(MapType::new(key_type, value_type))
1137    }
1138
1139    /// Field definition with field name and data type.
1140    pub fn field<N: Into<String>>(name: N, data_type: DataType) -> DataField {
1141        DataField::new(name, data_type, None)
1142    }
1143
1144    /// Field definition with field name, data type, and a description.
1145    pub fn field_with_description<N: Into<String>>(
1146        name: N,
1147        data_type: DataType,
1148        description: String,
1149    ) -> DataField {
1150        DataField::new(name, data_type, Some(description))
1151    }
1152
1153    /// Data type of a sequence of fields.
1154    pub fn row(fields: Vec<DataField>) -> DataType {
1155        DataType::Row(RowType::new(fields))
1156    }
1157
1158    /// Data type of a sequence of fields with generated field names (f0, f1, f2, ...).
1159    pub fn row_from_types(field_types: Vec<DataType>) -> DataType {
1160        let fields = field_types
1161            .into_iter()
1162            .enumerate()
1163            .map(|(i, dt)| DataField::new(format!("f{i}"), dt, None))
1164            .collect();
1165        DataType::Row(RowType::new(fields))
1166    }
1167}
1168
1169#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
1170pub struct DataField {
1171    pub name: String,
1172    pub data_type: DataType,
1173    pub description: Option<String>,
1174}
1175
1176impl DataField {
1177    pub fn new<N: Into<String>>(
1178        name: N,
1179        data_type: DataType,
1180        description: Option<String>,
1181    ) -> DataField {
1182        DataField {
1183            name: name.into(),
1184            data_type,
1185            description,
1186        }
1187    }
1188
1189    pub fn name(&self) -> &str {
1190        &self.name
1191    }
1192
1193    pub fn data_type(&self) -> &DataType {
1194        &self.data_type
1195    }
1196}
1197
1198impl Display for DataField {
1199    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1200        write!(f, "{} {}", self.name, self.data_type)
1201    }
1202}
1203
1204#[test]
1205fn test_primitive_types_display() {
1206    // Test simple primitive types with nullable and non-nullable variants
1207    assert_eq!(BooleanType::new().to_string(), "BOOLEAN");
1208    assert_eq!(
1209        BooleanType::with_nullable(false).to_string(),
1210        "BOOLEAN NOT NULL"
1211    );
1212
1213    assert_eq!(TinyIntType::new().to_string(), "TINYINT");
1214    assert_eq!(
1215        TinyIntType::with_nullable(false).to_string(),
1216        "TINYINT NOT NULL"
1217    );
1218
1219    assert_eq!(SmallIntType::new().to_string(), "SMALLINT");
1220    assert_eq!(
1221        SmallIntType::with_nullable(false).to_string(),
1222        "SMALLINT NOT NULL"
1223    );
1224
1225    assert_eq!(IntType::new().to_string(), "INT");
1226    assert_eq!(IntType::with_nullable(false).to_string(), "INT NOT NULL");
1227
1228    assert_eq!(BigIntType::new().to_string(), "BIGINT");
1229    assert_eq!(
1230        BigIntType::with_nullable(false).to_string(),
1231        "BIGINT NOT NULL"
1232    );
1233
1234    assert_eq!(FloatType::new().to_string(), "FLOAT");
1235    assert_eq!(
1236        FloatType::with_nullable(false).to_string(),
1237        "FLOAT NOT NULL"
1238    );
1239
1240    assert_eq!(DoubleType::new().to_string(), "DOUBLE");
1241    assert_eq!(
1242        DoubleType::with_nullable(false).to_string(),
1243        "DOUBLE NOT NULL"
1244    );
1245
1246    assert_eq!(StringType::new().to_string(), "STRING");
1247    assert_eq!(
1248        StringType::with_nullable(false).to_string(),
1249        "STRING NOT NULL"
1250    );
1251
1252    assert_eq!(DateType::new().to_string(), "DATE");
1253    assert_eq!(DateType::with_nullable(false).to_string(), "DATE NOT NULL");
1254
1255    assert_eq!(BytesType::new().to_string(), "BYTES");
1256    assert_eq!(
1257        BytesType::with_nullable(false).to_string(),
1258        "BYTES NOT NULL"
1259    );
1260}
1261
1262#[test]
1263fn test_parameterized_types_display() {
1264    // Test types with parameters (length, precision, scale, etc.)
1265    assert_eq!(CharType::new(10).to_string(), "CHAR(10)");
1266    assert_eq!(
1267        CharType::with_nullable(20, false).to_string(),
1268        "CHAR(20) NOT NULL"
1269    );
1270
1271    assert_eq!(BinaryType::new(100).to_string(), "BINARY(100)");
1272    assert_eq!(
1273        BinaryType::with_nullable(false, 256).to_string(),
1274        "BINARY(256) NOT NULL"
1275    );
1276
1277    assert_eq!(
1278        DecimalType::new(10, 2).unwrap().to_string(),
1279        "DECIMAL(10, 2)"
1280    );
1281    assert_eq!(
1282        DecimalType::with_nullable(false, 38, 10)
1283            .unwrap()
1284            .to_string(),
1285        "DECIMAL(38, 10) NOT NULL"
1286    );
1287
1288    assert_eq!(TimeType::new(0).unwrap().to_string(), "TIME(0)");
1289    assert_eq!(TimeType::new(3).unwrap().to_string(), "TIME(3)");
1290    assert_eq!(
1291        TimeType::with_nullable(false, 9).unwrap().to_string(),
1292        "TIME(9) NOT NULL"
1293    );
1294
1295    assert_eq!(TimestampType::new(6).unwrap().to_string(), "TIMESTAMP(6)");
1296    assert_eq!(TimestampType::new(0).unwrap().to_string(), "TIMESTAMP(0)");
1297    assert_eq!(
1298        TimestampType::with_nullable(false, 9).unwrap().to_string(),
1299        "TIMESTAMP(9) NOT NULL"
1300    );
1301
1302    assert_eq!(
1303        TimestampLTzType::new(6).unwrap().to_string(),
1304        "TIMESTAMP_LTZ(6)"
1305    );
1306    assert_eq!(
1307        TimestampLTzType::new(3).unwrap().to_string(),
1308        "TIMESTAMP_LTZ(3)"
1309    );
1310    assert_eq!(
1311        TimestampLTzType::with_nullable(false, 9)
1312            .unwrap()
1313            .to_string(),
1314        "TIMESTAMP_LTZ(9) NOT NULL"
1315    );
1316}
1317
1318#[test]
1319fn test_array_display() {
1320    let array_type = ArrayType::new(DataTypes::int());
1321    assert_eq!(array_type.to_string(), "ARRAY<INT>");
1322
1323    let array_type_non_null = ArrayType::with_nullable(false, DataTypes::string());
1324    assert_eq!(array_type_non_null.to_string(), "ARRAY<STRING> NOT NULL");
1325
1326    let nested_array = ArrayType::new(DataTypes::array(DataTypes::int()));
1327    assert_eq!(nested_array.to_string(), "ARRAY<ARRAY<INT>>");
1328}
1329
1330#[test]
1331fn test_map_display() {
1332    let map_type = MapType::new(DataTypes::string(), DataTypes::int());
1333    assert_eq!(map_type.to_string(), "MAP<STRING, INT>");
1334
1335    let map_type_non_null = MapType::with_nullable(false, DataTypes::int(), DataTypes::string());
1336    assert_eq!(map_type_non_null.to_string(), "MAP<INT, STRING> NOT NULL");
1337
1338    let nested_map = MapType::new(
1339        DataTypes::string(),
1340        DataTypes::map(DataTypes::int(), DataTypes::boolean()),
1341    );
1342    assert_eq!(nested_map.to_string(), "MAP<STRING, MAP<INT, BOOLEAN>>");
1343}
1344
1345#[test]
1346fn test_row_display() {
1347    let fields = vec![
1348        DataTypes::field("id", DataTypes::int()),
1349        DataTypes::field("name", DataTypes::string()),
1350    ];
1351    let row_type = RowType::new(fields);
1352    assert_eq!(row_type.to_string(), "ROW<id INT, name STRING>");
1353
1354    let fields_non_null = vec![DataTypes::field("age", DataTypes::bigint())];
1355    let row_type_non_null = RowType::with_nullable(false, fields_non_null);
1356    assert_eq!(row_type_non_null.to_string(), "ROW<age BIGINT> NOT NULL");
1357}
1358
1359#[test]
1360fn test_datatype_display() {
1361    assert_eq!(DataTypes::boolean().to_string(), "BOOLEAN");
1362    assert_eq!(DataTypes::int().to_string(), "INT");
1363    assert_eq!(DataTypes::string().to_string(), "STRING");
1364    assert_eq!(DataTypes::char(50).to_string(), "CHAR(50)");
1365    assert_eq!(DataTypes::decimal(10, 2).to_string(), "DECIMAL(10, 2)");
1366    assert_eq!(DataTypes::time_with_precision(3).to_string(), "TIME(3)");
1367    assert_eq!(
1368        DataTypes::timestamp_with_precision(6).to_string(),
1369        "TIMESTAMP(6)"
1370    );
1371    assert_eq!(
1372        DataTypes::timestamp_ltz_with_precision(9).to_string(),
1373        "TIMESTAMP_LTZ(9)"
1374    );
1375    assert_eq!(DataTypes::array(DataTypes::int()).to_string(), "ARRAY<INT>");
1376    assert_eq!(
1377        DataTypes::map(DataTypes::string(), DataTypes::int()).to_string(),
1378        "MAP<STRING, INT>"
1379    );
1380}
1381
1382#[test]
1383fn test_datafield_display() {
1384    let field = DataTypes::field("user_id", DataTypes::bigint());
1385    assert_eq!(field.to_string(), "user_id BIGINT");
1386
1387    let field2 = DataTypes::field("email", DataTypes::string());
1388    assert_eq!(field2.to_string(), "email STRING");
1389
1390    let field3 = DataTypes::field("score", DataTypes::decimal(10, 2));
1391    assert_eq!(field3.to_string(), "score DECIMAL(10, 2)");
1392}
1393
1394#[test]
1395fn test_complex_nested_display() {
1396    let row_type = DataTypes::row(vec![
1397        DataTypes::field("id", DataTypes::int()),
1398        DataTypes::field("tags", DataTypes::array(DataTypes::string())),
1399        DataTypes::field(
1400            "metadata",
1401            DataTypes::map(DataTypes::string(), DataTypes::string()),
1402        ),
1403    ]);
1404    assert_eq!(
1405        row_type.to_string(),
1406        "ROW<id INT, tags ARRAY<STRING>, metadata MAP<STRING, STRING>>"
1407    );
1408}
1409
1410#[test]
1411fn test_non_nullable_datatype() {
1412    let nullable_int = DataTypes::int();
1413    assert_eq!(nullable_int.to_string(), "INT");
1414
1415    let non_nullable_int = nullable_int.as_non_nullable();
1416    assert_eq!(non_nullable_int.to_string(), "INT NOT NULL");
1417}
1418
1419#[test]
1420fn test_deeply_nested_types() {
1421    let nested = DataTypes::array(DataTypes::map(
1422        DataTypes::string(),
1423        DataTypes::row(vec![
1424            DataTypes::field("x", DataTypes::int()),
1425            DataTypes::field("y", DataTypes::int()),
1426        ]),
1427    ));
1428    assert_eq!(nested.to_string(), "ARRAY<MAP<STRING, ROW<x INT, y INT>>>");
1429}
1430
1431// ============================================================================
1432// DecimalType validation tests
1433// ============================================================================
1434
1435#[test]
1436fn test_decimal_invalid_precision() {
1437    // DecimalType::with_nullable should return an error for invalid precision
1438    let result = DecimalType::with_nullable(true, 50, 2);
1439    assert!(result.is_err());
1440    assert!(
1441        result
1442            .unwrap_err()
1443            .to_string()
1444            .contains("Decimal precision must be between 1 and 38")
1445    );
1446}
1447
1448#[test]
1449fn test_decimal_invalid_scale() {
1450    // DecimalType::with_nullable should return an error when scale > precision
1451    let result = DecimalType::with_nullable(true, 10, 15);
1452    assert!(result.is_err());
1453    assert!(
1454        result
1455            .unwrap_err()
1456            .to_string()
1457            .contains("Decimal scale must be between 0 and the precision 10")
1458    );
1459}
1460
1461// ============================================================================
1462// DecimalType validation tests - edge cases
1463// ============================================================================
1464
1465#[test]
1466fn test_decimal_valid_precision_and_scale() {
1467    // Valid: precision=10, scale=2
1468    let result = DecimalType::with_nullable(true, 10, 2);
1469    assert!(result.is_ok());
1470    let decimal = result.unwrap();
1471    assert_eq!(decimal.precision(), 10);
1472    assert_eq!(decimal.scale(), 2);
1473    // Nullable: should NOT contain "NOT NULL"
1474    assert!(!decimal.to_string().contains("NOT NULL"));
1475
1476    // Valid: precision=38, scale=0
1477    let result = DecimalType::with_nullable(true, 38, 0);
1478    assert!(result.is_ok());
1479    let decimal = result.unwrap();
1480    assert_eq!(decimal.precision(), 38);
1481    assert_eq!(decimal.scale(), 0);
1482
1483    // Valid: precision=1, scale=0
1484    let result = DecimalType::with_nullable(false, 1, 0);
1485    assert!(result.is_ok());
1486    let decimal = result.unwrap();
1487    assert_eq!(decimal.precision(), 1);
1488    assert_eq!(decimal.scale(), 0);
1489    // Non-nullable: should contain "NOT NULL"
1490    assert!(decimal.to_string().contains("NOT NULL"));
1491}
1492
1493#[test]
1494fn test_decimal_invalid_precision_zero() {
1495    // Invalid: precision=0 (edge case not covered by existing tests)
1496    let result = DecimalType::with_nullable(true, 0, 0);
1497    assert!(result.is_err());
1498    assert!(
1499        result
1500            .unwrap_err()
1501            .to_string()
1502            .contains("Decimal precision must be between 1 and 38")
1503    );
1504}
1505
1506#[test]
1507fn test_decimal_scale_equals_precision_boundary() {
1508    // Boundary: precision=10, scale=10 (scale == precision is valid)
1509    let result = DecimalType::with_nullable(true, 10, 10);
1510    assert!(result.is_ok());
1511    let decimal = result.unwrap();
1512    assert_eq!(decimal.precision(), 10);
1513    assert_eq!(decimal.scale(), 10);
1514}
1515
1516// ============================================================================
1517// TimeType validation tests
1518// ============================================================================
1519
1520#[test]
1521fn test_time_valid_precision() {
1522    // Test all valid precision values 0 through 9
1523    for precision in 0..=9 {
1524        let result = TimeType::with_nullable(true, precision);
1525        assert!(result.is_ok(), "precision {precision} should be valid");
1526        let time = result.unwrap();
1527        assert_eq!(time.precision(), precision);
1528    }
1529}
1530
1531#[test]
1532fn test_time_invalid_precision() {
1533    // TimeType::with_nullable should return an error for invalid precision
1534    let result = TimeType::with_nullable(true, 10);
1535    assert!(result.is_err());
1536    assert!(
1537        result
1538            .unwrap_err()
1539            .to_string()
1540            .contains("Time precision must be between 0 and 9")
1541    );
1542}
1543
1544// ============================================================================
1545// TimestampType validation tests
1546// ============================================================================
1547
1548#[test]
1549fn test_timestamp_valid_precision() {
1550    // Test all valid precision values 0 through 9
1551    for precision in 0..=9 {
1552        let result = TimestampType::with_nullable(true, precision);
1553        assert!(result.is_ok(), "precision {precision} should be valid");
1554        let timestamp_type = result.unwrap();
1555        assert_eq!(timestamp_type.precision(), precision);
1556    }
1557}
1558
1559#[test]
1560fn test_timestamp_invalid_precision() {
1561    // TimestampType::with_nullable should return an error for invalid precision
1562    let result = TimestampType::with_nullable(true, 10);
1563    assert!(result.is_err());
1564    assert!(
1565        result
1566            .unwrap_err()
1567            .to_string()
1568            .contains("Timestamp precision must be between 0 and 9")
1569    );
1570}
1571
1572#[test]
1573fn test_timestamp_ltz_invalid_precision() {
1574    // TimestampLTzType::with_nullable should return an error for invalid precision
1575    let result = TimestampLTzType::with_nullable(true, 10);
1576    assert!(result.is_err());
1577    assert!(
1578        result
1579            .unwrap_err()
1580            .to_string()
1581            .contains("Timestamp with local time zone precision must be between 0 and 9")
1582    );
1583}
1584
1585// ============================================================================
1586// RowType projection tests
1587// ============================================================================
1588
1589#[test]
1590fn test_row_type_project_valid_indices() {
1591    // Create a 3-column row type
1592    let row_type = RowType::with_data_types_and_field_names(
1593        vec![DataTypes::int(), DataTypes::string(), DataTypes::bigint()],
1594        vec!["id", "name", "age"],
1595    );
1596
1597    // Valid projection by indices: [0, 2]
1598    let projected = row_type.project(&[0, 2]).unwrap();
1599    assert_eq!(projected.fields().len(), 2);
1600    assert_eq!(projected.fields()[0].name, "id");
1601    assert_eq!(projected.fields()[1].name, "age");
1602}
1603
1604#[test]
1605fn test_row_type_project_empty_indices() {
1606    // Create a 3-column row type
1607    let row_type = RowType::with_data_types_and_field_names(
1608        vec![DataTypes::int(), DataTypes::string(), DataTypes::bigint()],
1609        vec!["id", "name", "age"],
1610    );
1611
1612    // Projection with an empty indices array should yield an empty RowType
1613    let projected = row_type.project(&[]).unwrap();
1614    assert_eq!(projected.fields().len(), 0);
1615}
1616
1617#[test]
1618fn test_row_type_project_with_field_names_valid() {
1619    // Create a 3-column row type
1620    let row_type = RowType::with_data_types_and_field_names(
1621        vec![DataTypes::int(), DataTypes::string(), DataTypes::bigint()],
1622        vec!["id", "name", "age"],
1623    );
1624
1625    // Valid projection by names: ["id", "name"]
1626    let projected = row_type
1627        .project_with_field_names(&["id".to_string(), "name".to_string()])
1628        .unwrap();
1629    assert_eq!(projected.fields().len(), 2);
1630    assert_eq!(projected.fields()[0].name, "id");
1631    assert_eq!(projected.fields()[1].name, "name");
1632}
1633
1634#[test]
1635fn test_row_type_project_index_out_of_bounds() {
1636    // Create a 3-column row type
1637    let row_type = RowType::with_data_types_and_field_names(
1638        vec![DataTypes::int(), DataTypes::string(), DataTypes::bigint()],
1639        vec!["id", "name", "age"],
1640    );
1641
1642    // Error: index out of bounds
1643    let result = row_type.project(&[0, 5]);
1644    assert!(result.is_err());
1645    assert!(
1646        result
1647            .unwrap_err()
1648            .to_string()
1649            .contains("invalid field position: 5")
1650    );
1651}
1652
1653#[test]
1654fn test_row_type_project_with_field_names_nonexistent() {
1655    // Create a 3-column row type
1656    let row_type = RowType::with_data_types_and_field_names(
1657        vec![DataTypes::int(), DataTypes::string(), DataTypes::bigint()],
1658        vec!["id", "name", "age"],
1659    );
1660
1661    // Error: non-existent field name should throw exception
1662    let result = row_type.project_with_field_names(&["nonexistent".to_string()]);
1663    assert!(result.is_err());
1664    assert!(
1665        result
1666            .unwrap_err()
1667            .to_string()
1668            .contains("Field 'nonexistent' does not exist in the row type")
1669    );
1670
1671    // Mixed existing and non-existing: should also error on the first non-existent field
1672    let result = row_type.project_with_field_names(&["id".to_string(), "nonexistent".to_string()]);
1673    assert!(result.is_err());
1674    assert!(
1675        result
1676            .unwrap_err()
1677            .to_string()
1678            .contains("Field 'nonexistent' does not exist in the row type")
1679    );
1680}
1681
1682#[test]
1683fn test_row_type_project_duplicate_indices() {
1684    // Create a 3-column row type
1685    let row_type = RowType::with_data_types_and_field_names(
1686        vec![DataTypes::int(), DataTypes::string(), DataTypes::bigint()],
1687        vec!["id", "name", "age"],
1688    );
1689
1690    // Projection with duplicate indices: [0, 0, 1]
1691    // This documents the expected behavior - duplicates are allowed
1692    let projected = row_type.project(&[0, 0, 1]).unwrap();
1693    assert_eq!(projected.fields().len(), 3);
1694    assert_eq!(projected.fields()[0].name, "id");
1695    assert_eq!(projected.fields()[1].name, "id");
1696    assert_eq!(projected.fields()[2].name, "name");
1697}