diff --git a/crates/paimon/Cargo.toml b/crates/paimon/Cargo.toml index 6d057d5..1e33ce0 100644 --- a/crates/paimon/Cargo.toml +++ b/crates/paimon/Cargo.toml @@ -27,6 +27,7 @@ license.workspace = true version.workspace = true [dependencies] +bitflags = "2.6.0" chrono = {version = "0.4.38", features = ["serde"]} serde = { version = "1", features = ["derive"] } serde_with = "3.8.3" diff --git a/crates/paimon/src/spec/mod.rs b/crates/paimon/src/spec/mod.rs index eb25755..fc09dcd 100644 --- a/crates/paimon/src/spec/mod.rs +++ b/crates/paimon/src/spec/mod.rs @@ -27,3 +27,6 @@ pub use schema::*; mod snapshot; pub use snapshot::*; + +mod types; +pub use types::*; diff --git a/crates/paimon/src/spec/schema.rs b/crates/paimon/src/spec/schema.rs index b6a5498..7a9b0d0 100644 --- a/crates/paimon/src/spec/schema.rs +++ b/crates/paimon/src/spec/schema.rs @@ -15,12 +15,10 @@ // specific language governing permissions and limitations // under the License. -use crate::error::Error; +use crate::spec::types::DataType; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; use std::collections::HashMap; -use std::fmt::{Display, Formatter}; -use std::str::FromStr; /// The table schema for paimon table. /// @@ -53,32 +51,3 @@ pub struct DataField { typ: DataType, description: Option, } - -/// Data type for paimon table. -/// -/// Impl Reference: -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct DataType { - is_nullable: bool, - type_root: DataTypeRoot, -} - -impl Display for DataType { - fn fmt(&self, _: &mut Formatter<'_>) -> std::fmt::Result { - todo!() - } -} - -impl FromStr for DataType { - type Err = Error; - - fn from_str(_: &str) -> Result { - todo!() - } -} - -/// The root of data type. -/// -/// Impl Reference: -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum DataTypeRoot {} diff --git a/crates/paimon/src/spec/types.rs b/crates/paimon/src/spec/types.rs new file mode 100644 index 0000000..b44dc2d --- /dev/null +++ b/crates/paimon/src/spec/types.rs @@ -0,0 +1,1033 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use crate::error::Error; +use bitflags::bitflags; +use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter}; +use std::str::FromStr; + +bitflags! { +/// An enumeration of Data type families for clustering {@link DataTypeRoot}s into categories. +/// +/// Impl Reference: +#[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct DataTypeFamily: u32 { + const PREDEFINED = 1 << 0; + const CONSTRUCTED = 1 << 1; + const CHARACTER_STRING = 1 << 2; + const BINARY_STRING = 1 << 3; + const NUMERIC = 1 << 4; + const INTEGER_NUMERIC = 1 << 5; + const EXACT_NUMERIC = 1 << 6; + const APPROXIMATE_NUMERIC = 1 << 7; + const DATETIME = 1 << 8; + const TIME = 1 << 9; + const TIMESTAMP = 1 << 10; + const COLLECTION = 1 << 11; + const EXTENSION = 1 << 12; + } +} + +/// The root of data type. +/// +/// Impl Reference: +#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub enum DataTypeRoot { + Char, + Varchar, + Boolean, + Binary, + Varbinary, + Decimal, + Tinyint, + Smallint, + Integer, + Bigint, + Float, + Double, + Date, + TimeWithoutTimeZone, + TimestampWithoutTimeZone, + TimestampWithLocalTimeZone, + Array, + Multiset, + Map, + Row, +} + +impl DataTypeRoot { + pub fn families(&self) -> DataTypeFamily { + match self { + Self::Char => DataTypeFamily::PREDEFINED | DataTypeFamily::CHARACTER_STRING, + Self::Varchar => DataTypeFamily::PREDEFINED | DataTypeFamily::CHARACTER_STRING, + Self::Boolean => DataTypeFamily::PREDEFINED, + Self::Binary => DataTypeFamily::PREDEFINED | DataTypeFamily::BINARY_STRING, + Self::Varbinary => DataTypeFamily::PREDEFINED | DataTypeFamily::BINARY_STRING, + Self::Decimal => { + DataTypeFamily::PREDEFINED | DataTypeFamily::NUMERIC | DataTypeFamily::EXACT_NUMERIC + } + Self::Tinyint => { + DataTypeFamily::PREDEFINED + | DataTypeFamily::NUMERIC + | DataTypeFamily::INTEGER_NUMERIC + | DataTypeFamily::EXACT_NUMERIC + } + Self::Smallint => { + DataTypeFamily::PREDEFINED + | DataTypeFamily::NUMERIC + | DataTypeFamily::INTEGER_NUMERIC + | DataTypeFamily::EXACT_NUMERIC + } + Self::Integer => { + DataTypeFamily::PREDEFINED + | DataTypeFamily::NUMERIC + | DataTypeFamily::INTEGER_NUMERIC + | DataTypeFamily::EXACT_NUMERIC + } + Self::Bigint => { + DataTypeFamily::PREDEFINED + | DataTypeFamily::NUMERIC + | DataTypeFamily::INTEGER_NUMERIC + | DataTypeFamily::EXACT_NUMERIC + } + Self::Float => { + DataTypeFamily::PREDEFINED + | DataTypeFamily::NUMERIC + | DataTypeFamily::APPROXIMATE_NUMERIC + } + Self::Double => { + DataTypeFamily::PREDEFINED + | DataTypeFamily::NUMERIC + | DataTypeFamily::APPROXIMATE_NUMERIC + } + Self::Date => DataTypeFamily::PREDEFINED | DataTypeFamily::DATETIME, + Self::TimeWithoutTimeZone => { + DataTypeFamily::PREDEFINED | DataTypeFamily::DATETIME | DataTypeFamily::TIME + } + Self::TimestampWithoutTimeZone => { + DataTypeFamily::PREDEFINED | DataTypeFamily::DATETIME | DataTypeFamily::TIMESTAMP + } + Self::TimestampWithLocalTimeZone => { + DataTypeFamily::PREDEFINED + | DataTypeFamily::DATETIME + | DataTypeFamily::TIMESTAMP + | DataTypeFamily::EXTENSION + } + Self::Array => DataTypeFamily::CONSTRUCTED | DataTypeFamily::COLLECTION, + Self::Multiset => DataTypeFamily::CONSTRUCTED | DataTypeFamily::COLLECTION, + Self::Map => DataTypeFamily::CONSTRUCTED | DataTypeFamily::EXTENSION, + Self::Row => DataTypeFamily::CONSTRUCTED, + } + } +} + +/// A visitor that can visit different data types. +pub trait DataTypeVisitor { + fn visit(&mut self, data_type: &DataType) -> R; +} + +/// Data type for paimon table. +/// +/// Impl Reference: +#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct DataType { + is_nullable: bool, + type_root: DataTypeRoot, +} + +impl Display for DataType { + fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +impl FromStr for DataType { + type Err = Error; + + fn from_str(_: &str) -> Result { + todo!() + } +} + +#[allow(dead_code)] +impl DataType { + fn new(is_nullable: bool, type_root: DataTypeRoot) -> Self { + Self { + is_nullable, + type_root, + } + } + + /// Returns a deep copy of this type with possibly different nullability. + /// Impl Reference: + fn with_nullable(&self, is_nullable: bool) -> Self { + Self { + is_nullable, + type_root: self.type_root, + } + } + + /// Returns true if the data type is nullable. + /// + /// Impl Reference: + fn is_nullable(&self) -> bool { + self.is_nullable + } + + /// Returns the root of the data type. + /// + /// Impl Reference: + fn type_root(&self) -> &DataTypeRoot { + &self.type_root + } + + /// Returns whether the root of the type equals to the type_root or not. + /// + /// Impl Reference: + fn is(&self, type_root: &DataTypeRoot) -> bool { + &self.type_root == type_root + } + + /// Returns whether the family type of the type equals to the family or not. + /// + /// Impl Reference: + fn is_family(&self, family: DataTypeFamily) -> bool { + self.type_root.families().contains(family) + } + + /// Returns whether the root of the type equals to at least on of the type_roots or not. + /// + /// Impl Reference: + fn is_any_of(&self, type_roots: &[DataTypeRoot]) -> bool { + type_roots.iter().any(|tr: &DataTypeRoot| self.is(tr)) + } + + /// Returns whether the root of the type is part of at least one family of the families or not. + /// Impl Reference: + fn is_any_of_family(&self, families: &[DataTypeFamily]) -> bool { + families.iter().any(|f: &DataTypeFamily| self.is_family(*f)) + } + + fn accept(&self, visitor: &mut T) + where + T: DataTypeVisitor, + { + visitor.visit(self); + } +} + +/// ArrayType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +#[serde(rename_all = "camelCase")] +pub struct ArrayType { + element_type: DataType, +} + +impl Display for ArrayType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if self.element_type.is_nullable() { + write!(f, "ARRAY") + } else { + write!(f, "ARRAY NOT NULL") + } + } +} + +impl Default for ArrayType { + fn default() -> Self { + Self::new(true) + } +} + +impl ArrayType { + pub fn new(is_nullable: bool) -> Self { + Self { + element_type: DataType::new(is_nullable, DataTypeRoot::Array), + } + } +} + +/// BigIntType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct BigIntType { + element_type: DataType, +} + +impl Display for BigIntType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "BIGINT")?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for BigIntType { + fn default() -> Self { + Self::new(true) + } +} + +impl BigIntType { + pub fn new(is_nullable: bool) -> Self { + Self { + element_type: DataType::new(is_nullable, DataTypeRoot::Bigint), + } + } +} + +/// BinaryType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +#[serde(rename_all = "camelCase")] +pub struct BinaryType { + element_type: DataType, + length: usize, +} + +impl Display for BinaryType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "BINARY({})", self.length)?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for BinaryType { + fn default() -> Self { + Self::new(true, Self::DEFAULT_LENGTH) + } +} + +impl BinaryType { + pub const MIN_LENGTH: usize = 1; + + pub const MAX_LENGTH: usize = usize::MAX; + + pub const DEFAULT_LENGTH: usize = 1; + + pub fn new(is_nullable: bool, length: usize) -> Self { + Self::try_new(is_nullable, length).unwrap() + } + + pub fn try_new(is_nullable: bool, length: usize) -> Result { + if length < Self::MIN_LENGTH { + return Err("Binary string length must be at least 1."); + } + Ok(Self { + element_type: DataType { + is_nullable, + type_root: DataTypeRoot::Binary, + }, + length, + }) + } + + pub fn with_length(length: usize) -> Self { + Self::new(true, length) + } + + pub fn length(&self) -> usize { + self.length + } +} + +/// BooleanType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct BooleanType { + pub element_type: DataType, +} + +impl Display for BooleanType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "BOOLEAN")?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for BooleanType { + fn default() -> Self { + Self::new(true) + } +} + +impl BooleanType { + pub fn new(is_nullable: bool) -> Self { + Self { + element_type: DataType::new(is_nullable, DataTypeRoot::Boolean), + } + } +} + +/// CharType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CharType { + element_type: DataType, + length: usize, +} + +impl Display for CharType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "CHAR({})", self.length)?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for CharType { + fn default() -> Self { + Self::with_length(Self::DEFAULT_LENGTH) + } +} + +impl CharType { + pub const DEFAULT_LENGTH: usize = 1; + + pub const MIN_LENGTH: usize = 1; + + pub const MAX_LENGTH: usize = 255; + + pub fn new(is_nullable: bool, length: usize) -> Self { + Self::try_new(is_nullable, length).unwrap() + } + + pub fn try_new(is_nullable: bool, length: usize) -> Result { + if !(Self::MIN_LENGTH..=Self::MAX_LENGTH).contains(&length) { + return Err("Character string length must be between 1 and 255 (both inclusive)."); + } + Ok(CharType { + element_type: DataType { + is_nullable, + type_root: DataTypeRoot::Char, + }, + length, + }) + } + + pub fn with_length(length: usize) -> Self { + Self::new(true, length) + } + + pub fn length(&self) -> usize { + self.length + } +} + +/// DateType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +pub struct DateType { + element_type: DataType, +} + +impl Display for DateType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "DATE")?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for DateType { + fn default() -> Self { + Self::new(true) + } +} + +impl DateType { + pub fn new(is_nullable: bool) -> Self { + Self { + element_type: DataType::new(is_nullable, DataTypeRoot::Date), + } + } +} + +/// DecimalType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct DecimalType { + element_type: DataType, + precision: u32, + scale: u32, +} + +impl Display for DecimalType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "DECIMAL({}, {})", self.precision, self.scale)?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for DecimalType { + fn default() -> Self { + Self::with_precision_and_scale(Self::DEFAULT_PRECISION, Self::DEFAULT_SCALE) + } +} + +impl DecimalType { + pub const MIN_PRECISION: u32 = 1; + + pub const MAX_PRECISION: u32 = 38; + + pub const DEFAULT_PRECISION: u32 = 10; + + pub const MIN_SCALE: u32 = 0; + + pub const DEFAULT_SCALE: u32 = 0; + + pub fn new(is_nullable: bool, precision: u32, scale: u32) -> Self { + Self::try_new(is_nullable, precision, scale).unwrap() + } + + pub fn try_new(is_nullable: bool, precision: u32, scale: u32) -> Result { + if !(Self::MIN_PRECISION..=Self::MAX_PRECISION).contains(&precision) { + return Err(format!( + "Decimal precision must be between {} and {} (both inclusive).", + Self::MIN_PRECISION, + Self::MAX_PRECISION + )); + } + + if !(Self::MIN_SCALE..=precision).contains(&scale) { + return Err(format!( + "Decimal scale must be between {} and the precision {} (both inclusive).", + Self::MIN_SCALE, + precision + )); + } + + Ok(DecimalType { + element_type: DataType { + is_nullable, + type_root: DataTypeRoot::Decimal, + }, + precision, + scale, + }) + } + + pub fn with_precision_and_scale(precision: u32, scale: u32) -> Self { + Self::new(true, precision, scale) + } + + pub fn precision(&self) -> u32 { + self.precision + } + + pub fn scale(&self) -> u32 { + self.scale + } +} + +/// DoubleType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct DoubleType { + element_type: DataType, +} + +impl Display for DoubleType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "DOUBLE")?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for DoubleType { + fn default() -> Self { + Self::new(true) + } +} + +impl DoubleType { + pub fn new(is_nullable: bool) -> Self { + Self { + element_type: DataType::new(is_nullable, DataTypeRoot::Double), + } + } +} + +/// FloatType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct FloatType { + element_type: DataType, +} + +impl Display for FloatType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "FLOAT")?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for FloatType { + fn default() -> Self { + Self::new(true) + } +} + +impl FloatType { + pub fn new(is_nullable: bool) -> Self { + Self { + element_type: DataType::new(is_nullable, DataTypeRoot::Float), + } + } +} + +/// IntType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct IntType { + element_type: DataType, +} + +impl Display for IntType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "INTEGER")?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for IntType { + fn default() -> Self { + Self::new(true) + } +} + +impl IntType { + pub fn new(is_nullable: bool) -> Self { + Self { + element_type: DataType::new(is_nullable, DataTypeRoot::Integer), + } + } +} + +/// LocalZonedTimestampType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct LocalZonedTimestampType { + element_type: DataType, + precision: u32, +} + +impl Display for LocalZonedTimestampType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "TIMESTAMP WITH LOCAL TIME ZONE({})", self.precision)?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for LocalZonedTimestampType { + fn default() -> Self { + Self::with_precision(Self::DEFAULT_PRECISION) + } +} + +impl LocalZonedTimestampType { + pub const MIN_PRECISION: u32 = TimestampType::MIN_PRECISION; + + pub const MAX_PRECISION: u32 = TimestampType::MAX_PRECISION; + + pub const DEFAULT_PRECISION: u32 = TimestampType::DEFAULT_PRECISION; + + pub fn new(is_nullable: bool, precision: u32) -> Self { + LocalZonedTimestampType::try_new(is_nullable, precision).unwrap() + } + + pub fn try_new(is_nullable: bool, precision: u32) -> Result { + if !(Self::MIN_PRECISION..=Self::MAX_PRECISION).contains(&precision) { + return Err(format!( + "Timestamp precision must be between {} and {} (both inclusive).", + Self::MIN_PRECISION, + Self::MAX_PRECISION + )); + } + + Ok(LocalZonedTimestampType { + element_type: DataType { + is_nullable, + type_root: DataTypeRoot::TimestampWithLocalTimeZone, + }, + precision, + }) + } + + pub fn with_precision(precision: u32) -> Self { + Self::new(true, precision) + } + + pub fn precision(&self) -> u32 { + self.precision + } +} + +/// Next TODO: MapType、MultisetType、RowType + +/// SmallIntType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct SmallIntType { + element_type: DataType, +} + +impl Display for SmallIntType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "SMALLINT")?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for SmallIntType { + fn default() -> Self { + Self::new(true) + } +} + +impl SmallIntType { + pub fn new(is_nullable: bool) -> Self { + Self { + element_type: DataType::new(is_nullable, DataTypeRoot::Smallint), + } + } +} + +/// TimeType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct TimeType { + element_type: DataType, + precision: u32, +} + +impl Display for TimeType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "TIME({})", self.precision)?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for TimeType { + fn default() -> Self { + Self::with_precision(TimeType::DEFAULT_PRECISION) + } +} + +impl TimeType { + pub const MIN_PRECISION: u32 = 0; + + pub const MAX_PRECISION: u32 = 9; + + pub const DEFAULT_PRECISION: u32 = 0; + + pub fn new(is_nullable: bool, precision: u32) -> Self { + Self::try_new(is_nullable, precision).unwrap() + } + + pub fn try_new(is_nullable: bool, precision: u32) -> Result { + if !(Self::MIN_PRECISION..=Self::MAX_PRECISION).contains(&precision) { + return Err(format!( + "Time precision must be between {} and {} (both inclusive).", + Self::MIN_PRECISION, + Self::MAX_PRECISION + )); + } + + Ok(TimeType { + element_type: DataType { + is_nullable, + type_root: DataTypeRoot::TimeWithoutTimeZone, + }, + precision, + }) + } + + pub fn with_precision(precision: u32) -> Self { + Self::new(true, precision) + } + + pub fn precision(&self) -> u32 { + self.precision + } +} + +/// TimestampType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct TimestampType { + element_type: DataType, + precision: u32, +} + +impl Display for TimestampType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "TIMESTAMP({})", self.precision)?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for TimestampType { + fn default() -> Self { + Self::with_precision(Self::DEFAULT_PRECISION) + } +} + +impl TimestampType { + pub const MIN_PRECISION: u32 = 0; + + pub const MAX_PRECISION: u32 = 9; + + pub const DEFAULT_PRECISION: u32 = 6; + + pub fn new(is_nullable: bool, precision: u32) -> Self { + Self::try_new(is_nullable, precision).unwrap() + } + + pub fn try_new(is_nullable: bool, precision: u32) -> Result { + if !(Self::MIN_PRECISION..=Self::MAX_PRECISION).contains(&precision) { + return Err(format!( + "Timestamp precision must be between {} and {} (both inclusive).", + Self::MIN_PRECISION, + Self::MAX_PRECISION + )); + } + + Ok(TimestampType { + element_type: DataType { + is_nullable, + type_root: DataTypeRoot::TimestampWithoutTimeZone, + }, + precision, + }) + } + + pub fn with_precision(precision: u32) -> Self { + Self::new(true, precision) + } + + pub fn precision(&self) -> u32 { + self.precision + } +} + +/// TinyIntType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct TinyIntType { + element_type: DataType, +} + +impl Display for TinyIntType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "TINYINT")?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for TinyIntType { + fn default() -> Self { + Self::new(true) + } +} + +impl TinyIntType { + pub fn new(is_nullable: bool) -> Self { + Self { + element_type: DataType::new(is_nullable, DataTypeRoot::Tinyint), + } + } +} + +/// VarBinaryType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct VarBinaryType { + element_type: DataType, + length: u32, +} + +impl Display for VarBinaryType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "VARBINARY({})", self.length)?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for VarBinaryType { + fn default() -> Self { + Self::with_length(Self::DEFAULT_LENGTH) + } +} + +impl VarBinaryType { + pub const MIN_LENGTH: u32 = 1; + + pub const MAX_LENGTH: u32 = isize::MAX as u32; + + pub const DEFAULT_LENGTH: u32 = 1; + + pub fn new(is_nullable: bool, length: u32) -> Self { + Self::try_new(is_nullable, length).unwrap() + } + + pub fn try_new(is_nullable: bool, length: u32) -> Result { + if length < Self::MIN_LENGTH { + return Err("Binary string length must be at least 1.".to_string()); + } + + Ok(VarBinaryType { + element_type: DataType { + is_nullable, + type_root: DataTypeRoot::Varbinary, + }, + length, + }) + } + + pub fn with_length(length: u32) -> Self { + Self::new(true, length) + } + + pub fn length(&self) -> u32 { + self.length + } +} + +/// VarCharType for paimon. +/// +/// Impl Reference: . +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct VarCharType { + element_type: DataType, + length: u32, +} + +impl Display for VarCharType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "VARCHAR({})", self.length)?; + if !self.element_type.is_nullable() { + write!(f, " NOT NULL")?; + } + Ok(()) + } +} + +impl Default for VarCharType { + fn default() -> Self { + Self::with_length(Self::DEFAULT_LENGTH) + } +} + +impl VarCharType { + pub const MIN_LENGTH: u32 = 1; + + pub const MAX_LENGTH: u32 = isize::MAX as u32; + + pub const DEFAULT_LENGTH: u32 = 1; + + pub fn new(is_nullable: bool, length: u32) -> Self { + Self::try_new(is_nullable, length).unwrap() + } + + pub fn try_new(is_nullable: bool, length: u32) -> Result { + if !(Self::MIN_LENGTH..=Self::MAX_LENGTH).contains(&length) { + return Err(format!( + "Character string length must be between {} and {} (both inclusive).", + Self::MIN_LENGTH, + Self::MAX_LENGTH + )); + } + + Ok(VarCharType { + element_type: DataType { + is_nullable, + type_root: DataTypeRoot::Varchar, + }, + length, + }) + } + + pub fn with_length(length: u32) -> Self { + Self::new(true, length) + } + + pub fn length(&self) -> u32 { + self.length + } +}