Skip to main content

rudb_common/
types.rs

1//! The type system, per `spec/10-sql-and-types.md` section 10.1.
2//!
3//! A logical type is what SQL talks about. A physical type is how it is laid out. Keeping the two
4//! apart is what lets `DECIMAL(9, 2)` be stored in an `i32` without the planner having to know,
5//! and it is the same separation that later lets a `VARCHAR` column be handed to an operator as
6//! dictionary codes.
7//!
8//! Type names are spelled the way DuckDB spells them, including the aliases, because
9//! `spec/12-duckdb-compat.md` makes the dialect a compatibility surface and `CREATE TABLE t (a
10//! INT4)` is a thing people write.
11
12use std::fmt;
13
14use crate::error::{Error, Result};
15
16/// A named field of a `STRUCT` or a `UNION`.
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub struct Field {
19    /// The field name, unquoted and case sensitive as stored.
20    pub name: String,
21    /// The field type.
22    pub ty: LogicalType,
23}
24
25impl Field {
26    /// A field with a name and a type.
27    pub fn new(name: impl Into<String>, ty: LogicalType) -> Self {
28        Self { name: name.into(), ty }
29    }
30}
31
32/// What SQL thinks a value is.
33///
34/// Nulls are not in here. `spec/10-sql-and-types.md` says null is a per-value property at every
35/// nesting level, which makes it a property of a vector's validity mask rather than of a type.
36/// The one exception is [`LogicalType::Null`], which is the type of a literal `NULL` before
37/// anything has told it what it is, and which every other type absorbs during resolution.
38#[derive(Debug, Clone, PartialEq, Eq, Hash)]
39#[non_exhaustive]
40pub enum LogicalType {
41    /// The type of an untyped `NULL` literal.
42    Null,
43    /// `BOOLEAN`.
44    Boolean,
45    /// `TINYINT`, 8 bits signed.
46    TinyInt,
47    /// `SMALLINT`, 16 bits signed.
48    SmallInt,
49    /// `INTEGER`, 32 bits signed.
50    Integer,
51    /// `BIGINT`, 64 bits signed.
52    BigInt,
53    /// `HUGEINT`, 128 bits signed.
54    HugeInt,
55    /// `UTINYINT`, 8 bits unsigned.
56    UTinyInt,
57    /// `USMALLINT`, 16 bits unsigned.
58    USmallInt,
59    /// `UINTEGER`, 32 bits unsigned.
60    UInteger,
61    /// `UBIGINT`, 64 bits unsigned.
62    UBigInt,
63    /// `UHUGEINT`, 128 bits unsigned.
64    UHugeInt,
65    /// `FLOAT`, IEEE 754 binary32.
66    Float,
67    /// `DOUBLE`, IEEE 754 binary64.
68    Double,
69    /// `DECIMAL(width, scale)`, stored in the narrowest integer that holds `width` digits.
70    Decimal {
71        /// Total number of decimal digits, 1 through 38.
72        width: u8,
73        /// Digits to the right of the point, no greater than `width`.
74        scale: u8,
75    },
76    /// `VARCHAR`. Length modifiers parse and are then ignored, as they are in DuckDB.
77    Varchar,
78    /// `BLOB`.
79    Blob,
80    /// `BIT`, a bit string.
81    Bit,
82    /// `UUID`.
83    Uuid,
84    /// `DATE`, days since 1970-01-01.
85    Date,
86    /// `TIME`, microseconds since midnight.
87    Time,
88    /// `TIME WITH TIME ZONE`.
89    TimeTz,
90    /// `TIMESTAMP`, microseconds since the epoch.
91    Timestamp,
92    /// `TIMESTAMP_S`, seconds since the epoch.
93    TimestampS,
94    /// `TIMESTAMP_MS`, milliseconds since the epoch.
95    TimestampMs,
96    /// `TIMESTAMP_NS`, nanoseconds since the epoch.
97    TimestampNs,
98    /// `TIMESTAMP WITH TIME ZONE`.
99    TimestampTz,
100    /// `INTERVAL`, the months, days and microseconds triple.
101    Interval,
102    /// `T[]`, a variable length list.
103    List(Box<LogicalType>),
104    /// `T[n]`, a fixed length array.
105    Array(Box<LogicalType>, u32),
106    /// `STRUCT(name type, ...)`.
107    Struct(Vec<Field>),
108    /// `MAP(key, value)`.
109    Map(Box<LogicalType>, Box<LogicalType>),
110    /// `UNION(tag type, ...)`.
111    Union(Vec<Field>),
112}
113
114/// How a value is actually laid out in a vector.
115///
116/// The planner picks operators off this rather than off the logical type, which is why `DATE` and
117/// `INTEGER` share an implementation of everything that does not care what the number means.
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
119#[non_exhaustive]
120pub enum PhysicalType {
121    /// One byte per value, zero or one.
122    Bool,
123    /// 8 bit signed.
124    Int8,
125    /// 16 bit signed.
126    Int16,
127    /// 32 bit signed.
128    Int32,
129    /// 64 bit signed.
130    Int64,
131    /// 128 bit signed.
132    Int128,
133    /// 8 bit unsigned.
134    UInt8,
135    /// 16 bit unsigned.
136    UInt16,
137    /// 32 bit unsigned.
138    UInt32,
139    /// 64 bit unsigned.
140    UInt64,
141    /// 128 bit unsigned.
142    UInt128,
143    /// IEEE 754 binary32.
144    Float32,
145    /// IEEE 754 binary64.
146    Float64,
147    /// The months, days and microseconds triple.
148    Interval,
149    /// The 16 byte string representation from `spec/07-execution.md` section 7.1.
150    Varlen,
151    /// Offsets plus one child column.
152    List,
153    /// A fixed stride into one child column.
154    Array,
155    /// Named child columns, one per field.
156    Struct,
157    /// No storage. Only the validity mask says anything.
158    Empty,
159}
160
161impl LogicalType {
162    /// A `DECIMAL(width, scale)`, checked.
163    ///
164    /// # Errors
165    ///
166    /// If the width is zero or above 38, or the scale is greater than the width. Those are the
167    /// same bounds DuckDB enforces and the message is the same message.
168    pub fn decimal(width: u8, scale: u8) -> Result<Self> {
169        if width == 0 || width > MAX_DECIMAL_WIDTH {
170            return Err(Error::binder(format!("Width must be between 1 and {MAX_DECIMAL_WIDTH}!")));
171        }
172        if scale > width {
173            return Err(Error::binder(format!(
174                "Scale cannot be bigger than width, {scale} is bigger than {width}"
175            )));
176        }
177        Ok(Self::Decimal { width, scale })
178    }
179
180    /// A list of `element`.
181    #[must_use]
182    pub fn list(element: Self) -> Self {
183        Self::List(Box::new(element))
184    }
185
186    /// A fixed length array of `element`.
187    #[must_use]
188    pub fn array(element: Self, length: u32) -> Self {
189        Self::Array(Box::new(element), length)
190    }
191
192    /// A map from `key` to `value`.
193    #[must_use]
194    pub fn map(key: Self, value: Self) -> Self {
195        Self::Map(Box::new(key), Box::new(value))
196    }
197
198    /// How this type is laid out.
199    #[must_use]
200    pub fn physical(&self) -> PhysicalType {
201        match self {
202            Self::Null => PhysicalType::Empty,
203            Self::Boolean => PhysicalType::Bool,
204            Self::TinyInt => PhysicalType::Int8,
205            Self::SmallInt => PhysicalType::Int16,
206            Self::Integer | Self::Date => PhysicalType::Int32,
207            Self::BigInt
208            | Self::Time
209            | Self::TimeTz
210            | Self::Timestamp
211            | Self::TimestampS
212            | Self::TimestampMs
213            | Self::TimestampNs
214            | Self::TimestampTz => PhysicalType::Int64,
215            Self::HugeInt | Self::Uuid => PhysicalType::Int128,
216            Self::UTinyInt => PhysicalType::UInt8,
217            Self::USmallInt => PhysicalType::UInt16,
218            Self::UInteger => PhysicalType::UInt32,
219            Self::UBigInt => PhysicalType::UInt64,
220            Self::UHugeInt => PhysicalType::UInt128,
221            Self::Float => PhysicalType::Float32,
222            Self::Double => PhysicalType::Float64,
223            // The narrowest integer that holds the requested number of digits, which is the
224            // standard representation and the one DuckDB uses. A DECIMAL(9, 2) column costs four
225            // bytes a value and not sixteen.
226            Self::Decimal { width, .. } => match width {
227                0..=4 => PhysicalType::Int16,
228                5..=9 => PhysicalType::Int32,
229                10..=18 => PhysicalType::Int64,
230                _ => PhysicalType::Int128,
231            },
232            Self::Varchar | Self::Blob | Self::Bit => PhysicalType::Varlen,
233            Self::Interval => PhysicalType::Interval,
234            // A map is a list of two-field structs, which is how Arrow does it and how every
235            // engine that has to interoperate with Arrow ends up doing it.
236            Self::List(_) | Self::Map(_, _) => PhysicalType::List,
237            Self::Array(_, _) => PhysicalType::Array,
238            Self::Struct(_) | Self::Union(_) => PhysicalType::Struct,
239        }
240    }
241
242    /// Whether arithmetic applies.
243    #[must_use]
244    pub fn is_numeric(&self) -> bool {
245        self.is_integer() || matches!(self, Self::Float | Self::Double | Self::Decimal { .. })
246    }
247
248    /// Whether this is one of the integer types, signed or unsigned.
249    #[must_use]
250    pub fn is_integer(&self) -> bool {
251        matches!(
252            self,
253            Self::TinyInt
254                | Self::SmallInt
255                | Self::Integer
256                | Self::BigInt
257                | Self::HugeInt
258                | Self::UTinyInt
259                | Self::USmallInt
260                | Self::UInteger
261                | Self::UBigInt
262                | Self::UHugeInt
263        )
264    }
265
266    /// Whether this is a date, a time, a timestamp or an interval.
267    #[must_use]
268    pub fn is_temporal(&self) -> bool {
269        matches!(
270            self,
271            Self::Date
272                | Self::Time
273                | Self::TimeTz
274                | Self::Timestamp
275                | Self::TimestampS
276                | Self::TimestampMs
277                | Self::TimestampNs
278                | Self::TimestampTz
279                | Self::Interval
280        )
281    }
282
283    /// Whether this type contains other types.
284    ///
285    /// Nested types are stored columnar all the way down, so this is the question of whether a
286    /// column of this type is one column chunk or several.
287    #[must_use]
288    pub fn is_nested(&self) -> bool {
289        matches!(
290            self,
291            Self::List(_) | Self::Array(_, _) | Self::Struct(_) | Self::Map(_, _) | Self::Union(_)
292        )
293    }
294
295    /// The type both of these can be cast to without losing a value, if there is one.
296    ///
297    /// This is DuckDB's `MaxLogicalType` and it is what decides the type of `a + b`, of the arms of
298    /// a `CASE`, and of the columns of a `UNION`. The rule is a total order over the numeric types
299    /// with everything else absorbing into `VARCHAR` only when it is asked to, and `NULL` absorbing
300    /// into anything, which is what makes `CASE WHEN c THEN NULL ELSE 1 END` an integer.
301    ///
302    /// Mixing signed and unsigned widens rather than reinterprets, so `INTEGER` and `UINTEGER`
303    /// promote to `BIGINT` and not to either of themselves. That costs a byte per value on a case
304    /// that is rare and it is the only version that never silently changes a number, which matters
305    /// more here than the byte does: a wrong answer that is off by 4,294,967,296 is the worst kind
306    /// of bug this engine can have.
307    ///
308    /// Returns `None` when there is no such type, which is the binder's cue to raise rather than to
309    /// guess. Two different structs are `None` and not a struct of promoted fields, because field
310    /// order and field names would have to match and a rule that sometimes works is worse here than
311    /// one that never does.
312    #[must_use]
313    pub fn promote(&self, other: &Self) -> Option<Self> {
314        if self == other {
315            return Some(self.clone());
316        }
317        match (self, other) {
318            (Self::Null, ty) | (ty, Self::Null) => Some(ty.clone()),
319            (Self::List(left), Self::List(right)) => Some(Self::list(left.promote(right)?)),
320            _ if self.is_numeric() && other.is_numeric() => {
321                Some(promote_numeric(self.clone(), other.clone()))
322            }
323            // A date and a timestamp meet at the wider one, which is the timestamp, and the same
324            // holds for the timestamp units. `rank_temporal` is what says which is wider.
325            _ if self.is_temporal() && other.is_temporal() => {
326                match (rank_temporal(self), rank_temporal(other)) {
327                    (Some(left), Some(right)) => {
328                        Some(if left >= right { self.clone() } else { other.clone() })
329                    }
330                    _ => None,
331                }
332            }
333            _ => None,
334        }
335    }
336
337    /// The types this one contains, in child column order, or empty for a scalar type.
338    #[must_use]
339    pub fn children(&self) -> Vec<Self> {
340        match self {
341            Self::List(inner) | Self::Array(inner, _) => vec![inner.as_ref().clone()],
342            Self::Map(key, value) => vec![key.as_ref().clone(), value.as_ref().clone()],
343            Self::Struct(fields) | Self::Union(fields) => {
344                fields.iter().map(|f| f.ty.clone()).collect()
345            }
346            _ => Vec::new(),
347        }
348    }
349
350    /// Parses a SQL type name, aliases included.
351    ///
352    /// # Errors
353    ///
354    /// If the text is not a type name this understands. The message names the offending word,
355    /// because a type parse failure two levels inside a `STRUCT` is otherwise unreadable.
356    pub fn parse(text: &str) -> Result<Self> {
357        let tokens = lex(text)?;
358        let mut parser = TypeParser { tokens: &tokens, position: 0 };
359        let ty = parser.parse_type()?;
360        if parser.position != parser.tokens.len() {
361            return Err(Error::parser(format!("Type \"{text}\" has trailing text")));
362        }
363        Ok(ty)
364    }
365}
366
367/// The largest number of decimal digits a `DECIMAL` can carry, because 38 digits is what fits in
368/// 128 bits and there is no wider physical type.
369pub const MAX_DECIMAL_WIDTH: u8 = 38;
370
371impl fmt::Display for LogicalType {
372    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373        match self {
374            Self::Null => f.write_str("\"NULL\""),
375            Self::Boolean => f.write_str("BOOLEAN"),
376            Self::TinyInt => f.write_str("TINYINT"),
377            Self::SmallInt => f.write_str("SMALLINT"),
378            Self::Integer => f.write_str("INTEGER"),
379            Self::BigInt => f.write_str("BIGINT"),
380            Self::HugeInt => f.write_str("HUGEINT"),
381            Self::UTinyInt => f.write_str("UTINYINT"),
382            Self::USmallInt => f.write_str("USMALLINT"),
383            Self::UInteger => f.write_str("UINTEGER"),
384            Self::UBigInt => f.write_str("UBIGINT"),
385            Self::UHugeInt => f.write_str("UHUGEINT"),
386            Self::Float => f.write_str("FLOAT"),
387            Self::Double => f.write_str("DOUBLE"),
388            Self::Decimal { width, scale } => write!(f, "DECIMAL({width},{scale})"),
389            Self::Varchar => f.write_str("VARCHAR"),
390            Self::Blob => f.write_str("BLOB"),
391            Self::Bit => f.write_str("BIT"),
392            Self::Uuid => f.write_str("UUID"),
393            Self::Date => f.write_str("DATE"),
394            Self::Time => f.write_str("TIME"),
395            Self::TimeTz => f.write_str("TIME WITH TIME ZONE"),
396            Self::Timestamp => f.write_str("TIMESTAMP"),
397            Self::TimestampS => f.write_str("TIMESTAMP_S"),
398            Self::TimestampMs => f.write_str("TIMESTAMP_MS"),
399            Self::TimestampNs => f.write_str("TIMESTAMP_NS"),
400            Self::TimestampTz => f.write_str("TIMESTAMP WITH TIME ZONE"),
401            Self::Interval => f.write_str("INTERVAL"),
402            Self::List(inner) => write!(f, "{inner}[]"),
403            Self::Array(inner, length) => write!(f, "{inner}[{length}]"),
404            Self::Map(key, value) => write!(f, "MAP({key}, {value})"),
405            Self::Struct(fields) => write_fields(f, "STRUCT", fields),
406            Self::Union(fields) => write_fields(f, "UNION", fields),
407        }
408    }
409}
410
411/// Where a numeric type sits in the widening order.
412///
413/// The integers are ordered by how many values they hold, which is why an unsigned type ranks
414/// above the signed type of the same width. That order alone is not enough to promote a signed
415/// type with an unsigned one, since neither holds the other, and [`promote_integers`] is what
416/// handles that case before this function is reached.
417fn rank_numeric(ty: &LogicalType) -> u8 {
418    match ty {
419        LogicalType::TinyInt => 1,
420        LogicalType::UTinyInt => 2,
421        LogicalType::SmallInt => 3,
422        LogicalType::USmallInt => 4,
423        LogicalType::Integer => 5,
424        LogicalType::UInteger => 6,
425        LogicalType::BigInt => 7,
426        LogicalType::UBigInt => 8,
427        LogicalType::HugeInt => 9,
428        LogicalType::UHugeInt => 10,
429        LogicalType::Decimal { .. } => 11,
430        LogicalType::Float => 12,
431        LogicalType::Double => 13,
432        _ => 0,
433    }
434}
435
436/// Whether an integer type is signed, and how many bits it is.
437fn integer_shape(ty: &LogicalType) -> (bool, u8) {
438    match ty {
439        LogicalType::TinyInt => (true, 8),
440        LogicalType::SmallInt => (true, 16),
441        LogicalType::Integer => (true, 32),
442        LogicalType::BigInt => (true, 64),
443        LogicalType::HugeInt => (true, 128),
444        LogicalType::UTinyInt => (false, 8),
445        LogicalType::USmallInt => (false, 16),
446        LogicalType::UInteger => (false, 32),
447        LogicalType::UBigInt => (false, 64),
448        _ => (false, 128),
449    }
450}
451
452/// The integer type of that many bits and that signedness.
453fn integer_of(signed: bool, bits: u8) -> Option<LogicalType> {
454    Some(match (signed, bits) {
455        (true, 8) => LogicalType::TinyInt,
456        (true, 16) => LogicalType::SmallInt,
457        (true, 32) => LogicalType::Integer,
458        (true, 64) => LogicalType::BigInt,
459        (true, 128) => LogicalType::HugeInt,
460        (false, 8) => LogicalType::UTinyInt,
461        (false, 16) => LogicalType::USmallInt,
462        (false, 32) => LogicalType::UInteger,
463        (false, 64) => LogicalType::UBigInt,
464        (false, 128) => LogicalType::UHugeInt,
465        _ => return None,
466    })
467}
468
469/// The narrowest integer type that holds every value of both.
470///
471/// Two of the same signedness are just the wider one. A signed and an unsigned need a signed type
472/// strictly wider than the unsigned one, because a `UBIGINT` of 2^63 does not fit in a `BIGINT`
473/// and a `BIGINT` of -1 does not fit in a `UBIGINT`. When that runs off the end of the integer
474/// types, which is only `UHUGEINT` against a signed type, the answer is `DOUBLE`: it loses
475/// precision past 2^53 and it is the only thing left, and DuckDB does the same.
476fn promote_integers(left: &LogicalType, right: &LogicalType) -> LogicalType {
477    let (left_signed, left_bits) = integer_shape(left);
478    let (right_signed, right_bits) = integer_shape(right);
479    if left_signed == right_signed {
480        return if left_bits >= right_bits { left.clone() } else { right.clone() };
481    }
482    let (signed_bits, unsigned_bits) =
483        if left_signed { (left_bits, right_bits) } else { (right_bits, left_bits) };
484    let wanted = signed_bits.max(unsigned_bits.saturating_mul(2));
485    integer_of(true, wanted).unwrap_or(LogicalType::Double)
486}
487
488/// The narrowest numeric type that holds every value of both.
489fn promote_numeric(left: LogicalType, right: LogicalType) -> LogicalType {
490    // A decimal and a float meet at the float, since the ranks already say so, but two decimals
491    // meet at one wide enough for both the integer part and the fraction of each, which the ranks
492    // cannot express.
493    if let (
494        LogicalType::Decimal { width: left_width, scale: left_scale },
495        LogicalType::Decimal { width: right_width, scale: right_scale },
496    ) = (&left, &right)
497    {
498        let scale = (*left_scale).max(*right_scale);
499        let integral =
500            left_width.saturating_sub(*left_scale).max(right_width.saturating_sub(*right_scale));
501        let width = integral.saturating_add(scale).min(MAX_DECIMAL_WIDTH);
502        return LogicalType::Decimal { width, scale: scale.min(width) };
503    }
504    // An integer and a decimal have to leave room for the integer's digits to the left of the
505    // point, so the decimal widens rather than the integer simply casting into it.
506    let widened = match (&left, &right) {
507        (LogicalType::Decimal { width, scale }, other)
508        | (other, LogicalType::Decimal { width, scale })
509            if other.is_integer() =>
510        {
511            let needed = decimal_digits(other).saturating_add(*scale).min(MAX_DECIMAL_WIDTH);
512            Some(LogicalType::Decimal { width: (*width).max(needed), scale: *scale })
513        }
514        _ => None,
515    };
516    if let Some(ty) = widened {
517        return ty;
518    }
519    if left.is_integer() && right.is_integer() {
520        return promote_integers(&left, &right);
521    }
522    if rank_numeric(&left) >= rank_numeric(&right) { left } else { right }
523}
524
525/// How many decimal digits an integer type needs, which is what a decimal has to leave room for.
526fn decimal_digits(ty: &LogicalType) -> u8 {
527    match ty {
528        LogicalType::TinyInt | LogicalType::UTinyInt => 3,
529        LogicalType::SmallInt | LogicalType::USmallInt => 5,
530        LogicalType::Integer | LogicalType::UInteger => 10,
531        LogicalType::BigInt | LogicalType::UBigInt => 20,
532        _ => MAX_DECIMAL_WIDTH,
533    }
534}
535
536/// Where a temporal type sits in the widening order, or `None` if it does not widen into another.
537///
538/// An interval is a duration and not a point in time, so it has no rank and never promotes with a
539/// timestamp. That is the difference between `t + INTERVAL 1 DAY`, which is a function call the
540/// binder resolves, and `CASE WHEN c THEN t ELSE INTERVAL 1 DAY END`, which has no type.
541fn rank_temporal(ty: &LogicalType) -> Option<u8> {
542    match ty {
543        LogicalType::Date => Some(1),
544        LogicalType::TimestampS => Some(2),
545        LogicalType::TimestampMs => Some(3),
546        LogicalType::Timestamp => Some(4),
547        LogicalType::TimestampNs => Some(5),
548        LogicalType::TimestampTz => Some(6),
549        _ => None,
550    }
551}
552
553fn write_fields(f: &mut fmt::Formatter<'_>, keyword: &str, fields: &[Field]) -> fmt::Result {
554    f.write_str(keyword)?;
555    f.write_str("(")?;
556    for (index, field) in fields.iter().enumerate() {
557        if index > 0 {
558            f.write_str(", ")?;
559        }
560        write_identifier(f, &field.name)?;
561        write!(f, " {}", field.ty)?;
562    }
563    f.write_str(")")
564}
565
566/// Writes a field name, quoting it if it would not survive being read back unquoted.
567fn write_identifier(f: &mut fmt::Formatter<'_>, name: &str) -> fmt::Result {
568    let plain = !name.is_empty()
569        && name.chars().next().is_some_and(|c| c.is_ascii_alphabetic() || c == '_')
570        && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_');
571    if plain {
572        f.write_str(name)
573    } else {
574        f.write_str("\"")?;
575        for c in name.chars() {
576            if c == '"' {
577                f.write_str("\"\"")?;
578            } else {
579                write!(f, "{c}")?;
580            }
581        }
582        f.write_str("\"")
583    }
584}
585
586#[derive(Debug, Clone, PartialEq, Eq)]
587enum Token {
588    Word(String),
589    Quoted(String),
590    Number(u32),
591    LeftParen,
592    RightParen,
593    LeftBracket,
594    RightBracket,
595    Comma,
596}
597
598fn lex(text: &str) -> Result<Vec<Token>> {
599    let mut tokens = Vec::new();
600    let chars: Vec<char> = text.chars().collect();
601    let mut i = 0;
602    while i < chars.len() {
603        let c = chars[i];
604        match c {
605            c if c.is_whitespace() => i += 1,
606            '(' => {
607                tokens.push(Token::LeftParen);
608                i += 1;
609            }
610            ')' => {
611                tokens.push(Token::RightParen);
612                i += 1;
613            }
614            '[' => {
615                tokens.push(Token::LeftBracket);
616                i += 1;
617            }
618            ']' => {
619                tokens.push(Token::RightBracket);
620                i += 1;
621            }
622            ',' => {
623                tokens.push(Token::Comma);
624                i += 1;
625            }
626            '"' => {
627                let mut name = String::new();
628                i += 1;
629                loop {
630                    let Some(&c) = chars.get(i) else {
631                        return Err(Error::parser(format!(
632                            "Type \"{text}\" has an unterminated quoted name"
633                        )));
634                    };
635                    i += 1;
636                    if c == '"' {
637                        if chars.get(i) == Some(&'"') {
638                            name.push('"');
639                            i += 1;
640                            continue;
641                        }
642                        break;
643                    }
644                    name.push(c);
645                }
646                tokens.push(Token::Quoted(name));
647            }
648            c if c.is_ascii_digit() => {
649                let start = i;
650                while chars.get(i).is_some_and(char::is_ascii_digit) {
651                    i += 1;
652                }
653                let digits: String = chars[start..i].iter().collect();
654                let number = digits.parse::<u32>().map_err(|_| {
655                    Error::parser(format!("Type \"{text}\" has a number that is too large"))
656                })?;
657                tokens.push(Token::Number(number));
658            }
659            c if c.is_alphabetic() || c == '_' => {
660                let start = i;
661                while chars.get(i).is_some_and(|&c| c.is_alphanumeric() || c == '_') {
662                    i += 1;
663                }
664                tokens.push(Token::Word(chars[start..i].iter().collect()));
665            }
666            other => {
667                return Err(Error::parser(format!(
668                    "Type \"{text}\" has an unexpected character {other:?}"
669                )));
670            }
671        }
672    }
673    Ok(tokens)
674}
675
676struct TypeParser<'a> {
677    tokens: &'a [Token],
678    position: usize,
679}
680
681impl TypeParser<'_> {
682    fn peek(&self) -> Option<&Token> {
683        self.tokens.get(self.position)
684    }
685
686    fn eat(&mut self, token: &Token) -> bool {
687        if self.peek() == Some(token) {
688            self.position += 1;
689            true
690        } else {
691            false
692        }
693    }
694
695    /// Consumes `word` if it is next, case insensitively.
696    fn eat_word(&mut self, word: &str) -> bool {
697        match self.peek() {
698            Some(Token::Word(found)) if found.eq_ignore_ascii_case(word) => {
699                self.position += 1;
700                true
701            }
702            _ => false,
703        }
704    }
705
706    fn parse_type(&mut self) -> Result<LogicalType> {
707        let mut ty = self.parse_base()?;
708        // Suffixes bind left to right, so INTEGER[][3] is an array of three lists.
709        loop {
710            if !self.eat(&Token::LeftBracket) {
711                break;
712            }
713            if let Some(&Token::Number(length)) = self.peek() {
714                self.position += 1;
715                expect(self.eat(&Token::RightBracket), "]")?;
716                ty = LogicalType::array(ty, length);
717            } else {
718                expect(self.eat(&Token::RightBracket), "]")?;
719                ty = LogicalType::list(ty);
720            }
721        }
722        Ok(ty)
723    }
724
725    fn parse_base(&mut self) -> Result<LogicalType> {
726        // A quoted name is accepted here because the null type prints as "NULL" with the quotes,
727        // which is DuckDB's spelling and which has to read back.
728        let word = match self.peek().cloned() {
729            Some(Token::Word(word) | Token::Quoted(word)) => {
730                self.position += 1;
731                word
732            }
733            _ => return Err(Error::parser("Expected a type name".to_string())),
734        };
735        let upper = word.to_ascii_uppercase();
736
737        match upper.as_str() {
738            "STRUCT" | "ROW" => return self.parse_fields().map(LogicalType::Struct),
739            "UNION" => return self.parse_fields().map(LogicalType::Union),
740            "MAP" => {
741                expect(self.eat(&Token::LeftParen), "(")?;
742                let key = self.parse_type()?;
743                expect(self.eat(&Token::Comma), ",")?;
744                let value = self.parse_type()?;
745                expect(self.eat(&Token::RightParen), ")")?;
746                return Ok(LogicalType::map(key, value));
747            }
748            "DECIMAL" | "NUMERIC" | "DEC" => {
749                if !self.eat(&Token::LeftParen) {
750                    // Bare DECIMAL is DECIMAL(18, 3) in DuckDB, which is a surprising default and
751                    // is nonetheless the one people's queries depend on.
752                    return LogicalType::decimal(18, 3);
753                }
754                let width = self.parse_number()?;
755                let scale = if self.eat(&Token::Comma) { self.parse_number()? } else { 0 };
756                expect(self.eat(&Token::RightParen), ")")?;
757                let narrow = |n: u32| u8::try_from(n).unwrap_or(u8::MAX);
758                return LogicalType::decimal(narrow(width), narrow(scale));
759            }
760            // Multiword names. Each of these is a word that only means something with the words
761            // after it, so the lookahead is checked before the alias table is consulted.
762            "DOUBLE" => {
763                self.eat_word("PRECISION");
764                return Ok(LogicalType::Double);
765            }
766            "CHARACTER" => {
767                self.eat_word("VARYING");
768                self.eat_length_modifier()?;
769                return Ok(LogicalType::Varchar);
770            }
771            "TIME" | "TIMESTAMP" => {
772                let with_zone = self.eat_time_zone_suffix();
773                return Ok(match (upper.as_str(), with_zone) {
774                    ("TIME", false) => LogicalType::Time,
775                    ("TIME", true) => LogicalType::TimeTz,
776                    (_, false) => LogicalType::Timestamp,
777                    (_, true) => LogicalType::TimestampTz,
778                });
779            }
780            _ => {}
781        }
782
783        // A length modifier on a string type parses and is discarded, which is what DuckDB does:
784        // VARCHAR(10) does not truncate and does not reject, it is VARCHAR.
785        self.eat_length_modifier()?;
786        alias(&upper).ok_or_else(|| Error::parser(format!("Unrecognized type name \"{word}\"")))
787    }
788
789    /// `WITH TIME ZONE` or `WITHOUT TIME ZONE`, returning whether the zone is carried.
790    fn eat_time_zone_suffix(&mut self) -> bool {
791        let start = self.position;
792        let with = if self.eat_word("WITH") {
793            true
794        } else if self.eat_word("WITHOUT") {
795            false
796        } else {
797            return false;
798        };
799        if self.eat_word("TIME") && self.eat_word("ZONE") {
800            with
801        } else {
802            self.position = start;
803            false
804        }
805    }
806
807    fn eat_length_modifier(&mut self) -> Result<()> {
808        if self.eat(&Token::LeftParen) {
809            self.parse_number()?;
810            expect(self.eat(&Token::RightParen), ")")?;
811        }
812        Ok(())
813    }
814
815    fn parse_fields(&mut self) -> Result<Vec<Field>> {
816        expect(self.eat(&Token::LeftParen), "(")?;
817        let mut fields = Vec::new();
818        if self.eat(&Token::RightParen) {
819            return Ok(fields);
820        }
821        loop {
822            let name = match self.peek().cloned() {
823                Some(Token::Word(name) | Token::Quoted(name)) => {
824                    self.position += 1;
825                    name
826                }
827                _ => return Err(Error::parser("Expected a field name".to_string())),
828            };
829            let ty = self.parse_type()?;
830            fields.push(Field::new(name, ty));
831            if self.eat(&Token::Comma) {
832                continue;
833            }
834            expect(self.eat(&Token::RightParen), ")")?;
835            return Ok(fields);
836        }
837    }
838
839    fn parse_number(&mut self) -> Result<u32> {
840        match self.peek() {
841            Some(&Token::Number(n)) => {
842                self.position += 1;
843                Ok(n)
844            }
845            _ => Err(Error::parser("Expected a number".to_string())),
846        }
847    }
848}
849
850fn expect(matched: bool, what: &str) -> Result<()> {
851    if matched { Ok(()) } else { Err(Error::parser(format!("Expected \"{what}\""))) }
852}
853
854/// The single word type names, aliases included.
855///
856/// The aliases are DuckDB's, and they are here rather than in the parser because `CREATE TABLE t
857/// (a INT4)` and `CAST(x AS INT4)` have to agree and there is only one table.
858fn alias(upper: &str) -> Option<LogicalType> {
859    Some(match upper {
860        "NULL" => LogicalType::Null,
861        "BOOLEAN" | "BOOL" | "LOGICAL" => LogicalType::Boolean,
862        "TINYINT" | "INT1" => LogicalType::TinyInt,
863        "SMALLINT" | "INT2" | "SHORT" => LogicalType::SmallInt,
864        "INTEGER" | "INT" | "INT4" | "SIGNED" => LogicalType::Integer,
865        "BIGINT" | "INT8" | "LONG" => LogicalType::BigInt,
866        "HUGEINT" | "INT128" => LogicalType::HugeInt,
867        "UTINYINT" | "UINT1" => LogicalType::UTinyInt,
868        "USMALLINT" | "UINT2" => LogicalType::USmallInt,
869        "UINTEGER" | "UINT4" => LogicalType::UInteger,
870        "UBIGINT" | "UINT8" => LogicalType::UBigInt,
871        "UHUGEINT" | "UINT128" => LogicalType::UHugeInt,
872        "FLOAT" | "FLOAT4" | "REAL" => LogicalType::Float,
873        "FLOAT8" => LogicalType::Double,
874        "VARCHAR" | "CHAR" | "BPCHAR" | "TEXT" | "STRING" => LogicalType::Varchar,
875        "BLOB" | "BYTEA" | "BINARY" | "VARBINARY" => LogicalType::Blob,
876        "BIT" | "BITSTRING" => LogicalType::Bit,
877        "UUID" | "GUID" => LogicalType::Uuid,
878        "DATE" => LogicalType::Date,
879        "TIMETZ" => LogicalType::TimeTz,
880        "DATETIME" => LogicalType::Timestamp,
881        "TIMESTAMP_S" | "TIMESTAMP_SEC" | "TIMESTAMP_SECONDS" => LogicalType::TimestampS,
882        "TIMESTAMP_MS" | "TIMESTAMP_MILLISECONDS" => LogicalType::TimestampMs,
883        "TIMESTAMP_NS" | "TIMESTAMP_NANOSECONDS" => LogicalType::TimestampNs,
884        "TIMESTAMPTZ" => LogicalType::TimestampTz,
885        "INTERVAL" => LogicalType::Interval,
886        _ => return None,
887    })
888}
889
890#[cfg(test)]
891mod promotion_tests {
892    use super::LogicalType;
893
894    #[test]
895    fn a_type_promotes_with_itself_to_itself() {
896        for ty in [
897            LogicalType::Integer,
898            LogicalType::Varchar,
899            LogicalType::Boolean,
900            LogicalType::Struct(vec![]),
901        ] {
902            assert_eq!(ty.promote(&ty), Some(ty.clone()), "{ty} does not promote with itself");
903        }
904    }
905
906    #[test]
907    fn null_takes_the_other_type() {
908        assert_eq!(LogicalType::Null.promote(&LogicalType::Varchar), Some(LogicalType::Varchar));
909        assert_eq!(LogicalType::Date.promote(&LogicalType::Null), Some(LogicalType::Date));
910        assert_eq!(LogicalType::Null.promote(&LogicalType::Null), Some(LogicalType::Null));
911    }
912
913    #[test]
914    fn the_wider_number_wins() {
915        assert_eq!(
916            LogicalType::Integer.promote(&LogicalType::SmallInt),
917            Some(LogicalType::Integer)
918        );
919        assert_eq!(LogicalType::Integer.promote(&LogicalType::Double), Some(LogicalType::Double));
920        assert_eq!(LogicalType::Float.promote(&LogicalType::Double), Some(LogicalType::Double));
921    }
922
923    /// The one that is worth a test of its own, because reinterpreting instead of widening here is
924    /// a wrong answer off by four billion rather than a crash.
925    #[test]
926    fn signed_and_unsigned_widen_rather_than_reinterpret() {
927        assert_eq!(LogicalType::Integer.promote(&LogicalType::UInteger), Some(LogicalType::BigInt));
928        assert_eq!(
929            LogicalType::TinyInt.promote(&LogicalType::UTinyInt),
930            Some(LogicalType::SmallInt)
931        );
932        assert_eq!(LogicalType::BigInt.promote(&LogicalType::UBigInt), Some(LogicalType::HugeInt));
933    }
934
935    #[test]
936    fn promotion_does_not_care_which_side_a_type_is_on() {
937        let types = [
938            LogicalType::TinyInt,
939            LogicalType::UInteger,
940            LogicalType::BigInt,
941            LogicalType::Double,
942            LogicalType::Decimal { width: 10, scale: 2 },
943            LogicalType::Null,
944            LogicalType::Varchar,
945            LogicalType::Date,
946            LogicalType::Timestamp,
947        ];
948        for left in &types {
949            for right in &types {
950                assert_eq!(
951                    left.promote(right),
952                    right.promote(left),
953                    "{left} and {right} promote differently depending on the order"
954                );
955            }
956        }
957    }
958
959    #[test]
960    fn a_decimal_keeps_room_for_both_halves() {
961        let left = LogicalType::Decimal { width: 5, scale: 4 };
962        let right = LogicalType::Decimal { width: 5, scale: 1 };
963        assert_eq!(left.promote(&right), Some(LogicalType::Decimal { width: 8, scale: 4 }));
964    }
965
966    #[test]
967    fn an_integer_next_to_a_decimal_widens_the_decimal() {
968        let decimal = LogicalType::Decimal { width: 5, scale: 2 };
969        assert_eq!(
970            decimal.promote(&LogicalType::Integer),
971            Some(LogicalType::Decimal { width: 12, scale: 2 })
972        );
973    }
974
975    #[test]
976    fn a_date_and_a_timestamp_meet_at_the_timestamp() {
977        assert_eq!(
978            LogicalType::Date.promote(&LogicalType::Timestamp),
979            Some(LogicalType::Timestamp)
980        );
981        assert_eq!(
982            LogicalType::TimestampS.promote(&LogicalType::TimestampNs),
983            Some(LogicalType::TimestampNs)
984        );
985    }
986
987    /// An interval is a duration and a timestamp is a point, so there is no type that holds both
988    /// and saying so is the binder's cue to raise instead of guessing.
989    #[test]
990    fn types_that_do_not_meet_say_so() {
991        assert_eq!(LogicalType::Timestamp.promote(&LogicalType::Interval), None);
992        assert_eq!(LogicalType::Integer.promote(&LogicalType::Varchar), None);
993        assert_eq!(LogicalType::Boolean.promote(&LogicalType::Integer), None);
994    }
995
996    #[test]
997    fn a_list_promotes_by_its_element() {
998        let left = LogicalType::list(LogicalType::Integer);
999        let right = LogicalType::list(LogicalType::BigInt);
1000        assert_eq!(left.promote(&right), Some(LogicalType::list(LogicalType::BigInt)));
1001        assert_eq!(left.promote(&LogicalType::list(LogicalType::Varchar)), None);
1002    }
1003}
1004
1005#[cfg(test)]
1006mod tests {
1007    use super::{Field, LogicalType, PhysicalType};
1008
1009    /// Every type this crate knows about, used by the round trip test and by anything else that
1010    /// wants to be exhaustive without listing them again.
1011    fn every_type() -> Vec<LogicalType> {
1012        vec![
1013            LogicalType::Null,
1014            LogicalType::Boolean,
1015            LogicalType::TinyInt,
1016            LogicalType::SmallInt,
1017            LogicalType::Integer,
1018            LogicalType::BigInt,
1019            LogicalType::HugeInt,
1020            LogicalType::UTinyInt,
1021            LogicalType::USmallInt,
1022            LogicalType::UInteger,
1023            LogicalType::UBigInt,
1024            LogicalType::UHugeInt,
1025            LogicalType::Float,
1026            LogicalType::Double,
1027            LogicalType::Decimal { width: 18, scale: 3 },
1028            LogicalType::Decimal { width: 38, scale: 0 },
1029            LogicalType::Varchar,
1030            LogicalType::Blob,
1031            LogicalType::Bit,
1032            LogicalType::Uuid,
1033            LogicalType::Date,
1034            LogicalType::Time,
1035            LogicalType::TimeTz,
1036            LogicalType::Timestamp,
1037            LogicalType::TimestampS,
1038            LogicalType::TimestampMs,
1039            LogicalType::TimestampNs,
1040            LogicalType::TimestampTz,
1041            LogicalType::Interval,
1042            LogicalType::list(LogicalType::Integer),
1043            LogicalType::list(LogicalType::list(LogicalType::Varchar)),
1044            LogicalType::array(LogicalType::Double, 3),
1045            LogicalType::map(LogicalType::Varchar, LogicalType::Integer),
1046            LogicalType::Struct(vec![
1047                Field::new("a", LogicalType::Integer),
1048                Field::new("b", LogicalType::list(LogicalType::Varchar)),
1049            ]),
1050            LogicalType::Union(vec![
1051                Field::new("num", LogicalType::Integer),
1052                Field::new("str", LogicalType::Varchar),
1053            ]),
1054        ]
1055    }
1056
1057    #[test]
1058    fn every_type_survives_being_printed_and_read_back() {
1059        // The textual plan format in spec/04-architecture.md round trips, and a plan carries
1060        // types, so this is the bottom of that guarantee. Failing it means a plan that cannot be
1061        // reparsed, which is the whole reason the format exists.
1062        for ty in every_type() {
1063            let printed = ty.to_string();
1064            let parsed = LogicalType::parse(&printed)
1065                .unwrap_or_else(|e| panic!("{printed} did not parse back: {e}"));
1066            assert_eq!(parsed, ty, "{printed} parsed to something else");
1067        }
1068    }
1069
1070    #[test]
1071    fn a_field_name_that_needs_quoting_gets_quoted() {
1072        let ty = LogicalType::Struct(vec![
1073            Field::new("plain", LogicalType::Integer),
1074            Field::new("has space", LogicalType::Integer),
1075            Field::new("has\"quote", LogicalType::Integer),
1076            Field::new("2leading", LogicalType::Integer),
1077        ]);
1078        assert_eq!(
1079            ty.to_string(),
1080            "STRUCT(plain INTEGER, \"has space\" INTEGER, \"has\"\"quote\" INTEGER, \
1081             \"2leading\" INTEGER)"
1082        );
1083        assert_eq!(LogicalType::parse(&ty.to_string()).unwrap(), ty);
1084    }
1085
1086    #[test]
1087    fn the_duckdb_aliases_resolve() {
1088        let cases = [
1089            ("int4", LogicalType::Integer),
1090            ("INT", LogicalType::Integer),
1091            ("signed", LogicalType::Integer),
1092            ("int8", LogicalType::BigInt),
1093            ("float4", LogicalType::Float),
1094            ("float8", LogicalType::Double),
1095            ("double precision", LogicalType::Double),
1096            ("text", LogicalType::Varchar),
1097            ("varchar(10)", LogicalType::Varchar),
1098            ("character varying(255)", LogicalType::Varchar),
1099            ("bool", LogicalType::Boolean),
1100            ("datetime", LogicalType::Timestamp),
1101            ("numeric(9, 2)", LogicalType::Decimal { width: 9, scale: 2 }),
1102            ("decimal", LogicalType::Decimal { width: 18, scale: 3 }),
1103            ("timestamp without time zone", LogicalType::Timestamp),
1104            ("timestamp with time zone", LogicalType::TimestampTz),
1105            ("time with time zone", LogicalType::TimeTz),
1106        ];
1107        for (text, expected) in cases {
1108            assert_eq!(LogicalType::parse(text).unwrap(), expected, "{text}");
1109        }
1110    }
1111
1112    #[test]
1113    fn list_and_array_suffixes_bind_left_to_right() {
1114        assert_eq!(
1115            LogicalType::parse("INTEGER[][3]").unwrap(),
1116            LogicalType::array(LogicalType::list(LogicalType::Integer), 3)
1117        );
1118        assert_eq!(
1119            LogicalType::parse("STRUCT(a INT)[]").unwrap(),
1120            LogicalType::list(LogicalType::Struct(vec![Field::new("a", LogicalType::Integer)]))
1121        );
1122    }
1123
1124    #[test]
1125    fn a_decimal_is_stored_in_the_narrowest_integer_that_holds_it() {
1126        assert_eq!(LogicalType::decimal(4, 2).unwrap().physical(), PhysicalType::Int16);
1127        assert_eq!(LogicalType::decimal(9, 2).unwrap().physical(), PhysicalType::Int32);
1128        assert_eq!(LogicalType::decimal(18, 2).unwrap().physical(), PhysicalType::Int64);
1129        assert_eq!(LogicalType::decimal(38, 2).unwrap().physical(), PhysicalType::Int128);
1130    }
1131
1132    #[test]
1133    fn a_decimal_outside_the_bounds_is_rejected_rather_than_clamped() {
1134        assert!(LogicalType::decimal(0, 0).is_err());
1135        assert!(LogicalType::decimal(39, 0).is_err());
1136        assert!(LogicalType::decimal(4, 5).is_err());
1137        assert!(LogicalType::parse("DECIMAL(39,0)").is_err());
1138    }
1139
1140    #[test]
1141    fn a_date_and_an_integer_share_a_layout_and_not_a_meaning() {
1142        assert_eq!(LogicalType::Date.physical(), LogicalType::Integer.physical());
1143        assert_ne!(LogicalType::Date, LogicalType::Integer);
1144        assert!(LogicalType::Date.is_temporal());
1145        assert!(!LogicalType::Date.is_numeric());
1146    }
1147
1148    #[test]
1149    fn nesting_reports_its_children_in_child_column_order() {
1150        let ty = LogicalType::map(LogicalType::Varchar, LogicalType::Integer);
1151        assert!(ty.is_nested());
1152        assert_eq!(ty.children(), vec![LogicalType::Varchar, LogicalType::Integer]);
1153        assert_eq!(LogicalType::Integer.children(), Vec::new());
1154    }
1155
1156    #[test]
1157    fn text_that_is_not_a_type_is_rejected_with_the_word_that_broke_it() {
1158        let error = LogicalType::parse("INTEGRE").unwrap_err();
1159        assert!(error.message().contains("INTEGRE"), "{error}");
1160        assert!(LogicalType::parse("INTEGER JUNK").is_err());
1161        assert!(LogicalType::parse("STRUCT(a)").is_err());
1162        assert!(LogicalType::parse("").is_err());
1163    }
1164}