1use crate::{Result, TableError};
2use serde::{Deserialize, Serialize};
3use serde_json::Value as JsonValue;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
7#[serde(transparent)]
8pub struct FieldId(pub u32);
9
10impl From<u32> for FieldId {
11 fn from(value: u32) -> Self {
12 Self(value)
13 }
14}
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum TimestampKind {
20 WithoutTimeZone,
21 WithLocalTimeZone,
22}
23
24#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
26pub struct ExtensionType {
27 pub type_id: String,
28 pub parameters: JsonValue,
29 pub physical_type: Box<LogicalType>,
30}
31
32impl ExtensionType {
33 pub fn new(
34 type_id: impl Into<String>,
35 parameters: JsonValue,
36 physical_type: LogicalType,
37 ) -> Result<Self> {
38 let extension = Self {
39 type_id: type_id.into(),
40 parameters,
41 physical_type: Box::new(physical_type),
42 };
43 extension.validate()?;
44 Ok(extension)
45 }
46
47 pub(crate) fn validate(&self) -> Result<()> {
48 if self.type_id.trim().is_empty() {
49 return Err(TableError::InvalidSchema(
50 "extension type id must not be empty".to_string(),
51 ));
52 }
53 self.physical_type.validate()
54 }
55}
56
57#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
59pub struct LogicalType {
60 pub nullable: bool,
61 #[serde(flatten)]
62 pub kind: LogicalTypeKind,
63}
64
65#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
67#[serde(tag = "kind", rename_all = "snake_case")]
68pub enum LogicalTypeKind {
69 Boolean,
70 Int8,
71 Int16,
72 Int32,
73 Int64,
74 Float32,
75 Float64,
76 Decimal {
77 precision: u8,
78 scale: u8,
79 },
80 Date,
81 Time {
82 precision: u8,
83 },
84 Timestamp {
85 precision: u8,
86 timestamp_kind: TimestampKind,
87 },
88 String,
89 Binary,
90 List {
91 element_type: Box<LogicalType>,
92 },
93 Map {
94 key_type: Box<LogicalType>,
95 value_type: Box<LogicalType>,
96 },
97 Struct {
98 fields: Vec<DataField>,
99 },
100 Extension {
101 extension: ExtensionType,
102 },
103}
104
105impl LogicalType {
106 fn new(kind: LogicalTypeKind) -> Self {
107 Self {
108 nullable: false,
109 kind,
110 }
111 }
112
113 pub fn boolean() -> Self {
114 Self::new(LogicalTypeKind::Boolean)
115 }
116
117 pub fn int8() -> Self {
118 Self::new(LogicalTypeKind::Int8)
119 }
120
121 pub fn int16() -> Self {
122 Self::new(LogicalTypeKind::Int16)
123 }
124
125 pub fn int32() -> Self {
126 Self::new(LogicalTypeKind::Int32)
127 }
128
129 pub fn int64() -> Self {
130 Self::new(LogicalTypeKind::Int64)
131 }
132
133 pub fn float32() -> Self {
134 Self::new(LogicalTypeKind::Float32)
135 }
136
137 pub fn float64() -> Self {
138 Self::new(LogicalTypeKind::Float64)
139 }
140
141 pub fn decimal(precision: u8, scale: u8) -> Self {
142 Self::new(LogicalTypeKind::Decimal { precision, scale })
143 }
144
145 pub fn date() -> Self {
146 Self::new(LogicalTypeKind::Date)
147 }
148
149 pub fn time(precision: u8) -> Self {
150 Self::new(LogicalTypeKind::Time { precision })
151 }
152
153 pub fn timestamp(precision: u8, timestamp_kind: TimestampKind) -> Self {
154 Self::new(LogicalTypeKind::Timestamp {
155 precision,
156 timestamp_kind,
157 })
158 }
159
160 pub fn string() -> Self {
161 Self::new(LogicalTypeKind::String)
162 }
163
164 pub fn binary() -> Self {
165 Self::new(LogicalTypeKind::Binary)
166 }
167
168 pub fn list(element_type: LogicalType) -> Self {
169 Self::new(LogicalTypeKind::List {
170 element_type: Box::new(element_type),
171 })
172 }
173
174 pub fn map(key_type: LogicalType, value_type: LogicalType) -> Self {
175 Self::new(LogicalTypeKind::Map {
176 key_type: Box::new(key_type),
177 value_type: Box::new(value_type),
178 })
179 }
180
181 pub fn struct_type(fields: Vec<DataField>) -> Self {
182 Self::new(LogicalTypeKind::Struct { fields })
183 }
184
185 pub fn struct_from_fields<I, N>(fields: I) -> Result<Self>
192 where
193 I: IntoIterator<Item = (N, LogicalType)>,
194 N: Into<String>,
195 {
196 let mut fields = fields
197 .into_iter()
198 .map(|(name, logical_type)| DataField {
199 id: FieldId(0),
200 name: name.into(),
201 logical_type,
202 })
203 .collect::<Vec<_>>();
204 let mut next_id = 0;
205 assign_fresh_field_ids(&mut fields, &mut next_id)?;
206 let logical_type = Self::struct_type(fields);
207 logical_type.validate()?;
208 Ok(logical_type)
209 }
210
211 pub fn extension(extension: ExtensionType) -> Self {
212 Self::new(LogicalTypeKind::Extension { extension })
213 }
214
215 #[must_use]
216 pub fn nullable(mut self) -> Self {
217 self.nullable = true;
218 self
219 }
220
221 #[must_use]
222 pub fn not_null(mut self) -> Self {
223 self.nullable = false;
224 self
225 }
226
227 pub(crate) fn validate(&self) -> Result<()> {
228 match &self.kind {
229 LogicalTypeKind::Decimal { precision, scale }
230 if *precision == 0 || *precision > 38 || scale > precision =>
231 {
232 return Err(TableError::InvalidSchema(format!(
233 "invalid decimal precision/scale: {precision}/{scale}"
234 )));
235 }
236 LogicalTypeKind::Time { precision } | LogicalTypeKind::Timestamp { precision, .. }
237 if *precision > 9 =>
238 {
239 return Err(TableError::InvalidSchema(format!(
240 "time precision must be in [0, 9], got {precision}"
241 )));
242 }
243 LogicalTypeKind::List { element_type } => element_type.validate()?,
244 LogicalTypeKind::Map {
245 key_type,
246 value_type,
247 } => {
248 if key_type.nullable {
249 return Err(TableError::InvalidSchema(
250 "map key type must not be nullable".to_string(),
251 ));
252 }
253 key_type.validate()?;
254 value_type.validate()?;
255 }
256 LogicalTypeKind::Struct { fields } => {
257 for field in fields {
258 field.validate()?;
259 }
260 }
261 LogicalTypeKind::Extension { extension } => extension.validate()?,
262 _ => {}
263 }
264 Ok(())
265 }
266
267 pub(crate) fn is_key_compatible(&self) -> bool {
268 !self.nullable
269 && matches!(
270 self.kind,
271 LogicalTypeKind::Boolean
272 | LogicalTypeKind::Int8
273 | LogicalTypeKind::Int16
274 | LogicalTypeKind::Int32
275 | LogicalTypeKind::Int64
276 | LogicalTypeKind::Decimal { .. }
277 | LogicalTypeKind::Date
278 | LogicalTypeKind::Time { .. }
279 | LogicalTypeKind::Timestamp { .. }
280 | LogicalTypeKind::String
281 | LogicalTypeKind::Binary
282 )
283 }
284}
285
286#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
288pub struct DataField {
289 pub id: FieldId,
290 pub name: String,
291 pub logical_type: LogicalType,
292}
293
294impl DataField {
295 pub fn new(
301 id: impl Into<FieldId>,
302 name: impl Into<String>,
303 logical_type: LogicalType,
304 ) -> Result<Self> {
305 let field = Self {
306 id: id.into(),
307 name: name.into(),
308 logical_type,
309 };
310 field.validate()?;
311 Ok(field)
312 }
313
314 pub(crate) fn validate(&self) -> Result<()> {
315 if self.name.trim().is_empty() {
316 return Err(TableError::InvalidSchema(format!(
317 "field {} name must not be empty",
318 self.id.0
319 )));
320 }
321 self.logical_type.validate()
322 }
323}
324
325pub(crate) fn assign_fresh_field_ids(fields: &mut [DataField], next_id: &mut u32) -> Result<()> {
327 for field in fields {
328 field.id = FieldId(*next_id);
329 *next_id = next_id.checked_add(1).ok_or_else(|| {
330 TableError::InvalidSchema("fresh schema exceeds available field ids".to_string())
331 })?;
332 assign_fresh_type_ids(&mut field.logical_type, next_id)?;
333 }
334 Ok(())
335}
336
337pub(crate) fn assign_fresh_type_ids(
338 logical_type: &mut LogicalType,
339 next_id: &mut u32,
340) -> Result<()> {
341 match &mut logical_type.kind {
342 LogicalTypeKind::List { element_type } => assign_fresh_type_ids(element_type, next_id),
343 LogicalTypeKind::Map {
344 key_type,
345 value_type,
346 } => {
347 assign_fresh_type_ids(key_type, next_id)?;
348 assign_fresh_type_ids(value_type, next_id)
349 }
350 LogicalTypeKind::Struct { fields } => assign_fresh_field_ids(fields, next_id),
351 LogicalTypeKind::Extension { extension } => {
352 assign_fresh_type_ids(&mut extension.physical_type, next_id)
353 }
354 _ => Ok(()),
355 }
356}