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`, and a named column of a table.
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    /// Whether the column refuses nulls, which is the `NOT NULL` the DDL declared it with.
24    ///
25    /// Always false inside a `STRUCT` or a `UNION`, because a null is a property of a value at
26    /// every nesting level and no type in SQL says a value cannot be one. This is here rather than
27    /// on a separate column type because a table column is already spelled with this struct, and a
28    /// second one that was this one plus a flag would have to be threaded through the binder, the
29    /// scope and every operator schema to carry a bit that only the insert path reads.
30    pub not_null: bool,
31}
32
33impl Field {
34    /// A field with a name and a type, which accepts nulls.
35    pub fn new(name: impl Into<String>, ty: LogicalType) -> Self {
36        Self { name: name.into(), ty, not_null: false }
37    }
38
39    /// A column with a name and a type, which refuses nulls.
40    pub fn required(name: impl Into<String>, ty: LogicalType) -> Self {
41        Self { name: name.into(), ty, not_null: true }
42    }
43}
44
45/// What SQL thinks a value is.
46///
47/// Nulls are not in here. `spec/10-sql-and-types.md` says null is a per-value property at every
48/// nesting level, which makes it a property of a vector's validity mask rather than of a type.
49/// The one exception is [`LogicalType::Null`], which is the type of a literal `NULL` before
50/// anything has told it what it is, and which every other type absorbs during resolution.
51#[derive(Debug, Clone, PartialEq, Eq, Hash)]
52#[non_exhaustive]
53pub enum LogicalType {
54    /// The type of an untyped `NULL` literal.
55    Null,
56    /// `BOOLEAN`.
57    Boolean,
58    /// `TINYINT`, 8 bits signed.
59    TinyInt,
60    /// `SMALLINT`, 16 bits signed.
61    SmallInt,
62    /// `INTEGER`, 32 bits signed.
63    Integer,
64    /// `BIGINT`, 64 bits signed.
65    BigInt,
66    /// `HUGEINT`, 128 bits signed.
67    HugeInt,
68    /// `UTINYINT`, 8 bits unsigned.
69    UTinyInt,
70    /// `USMALLINT`, 16 bits unsigned.
71    USmallInt,
72    /// `UINTEGER`, 32 bits unsigned.
73    UInteger,
74    /// `UBIGINT`, 64 bits unsigned.
75    UBigInt,
76    /// `UHUGEINT`, 128 bits unsigned.
77    UHugeInt,
78    /// `FLOAT`, IEEE 754 binary32.
79    Float,
80    /// `DOUBLE`, IEEE 754 binary64.
81    Double,
82    /// `DECIMAL(width, scale)`, stored in the narrowest integer that holds `width` digits.
83    Decimal {
84        /// Total number of decimal digits, 1 through 38.
85        width: u8,
86        /// Digits to the right of the point, no greater than `width`.
87        scale: u8,
88    },
89    /// `VARCHAR`. Length modifiers parse and are then ignored, as they are in DuckDB.
90    Varchar,
91    /// `BLOB`.
92    Blob,
93    /// `BIT`, a bit string.
94    Bit,
95    /// `UUID`.
96    Uuid,
97    /// `DATE`, days since 1970-01-01.
98    Date,
99    /// `TIME`, microseconds since midnight.
100    Time,
101    /// `TIME WITH TIME ZONE`.
102    TimeTz,
103    /// `TIMESTAMP`, microseconds since the epoch.
104    Timestamp,
105    /// `TIMESTAMP_S`, seconds since the epoch.
106    TimestampS,
107    /// `TIMESTAMP_MS`, milliseconds since the epoch.
108    TimestampMs,
109    /// `TIMESTAMP_NS`, nanoseconds since the epoch.
110    TimestampNs,
111    /// `TIMESTAMP WITH TIME ZONE`.
112    TimestampTz,
113    /// `INTERVAL`, the months, days and microseconds triple.
114    Interval,
115    /// `T[]`, a variable length list.
116    List(Box<LogicalType>),
117    /// `T[n]`, a fixed length array.
118    Array(Box<LogicalType>, u32),
119    /// `STRUCT(name type, ...)`.
120    Struct(Vec<Field>),
121    /// `MAP(key, value)`.
122    Map(Box<LogicalType>, Box<LogicalType>),
123    /// `UNION(tag type, ...)`.
124    Union(Vec<Field>),
125}
126
127/// How a value is actually laid out in a vector.
128///
129/// The planner picks operators off this rather than off the logical type, which is why `DATE` and
130/// `INTEGER` share an implementation of everything that does not care what the number means.
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
132#[non_exhaustive]
133pub enum PhysicalType {
134    /// One byte per value, zero or one.
135    Bool,
136    /// 8 bit signed.
137    Int8,
138    /// 16 bit signed.
139    Int16,
140    /// 32 bit signed.
141    Int32,
142    /// 64 bit signed.
143    Int64,
144    /// 128 bit signed.
145    Int128,
146    /// 8 bit unsigned.
147    UInt8,
148    /// 16 bit unsigned.
149    UInt16,
150    /// 32 bit unsigned.
151    UInt32,
152    /// 64 bit unsigned.
153    UInt64,
154    /// 128 bit unsigned.
155    UInt128,
156    /// IEEE 754 binary32.
157    Float32,
158    /// IEEE 754 binary64.
159    Float64,
160    /// The months, days and microseconds triple.
161    Interval,
162    /// The 16 byte string representation from `spec/07-execution.md` section 7.1.
163    Varlen,
164    /// Offsets plus one child column.
165    List,
166    /// A fixed stride into one child column.
167    Array,
168    /// Named child columns, one per field.
169    Struct,
170    /// No storage. Only the validity mask says anything.
171    Empty,
172}
173
174impl PhysicalType {
175    /// How many bytes one value of this layout takes in a vector.
176    ///
177    /// What the values buffer costs per row, and not what a row costs. A `VARCHAR` is 16 because that
178    /// is the view, and the bytes of a long string are somewhere else. A `LIST` is 8 because that is
179    /// the start and the length, and the elements are in the child. A `STRUCT` is 0 because the parent
180    /// holds nothing of its own beyond the validity mask, and every byte it costs is a child's.
181    ///
182    /// This is the number `duckdb_types().type_size` reports, so it is worth naming the three places
183    /// it disagrees with the pinned binary, all of which are this engine's layout rather than a
184    /// transcription error. A list entry is two `u32` here and two `u64` there, so a `LIST` and a
185    /// `MAP` are 8 against 16. The null type stores nothing here and is given an `INT32` body there,
186    /// so it is 0 against 4.
187    #[must_use]
188    pub const fn size(self) -> usize {
189        match self {
190            Self::Bool | Self::Int8 | Self::UInt8 => 1,
191            Self::Int16 | Self::UInt16 => 2,
192            Self::Int32 | Self::UInt32 | Self::Float32 => 4,
193            Self::Int64 | Self::UInt64 | Self::Float64 | Self::List => 8,
194            Self::Int128 | Self::UInt128 | Self::Interval | Self::Varlen => 16,
195            Self::Array | Self::Struct | Self::Empty => 0,
196        }
197    }
198}
199
200impl LogicalType {
201    /// A `DECIMAL(width, scale)`, checked.
202    ///
203    /// # Errors
204    ///
205    /// If the width is zero or above 38, or the scale is greater than the width. Those are the
206    /// same bounds DuckDB enforces and the message is the same message.
207    pub fn decimal(width: u8, scale: u8) -> Result<Self> {
208        if width == 0 || width > MAX_DECIMAL_WIDTH {
209            return Err(Error::binder(format!("Width must be between 1 and {MAX_DECIMAL_WIDTH}!")));
210        }
211        if scale > width {
212            return Err(Error::binder(format!(
213                "Scale cannot be bigger than width, {scale} is bigger than {width}"
214            )));
215        }
216        Ok(Self::Decimal { width, scale })
217    }
218
219    /// A list of `element`.
220    #[must_use]
221    pub fn list(element: Self) -> Self {
222        Self::List(Box::new(element))
223    }
224
225    /// A fixed length array of `element`.
226    #[must_use]
227    pub fn array(element: Self, length: u32) -> Self {
228        Self::Array(Box::new(element), length)
229    }
230
231    /// A map from `key` to `value`.
232    #[must_use]
233    pub fn map(key: Self, value: Self) -> Self {
234        Self::Map(Box::new(key), Box::new(value))
235    }
236
237    /// How this type is laid out.
238    #[must_use]
239    pub fn physical(&self) -> PhysicalType {
240        match self {
241            Self::Null => PhysicalType::Empty,
242            Self::Boolean => PhysicalType::Bool,
243            Self::TinyInt => PhysicalType::Int8,
244            Self::SmallInt => PhysicalType::Int16,
245            Self::Integer | Self::Date => PhysicalType::Int32,
246            Self::BigInt
247            | Self::Time
248            | Self::TimeTz
249            | Self::Timestamp
250            | Self::TimestampS
251            | Self::TimestampMs
252            | Self::TimestampNs
253            | Self::TimestampTz => PhysicalType::Int64,
254            Self::HugeInt | Self::Uuid => PhysicalType::Int128,
255            Self::UTinyInt => PhysicalType::UInt8,
256            Self::USmallInt => PhysicalType::UInt16,
257            Self::UInteger => PhysicalType::UInt32,
258            Self::UBigInt => PhysicalType::UInt64,
259            Self::UHugeInt => PhysicalType::UInt128,
260            Self::Float => PhysicalType::Float32,
261            Self::Double => PhysicalType::Float64,
262            // The narrowest integer that holds the requested number of digits, which is the
263            // standard representation and the one DuckDB uses. A DECIMAL(9, 2) column costs four
264            // bytes a value and not sixteen.
265            Self::Decimal { width, .. } => match width {
266                0..=4 => PhysicalType::Int16,
267                5..=9 => PhysicalType::Int32,
268                10..=18 => PhysicalType::Int64,
269                _ => PhysicalType::Int128,
270            },
271            Self::Varchar | Self::Blob | Self::Bit => PhysicalType::Varlen,
272            Self::Interval => PhysicalType::Interval,
273            // A map is a list of two-field structs, which is how Arrow does it and how every
274            // engine that has to interoperate with Arrow ends up doing it.
275            Self::List(_) | Self::Map(_, _) => PhysicalType::List,
276            Self::Array(_, _) => PhysicalType::Array,
277            Self::Struct(_) | Self::Union(_) => PhysicalType::Struct,
278        }
279    }
280
281    /// The name DuckDB's messages give this type, which is the integer it is stored in rather than
282    /// the type it is written as.
283    ///
284    /// An overflow says `INT32` and not `INTEGER`, a failed cast says `INT8` and not `TINYINT`, and
285    /// a boolean is `BOOL` in both. A decimal carries the width of the integer behind it rather
286    /// than the width that was declared, so a DECIMAL(18,8) and a DECIMAL(11,0) are both
287    /// `DECIMAL(18)`. Everything that is not a number is written the way it is spelled.
288    #[must_use]
289    pub fn physical_name(&self) -> String {
290        if let Some(width) = self.decimal_storage() {
291            return format!("DECIMAL({width})");
292        }
293        let name = match self {
294            Self::Boolean => "BOOL",
295            Self::TinyInt => "INT8",
296            Self::SmallInt => "INT16",
297            Self::Integer => "INT32",
298            Self::BigInt => "INT64",
299            Self::HugeInt => "INT128",
300            Self::UTinyInt => "UINT8",
301            Self::USmallInt => "UINT16",
302            Self::UInteger => "UINT32",
303            Self::UBigInt => "UINT64",
304            Self::UHugeInt => "UINT128",
305            other => return other.to_string(),
306        };
307        name.to_string()
308    }
309
310    /// The widest decimal the integer behind this one holds, or `None` when this is not a decimal.
311    ///
312    /// A decimal is stored in the narrowest of `i16`, `i32`, `i64` and `i128` that fits its width,
313    /// and a message names the bucket rather than the declaration, so a DECIMAL(18,8) and a
314    /// DECIMAL(11,0) are both `DECIMAL(18)`. The two wide buckets were measured. The two narrow
315    /// ones follow the same rule and are hard to reach, since a decimal that narrow widens before
316    /// it can overflow.
317    #[must_use]
318    pub fn decimal_storage(&self) -> Option<u8> {
319        let Self::Decimal { width, .. } = self else {
320            return None;
321        };
322        Some(match width {
323            0..=4 => 4,
324            5..=9 => 9,
325            10..=18 => 18,
326            _ => MAX_DECIMAL_WIDTH,
327        })
328    }
329
330    /// Whether arithmetic applies.
331    #[must_use]
332    pub fn is_numeric(&self) -> bool {
333        self.is_integer() || matches!(self, Self::Float | Self::Double | Self::Decimal { .. })
334    }
335
336    /// Whether this is one of the integer types, signed or unsigned.
337    #[must_use]
338    pub fn is_integer(&self) -> bool {
339        matches!(
340            self,
341            Self::TinyInt
342                | Self::SmallInt
343                | Self::Integer
344                | Self::BigInt
345                | Self::HugeInt
346                | Self::UTinyInt
347                | Self::USmallInt
348                | Self::UInteger
349                | Self::UBigInt
350                | Self::UHugeInt
351        )
352    }
353
354    /// The width and scale of the decimal that holds every value of this type exactly.
355    ///
356    /// A decimal is its own, an integer is one with no fraction and room for its digits, and
357    /// nothing else has one. This is what the rule for the type of a product is written in terms
358    /// of, since `DECIMAL(4,2) * INTEGER` is as wide as `DECIMAL(4,2) * DECIMAL(10,0)` upstream and
359    /// the two ought to be the same line of code here.
360    #[must_use]
361    pub fn decimal_shape(&self) -> Option<(u8, u8)> {
362        match self {
363            Self::Decimal { width, scale } => Some((*width, *scale)),
364            other if other.is_integer() => Some((decimal_digits(other), 0)),
365            _ => None,
366        }
367    }
368
369    /// Whether this is a date, a time, a timestamp or an interval.
370    #[must_use]
371    pub fn is_temporal(&self) -> bool {
372        matches!(
373            self,
374            Self::Date
375                | Self::Time
376                | Self::TimeTz
377                | Self::Timestamp
378                | Self::TimestampS
379                | Self::TimestampMs
380                | Self::TimestampNs
381                | Self::TimestampTz
382                | Self::Interval
383        )
384    }
385
386    /// Whether this type contains other types.
387    ///
388    /// Nested types are stored columnar all the way down, so this is the question of whether a
389    /// column of this type is one column chunk or several.
390    #[must_use]
391    pub fn is_nested(&self) -> bool {
392        matches!(
393            self,
394            Self::List(_) | Self::Array(_, _) | Self::Struct(_) | Self::Map(_, _) | Self::Union(_)
395        )
396    }
397
398    /// Whether two values of this type are equal exactly when they are the same key.
399    ///
400    /// The question a hash table asks before it is used to answer an equality. A hash table has one
401    /// bucket per value, so it can only answer `=` for a type where being the same bytes and being
402    /// equal are the same thing. The scalar types are like that. A list containing a null is not: it
403    /// compares to another list by SQL's rules rather than by its bytes, so a table that treated two
404    /// such lists as one key would answer a join with rows `=` says nothing about. `NULL` is not
405    /// either, for the same reason one step further along.
406    ///
407    /// Asked in two places, which is why it lives here rather than in one of them. The join operator
408    /// asks it to decide whether to build a table or to compare every pair, and the optimizer asks it
409    /// before it moves a predicate onto a join, because a predicate that costs the join its table
410    /// costs more than it saves.
411    #[must_use]
412    pub fn is_keyed(&self) -> bool {
413        !self.is_nested() && !matches!(self, Self::Null)
414    }
415
416    /// The type both of these can be cast to without losing a value, if there is one.
417    ///
418    /// This is DuckDB's `MaxLogicalType` and it is where the type of `a + b` starts, and it is the
419    /// whole answer for the arms of a `CASE` and the columns of a `UNION`. An addition takes one
420    /// more digit than this when the answer is a decimal, because two eighteen digit numbers add to
421    /// nineteen, and that part is the signature table's rather than this function's: a `UNION` of
422    /// two `DECIMAL(18,0)` columns is a `DECIMAL(18,0)` and their sum is not. The rule is a total
423    /// order over the numeric types
424    /// with everything else absorbing into `VARCHAR` only when it is asked to, and `NULL` absorbing
425    /// into anything, which is what makes `CASE WHEN c THEN NULL ELSE 1 END` an integer.
426    ///
427    /// Mixing signed and unsigned widens rather than reinterprets, so `INTEGER` and `UINTEGER`
428    /// promote to `BIGINT` and not to either of themselves. That costs a byte per value on a case
429    /// that is rare and it is the only version that never silently changes a number, which matters
430    /// more here than the byte does: a wrong answer that is off by 4,294,967,296 is the worst kind
431    /// of bug this engine can have.
432    ///
433    /// Returns `None` when there is no such type, which is the binder's cue to raise rather than to
434    /// guess. Two different structs are `None` and not a struct of promoted fields, because field
435    /// order and field names would have to match and a rule that sometimes works is worse here than
436    /// one that never does.
437    #[must_use]
438    pub fn promote(&self, other: &Self) -> Option<Self> {
439        if self == other {
440            return Some(self.clone());
441        }
442        match (self, other) {
443            (Self::Null, ty) | (ty, Self::Null) => Some(ty.clone()),
444            (Self::List(left), Self::List(right)) => Some(Self::list(left.promote(right)?)),
445            _ if self.is_numeric() && other.is_numeric() => {
446                Some(promote_numeric(self.clone(), other.clone()))
447            }
448            // A date and a timestamp meet at the wider one, which is the timestamp, and the same
449            // holds for the timestamp units. `rank_temporal` is what says which is wider.
450            _ if self.is_temporal() && other.is_temporal() => {
451                match (rank_temporal(self), rank_temporal(other)) {
452                    (Some(left), Some(right)) => {
453                        Some(if left >= right { self.clone() } else { other.clone() })
454                    }
455                    _ => None,
456                }
457            }
458            _ => None,
459        }
460    }
461
462    /// The types this one contains, in child column order, or empty for a scalar type.
463    #[must_use]
464    pub fn children(&self) -> Vec<Self> {
465        match self {
466            Self::List(inner) | Self::Array(inner, _) => vec![inner.as_ref().clone()],
467            Self::Map(key, value) => vec![key.as_ref().clone(), value.as_ref().clone()],
468            Self::Struct(fields) | Self::Union(fields) => {
469                fields.iter().map(|f| f.ty.clone()).collect()
470            }
471            _ => Vec::new(),
472        }
473    }
474
475    /// Parses a SQL type name, aliases included.
476    ///
477    /// # Errors
478    ///
479    /// If the text is not a type name this understands. The message names the offending word,
480    /// because a type parse failure two levels inside a `STRUCT` is otherwise unreadable.
481    pub fn parse(text: &str) -> Result<Self> {
482        let tokens = lex(text)?;
483        let mut parser = TypeParser { tokens: &tokens, position: 0 };
484        let ty = parser.parse_type()?;
485        if parser.position != parser.tokens.len() {
486            return Err(Error::parser(format!("Type \"{text}\" has trailing text")));
487        }
488        Ok(ty)
489    }
490}
491
492/// The largest number of decimal digits a `DECIMAL` can carry, because 38 digits is what fits in
493/// 128 bits and there is no wider physical type.
494pub const MAX_DECIMAL_WIDTH: u8 = 38;
495
496impl fmt::Display for LogicalType {
497    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
498        match self {
499            Self::Null => f.write_str("\"NULL\""),
500            Self::Boolean => f.write_str("BOOLEAN"),
501            Self::TinyInt => f.write_str("TINYINT"),
502            Self::SmallInt => f.write_str("SMALLINT"),
503            Self::Integer => f.write_str("INTEGER"),
504            Self::BigInt => f.write_str("BIGINT"),
505            Self::HugeInt => f.write_str("HUGEINT"),
506            Self::UTinyInt => f.write_str("UTINYINT"),
507            Self::USmallInt => f.write_str("USMALLINT"),
508            Self::UInteger => f.write_str("UINTEGER"),
509            Self::UBigInt => f.write_str("UBIGINT"),
510            Self::UHugeInt => f.write_str("UHUGEINT"),
511            Self::Float => f.write_str("FLOAT"),
512            Self::Double => f.write_str("DOUBLE"),
513            Self::Decimal { width, scale } => write!(f, "DECIMAL({width},{scale})"),
514            Self::Varchar => f.write_str("VARCHAR"),
515            Self::Blob => f.write_str("BLOB"),
516            Self::Bit => f.write_str("BIT"),
517            Self::Uuid => f.write_str("UUID"),
518            Self::Date => f.write_str("DATE"),
519            Self::Time => f.write_str("TIME"),
520            Self::TimeTz => f.write_str("TIME WITH TIME ZONE"),
521            Self::Timestamp => f.write_str("TIMESTAMP"),
522            Self::TimestampS => f.write_str("TIMESTAMP_S"),
523            Self::TimestampMs => f.write_str("TIMESTAMP_MS"),
524            Self::TimestampNs => f.write_str("TIMESTAMP_NS"),
525            Self::TimestampTz => f.write_str("TIMESTAMP WITH TIME ZONE"),
526            Self::Interval => f.write_str("INTERVAL"),
527            Self::List(inner) => write!(f, "{inner}[]"),
528            Self::Array(inner, length) => write!(f, "{inner}[{length}]"),
529            Self::Map(key, value) => write!(f, "MAP({key}, {value})"),
530            Self::Struct(fields) => write_fields(f, "STRUCT", fields),
531            Self::Union(fields) => write_fields(f, "UNION", fields),
532        }
533    }
534}
535
536/// Where a numeric type sits in the widening order.
537///
538/// The integers are ordered by how many values they hold, which is why an unsigned type ranks
539/// above the signed type of the same width. That order alone is not enough to promote a signed
540/// type with an unsigned one, since neither holds the other, and [`promote_integers`] is what
541/// handles that case before this function is reached.
542fn rank_numeric(ty: &LogicalType) -> u8 {
543    match ty {
544        LogicalType::TinyInt => 1,
545        LogicalType::UTinyInt => 2,
546        LogicalType::SmallInt => 3,
547        LogicalType::USmallInt => 4,
548        LogicalType::Integer => 5,
549        LogicalType::UInteger => 6,
550        LogicalType::BigInt => 7,
551        LogicalType::UBigInt => 8,
552        LogicalType::HugeInt => 9,
553        LogicalType::UHugeInt => 10,
554        LogicalType::Decimal { .. } => 11,
555        LogicalType::Float => 12,
556        LogicalType::Double => 13,
557        _ => 0,
558    }
559}
560
561/// Whether an integer type is signed, and how many bits it is.
562fn integer_shape(ty: &LogicalType) -> (bool, u8) {
563    match ty {
564        LogicalType::TinyInt => (true, 8),
565        LogicalType::SmallInt => (true, 16),
566        LogicalType::Integer => (true, 32),
567        LogicalType::BigInt => (true, 64),
568        LogicalType::HugeInt => (true, 128),
569        LogicalType::UTinyInt => (false, 8),
570        LogicalType::USmallInt => (false, 16),
571        LogicalType::UInteger => (false, 32),
572        LogicalType::UBigInt => (false, 64),
573        _ => (false, 128),
574    }
575}
576
577/// The integer type of that many bits and that signedness.
578fn integer_of(signed: bool, bits: u8) -> Option<LogicalType> {
579    Some(match (signed, bits) {
580        (true, 8) => LogicalType::TinyInt,
581        (true, 16) => LogicalType::SmallInt,
582        (true, 32) => LogicalType::Integer,
583        (true, 64) => LogicalType::BigInt,
584        (true, 128) => LogicalType::HugeInt,
585        (false, 8) => LogicalType::UTinyInt,
586        (false, 16) => LogicalType::USmallInt,
587        (false, 32) => LogicalType::UInteger,
588        (false, 64) => LogicalType::UBigInt,
589        (false, 128) => LogicalType::UHugeInt,
590        _ => return None,
591    })
592}
593
594/// The narrowest integer type that holds every value of both.
595///
596/// Two of the same signedness are just the wider one. A signed and an unsigned need a signed type
597/// strictly wider than the unsigned one, because a `UBIGINT` of 2^63 does not fit in a `BIGINT`
598/// and a `BIGINT` of -1 does not fit in a `UBIGINT`. When that runs off the end of the integer
599/// types, which is only `UHUGEINT` against a signed type, the answer is `DOUBLE`: it loses
600/// precision past 2^53 and it is the only thing left, and DuckDB does the same.
601fn promote_integers(left: &LogicalType, right: &LogicalType) -> LogicalType {
602    let (left_signed, left_bits) = integer_shape(left);
603    let (right_signed, right_bits) = integer_shape(right);
604    if left_signed == right_signed {
605        return if left_bits >= right_bits { left.clone() } else { right.clone() };
606    }
607    let (signed_bits, unsigned_bits) =
608        if left_signed { (left_bits, right_bits) } else { (right_bits, left_bits) };
609    let wanted = signed_bits.max(unsigned_bits.saturating_mul(2));
610    integer_of(true, wanted).unwrap_or(LogicalType::Double)
611}
612
613/// The narrowest numeric type that holds every value of both.
614fn promote_numeric(left: LogicalType, right: LogicalType) -> LogicalType {
615    // A decimal and a float meet at the float, since the ranks already say so, but two decimals
616    // meet at one wide enough for both the integer part and the fraction of each, which the ranks
617    // cannot express.
618    if let (
619        LogicalType::Decimal { width: left_width, scale: left_scale },
620        LogicalType::Decimal { width: right_width, scale: right_scale },
621    ) = (&left, &right)
622    {
623        let scale = (*left_scale).max(*right_scale);
624        let integral =
625            left_width.saturating_sub(*left_scale).max(right_width.saturating_sub(*right_scale));
626        let width = integral.saturating_add(scale).min(MAX_DECIMAL_WIDTH);
627        return LogicalType::Decimal { width, scale: scale.min(width) };
628    }
629    // An integer and a decimal have to leave room for the integer's digits to the left of the
630    // point, so the decimal widens rather than the integer simply casting into it.
631    let widened = match (&left, &right) {
632        (LogicalType::Decimal { width, scale }, other)
633        | (other, LogicalType::Decimal { width, scale })
634            if other.is_integer() =>
635        {
636            let needed = decimal_digits(other).saturating_add(*scale).min(MAX_DECIMAL_WIDTH);
637            Some(LogicalType::Decimal { width: (*width).max(needed), scale: *scale })
638        }
639        _ => None,
640    };
641    if let Some(ty) = widened {
642        return ty;
643    }
644    if left.is_integer() && right.is_integer() {
645        return promote_integers(&left, &right);
646    }
647    if rank_numeric(&left) >= rank_numeric(&right) { left } else { right }
648}
649
650/// How many decimal digits an integer type needs, which is what a decimal has to leave room for.
651///
652/// It is the digits of the largest value the type holds, which is why `BIGINT` is nineteen and
653/// `UBIGINT` is twenty: 9,223,372,036,854,775,807 against 18,446,744,073,709,551,615. The pair
654/// below it is not like that, because a signed type and the unsigned type of the same width have
655/// the same digit count once the sign is off the front. Measured on `v2.0.0-dev84237` through the
656/// type of a sum: `DECIMAL(4,2)` with a `BIGINT` is `DECIMAL(22,2)` and with a `UBIGINT` is
657/// `DECIMAL(23,2)`.
658fn decimal_digits(ty: &LogicalType) -> u8 {
659    match ty {
660        LogicalType::TinyInt | LogicalType::UTinyInt => 3,
661        LogicalType::SmallInt | LogicalType::USmallInt => 5,
662        LogicalType::Integer | LogicalType::UInteger => 10,
663        LogicalType::BigInt => 19,
664        LogicalType::UBigInt => 20,
665        _ => MAX_DECIMAL_WIDTH,
666    }
667}
668
669/// Where a temporal type sits in the widening order, or `None` if it does not widen into another.
670///
671/// An interval is a duration and not a point in time, so it has no rank and never promotes with a
672/// timestamp. That is the difference between `t + INTERVAL 1 DAY`, which is a function call the
673/// binder resolves, and `CASE WHEN c THEN t ELSE INTERVAL 1 DAY END`, which has no type.
674fn rank_temporal(ty: &LogicalType) -> Option<u8> {
675    match ty {
676        LogicalType::Date => Some(1),
677        LogicalType::TimestampS => Some(2),
678        LogicalType::TimestampMs => Some(3),
679        LogicalType::Timestamp => Some(4),
680        LogicalType::TimestampNs => Some(5),
681        LogicalType::TimestampTz => Some(6),
682        _ => None,
683    }
684}
685
686fn write_fields(f: &mut fmt::Formatter<'_>, keyword: &str, fields: &[Field]) -> fmt::Result {
687    f.write_str(keyword)?;
688    f.write_str("(")?;
689    for (index, field) in fields.iter().enumerate() {
690        if index > 0 {
691            f.write_str(", ")?;
692        }
693        write_identifier(f, &field.name)?;
694        write!(f, " {}", field.ty)?;
695    }
696    f.write_str(")")
697}
698
699/// Writes a field name, quoting it if it would not survive being read back unquoted.
700fn write_identifier(f: &mut fmt::Formatter<'_>, name: &str) -> fmt::Result {
701    let plain = !name.is_empty()
702        && name.chars().next().is_some_and(|c| c.is_ascii_alphabetic() || c == '_')
703        && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_');
704    if plain {
705        f.write_str(name)
706    } else {
707        f.write_str("\"")?;
708        for c in name.chars() {
709            if c == '"' {
710                f.write_str("\"\"")?;
711            } else {
712                write!(f, "{c}")?;
713            }
714        }
715        f.write_str("\"")
716    }
717}
718
719#[derive(Debug, Clone, PartialEq, Eq)]
720enum Token {
721    Word(String),
722    Quoted(String),
723    Number(u32),
724    LeftParen,
725    RightParen,
726    LeftBracket,
727    RightBracket,
728    Comma,
729}
730
731fn lex(text: &str) -> Result<Vec<Token>> {
732    let mut tokens = Vec::new();
733    let chars: Vec<char> = text.chars().collect();
734    let mut i = 0;
735    while i < chars.len() {
736        let c = chars[i];
737        match c {
738            c if c.is_whitespace() => i += 1,
739            '(' => {
740                tokens.push(Token::LeftParen);
741                i += 1;
742            }
743            ')' => {
744                tokens.push(Token::RightParen);
745                i += 1;
746            }
747            '[' => {
748                tokens.push(Token::LeftBracket);
749                i += 1;
750            }
751            ']' => {
752                tokens.push(Token::RightBracket);
753                i += 1;
754            }
755            ',' => {
756                tokens.push(Token::Comma);
757                i += 1;
758            }
759            '"' => {
760                let mut name = String::new();
761                i += 1;
762                loop {
763                    let Some(&c) = chars.get(i) else {
764                        return Err(Error::parser(format!(
765                            "Type \"{text}\" has an unterminated quoted name"
766                        )));
767                    };
768                    i += 1;
769                    if c == '"' {
770                        if chars.get(i) == Some(&'"') {
771                            name.push('"');
772                            i += 1;
773                            continue;
774                        }
775                        break;
776                    }
777                    name.push(c);
778                }
779                tokens.push(Token::Quoted(name));
780            }
781            c if c.is_ascii_digit() => {
782                let start = i;
783                while chars.get(i).is_some_and(char::is_ascii_digit) {
784                    i += 1;
785                }
786                let digits: String = chars[start..i].iter().collect();
787                let number = digits.parse::<u32>().map_err(|_| {
788                    Error::parser(format!("Type \"{text}\" has a number that is too large"))
789                })?;
790                tokens.push(Token::Number(number));
791            }
792            c if c.is_alphabetic() || c == '_' => {
793                let start = i;
794                while chars.get(i).is_some_and(|&c| c.is_alphanumeric() || c == '_') {
795                    i += 1;
796                }
797                tokens.push(Token::Word(chars[start..i].iter().collect()));
798            }
799            other => {
800                return Err(Error::parser(format!(
801                    "Type \"{text}\" has an unexpected character {other:?}"
802                )));
803            }
804        }
805    }
806    Ok(tokens)
807}
808
809struct TypeParser<'a> {
810    tokens: &'a [Token],
811    position: usize,
812}
813
814impl TypeParser<'_> {
815    fn peek(&self) -> Option<&Token> {
816        self.tokens.get(self.position)
817    }
818
819    fn eat(&mut self, token: &Token) -> bool {
820        if self.peek() == Some(token) {
821            self.position += 1;
822            true
823        } else {
824            false
825        }
826    }
827
828    /// Consumes `word` if it is next, case insensitively.
829    fn eat_word(&mut self, word: &str) -> bool {
830        match self.peek() {
831            Some(Token::Word(found)) if found.eq_ignore_ascii_case(word) => {
832                self.position += 1;
833                true
834            }
835            _ => false,
836        }
837    }
838
839    fn parse_type(&mut self) -> Result<LogicalType> {
840        let mut ty = self.parse_base()?;
841        // Suffixes bind left to right, so INTEGER[][3] is an array of three lists.
842        loop {
843            if !self.eat(&Token::LeftBracket) {
844                break;
845            }
846            if let Some(&Token::Number(length)) = self.peek() {
847                self.position += 1;
848                expect(self.eat(&Token::RightBracket), "]")?;
849                ty = LogicalType::array(ty, length);
850            } else {
851                expect(self.eat(&Token::RightBracket), "]")?;
852                ty = LogicalType::list(ty);
853            }
854        }
855        Ok(ty)
856    }
857
858    fn parse_base(&mut self) -> Result<LogicalType> {
859        // A quoted name is accepted here because the null type prints as "NULL" with the quotes,
860        // which is DuckDB's spelling and which has to read back.
861        let word = match self.peek().cloned() {
862            Some(Token::Word(word) | Token::Quoted(word)) => {
863                self.position += 1;
864                word
865            }
866            _ => return Err(Error::parser("Expected a type name".to_string())),
867        };
868        let upper = word.to_ascii_uppercase();
869
870        match upper.as_str() {
871            "STRUCT" | "ROW" => return self.parse_fields().map(LogicalType::Struct),
872            "UNION" => return self.parse_fields().map(LogicalType::Union),
873            "MAP" => {
874                expect(self.eat(&Token::LeftParen), "(")?;
875                let key = self.parse_type()?;
876                expect(self.eat(&Token::Comma), ",")?;
877                let value = self.parse_type()?;
878                expect(self.eat(&Token::RightParen), ")")?;
879                return Ok(LogicalType::map(key, value));
880            }
881            "DECIMAL" | "NUMERIC" | "DEC" => {
882                if !self.eat(&Token::LeftParen) {
883                    // Bare DECIMAL is DECIMAL(18, 3) in DuckDB, which is a surprising default and
884                    // is nonetheless the one people's queries depend on.
885                    return LogicalType::decimal(18, 3);
886                }
887                let width = self.parse_number()?;
888                let scale = if self.eat(&Token::Comma) { self.parse_number()? } else { 0 };
889                expect(self.eat(&Token::RightParen), ")")?;
890                let narrow = |n: u32| u8::try_from(n).unwrap_or(u8::MAX);
891                return LogicalType::decimal(narrow(width), narrow(scale));
892            }
893            // Multiword names. Each of these is a word that only means something with the words
894            // after it, so the lookahead is checked before the alias table is consulted.
895            "DOUBLE" => {
896                self.eat_word("PRECISION");
897                return Ok(LogicalType::Double);
898            }
899            "CHARACTER" => {
900                self.eat_word("VARYING");
901                self.eat_length_modifier()?;
902                return Ok(LogicalType::Varchar);
903            }
904            "TIME" | "TIMESTAMP" => {
905                let with_zone = self.eat_time_zone_suffix();
906                return Ok(match (upper.as_str(), with_zone) {
907                    ("TIME", false) => LogicalType::Time,
908                    ("TIME", true) => LogicalType::TimeTz,
909                    (_, false) => LogicalType::Timestamp,
910                    (_, true) => LogicalType::TimestampTz,
911                });
912            }
913            _ => {}
914        }
915
916        // A length modifier on a string type parses and is discarded, which is what DuckDB does:
917        // VARCHAR(10) does not truncate and does not reject, it is VARCHAR.
918        self.eat_length_modifier()?;
919        alias(&upper).ok_or_else(|| Error::parser(format!("Unrecognized type name \"{word}\"")))
920    }
921
922    /// `WITH TIME ZONE` or `WITHOUT TIME ZONE`, returning whether the zone is carried.
923    fn eat_time_zone_suffix(&mut self) -> bool {
924        let start = self.position;
925        let with = if self.eat_word("WITH") {
926            true
927        } else if self.eat_word("WITHOUT") {
928            false
929        } else {
930            return false;
931        };
932        if self.eat_word("TIME") && self.eat_word("ZONE") {
933            with
934        } else {
935            self.position = start;
936            false
937        }
938    }
939
940    fn eat_length_modifier(&mut self) -> Result<()> {
941        if self.eat(&Token::LeftParen) {
942            self.parse_number()?;
943            expect(self.eat(&Token::RightParen), ")")?;
944        }
945        Ok(())
946    }
947
948    fn parse_fields(&mut self) -> Result<Vec<Field>> {
949        expect(self.eat(&Token::LeftParen), "(")?;
950        let mut fields = Vec::new();
951        if self.eat(&Token::RightParen) {
952            return Ok(fields);
953        }
954        loop {
955            let name = match self.peek().cloned() {
956                Some(Token::Word(name) | Token::Quoted(name)) => {
957                    self.position += 1;
958                    name
959                }
960                _ => return Err(Error::parser("Expected a field name".to_string())),
961            };
962            let ty = self.parse_type()?;
963            fields.push(Field::new(name, ty));
964            if self.eat(&Token::Comma) {
965                continue;
966            }
967            expect(self.eat(&Token::RightParen), ")")?;
968            return Ok(fields);
969        }
970    }
971
972    fn parse_number(&mut self) -> Result<u32> {
973        match self.peek() {
974            Some(&Token::Number(n)) => {
975                self.position += 1;
976                Ok(n)
977            }
978            _ => Err(Error::parser("Expected a number".to_string())),
979        }
980    }
981}
982
983fn expect(matched: bool, what: &str) -> Result<()> {
984    if matched { Ok(()) } else { Err(Error::parser(format!("Expected \"{what}\""))) }
985}
986
987/// The single word type names, aliases included.
988///
989/// The aliases are DuckDB's, and they are here rather than in the parser because `CREATE TABLE t
990/// (a INT4)` and `CAST(x AS INT4)` have to agree and there is only one table.
991///
992/// Every name in here was checked against the pinned binary with `typeof(NULL::name)` and every name
993/// that is not in here was checked the same way, because the numeric ones do not follow the rule they
994/// look like they follow. `int1` through `int8` count bytes, `int16` through `int128` count bits, and
995/// both series exist on the signed side, so `int8` and `int64` are the same type. On the unsigned side
996/// only the bit counting series exists, so `uint8` is one byte and `uint1` is not a type at all. An
997/// earlier version of this table assumed the two sides were symmetric and answered `UBIGINT` for
998/// `uint8`, which is the wrong type rather than an error, and nothing caught it because both engines
999/// answered. That is why `the_unsigned_names_count_bits_and_the_short_signed_ones_count_bytes` carries
1000/// the measured grid below: a name added by following the pattern is now a failing test.
1001fn alias(upper: &str) -> Option<LogicalType> {
1002    Some(match upper {
1003        "NULL" => LogicalType::Null,
1004        "BOOLEAN" | "BOOL" | "LOGICAL" => LogicalType::Boolean,
1005        "TINYINT" | "INT1" => LogicalType::TinyInt,
1006        "SMALLINT" | "INT2" | "INT16" | "SHORT" => LogicalType::SmallInt,
1007        "INTEGER" | "INT" | "INT4" | "INT32" | "SIGNED" | "INTEGRAL" => LogicalType::Integer,
1008        "BIGINT" | "INT8" | "INT64" | "LONG" | "OID" => LogicalType::BigInt,
1009        "HUGEINT" | "INT128" => LogicalType::HugeInt,
1010        "UTINYINT" | "UINT8" => LogicalType::UTinyInt,
1011        "USMALLINT" | "UINT16" => LogicalType::USmallInt,
1012        "UINTEGER" | "UINT32" => LogicalType::UInteger,
1013        "UBIGINT" | "UINT64" => LogicalType::UBigInt,
1014        "UHUGEINT" | "UINT128" => LogicalType::UHugeInt,
1015        "FLOAT" | "FLOAT4" | "REAL" => LogicalType::Float,
1016        "FLOAT8" => LogicalType::Double,
1017        "VARCHAR" | "CHAR" | "BPCHAR" | "TEXT" | "STRING" | "NVARCHAR" => LogicalType::Varchar,
1018        "BLOB" | "BYTEA" | "BINARY" | "VARBINARY" => LogicalType::Blob,
1019        "BIT" | "BITSTRING" => LogicalType::Bit,
1020        "UUID" | "GUID" => LogicalType::Uuid,
1021        "DATE" => LogicalType::Date,
1022        "TIMETZ" => LogicalType::TimeTz,
1023        "DATETIME" | "TIMESTAMP_US" => LogicalType::Timestamp,
1024        "TIMESTAMP_S" => LogicalType::TimestampS,
1025        "TIMESTAMP_MS" => LogicalType::TimestampMs,
1026        "TIMESTAMP_NS" => LogicalType::TimestampNs,
1027        "TIMESTAMPTZ" => LogicalType::TimestampTz,
1028        "INTERVAL" => LogicalType::Interval,
1029        _ => return None,
1030    })
1031}
1032
1033#[cfg(test)]
1034mod promotion_tests {
1035    use super::LogicalType;
1036
1037    #[test]
1038    fn a_type_promotes_with_itself_to_itself() {
1039        for ty in [
1040            LogicalType::Integer,
1041            LogicalType::Varchar,
1042            LogicalType::Boolean,
1043            LogicalType::Struct(vec![]),
1044        ] {
1045            assert_eq!(ty.promote(&ty), Some(ty.clone()), "{ty} does not promote with itself");
1046        }
1047    }
1048
1049    #[test]
1050    fn null_takes_the_other_type() {
1051        assert_eq!(LogicalType::Null.promote(&LogicalType::Varchar), Some(LogicalType::Varchar));
1052        assert_eq!(LogicalType::Date.promote(&LogicalType::Null), Some(LogicalType::Date));
1053        assert_eq!(LogicalType::Null.promote(&LogicalType::Null), Some(LogicalType::Null));
1054    }
1055
1056    #[test]
1057    fn the_wider_number_wins() {
1058        assert_eq!(
1059            LogicalType::Integer.promote(&LogicalType::SmallInt),
1060            Some(LogicalType::Integer)
1061        );
1062        assert_eq!(LogicalType::Integer.promote(&LogicalType::Double), Some(LogicalType::Double));
1063        assert_eq!(LogicalType::Float.promote(&LogicalType::Double), Some(LogicalType::Double));
1064    }
1065
1066    /// The one that is worth a test of its own, because reinterpreting instead of widening here is
1067    /// a wrong answer off by four billion rather than a crash.
1068    #[test]
1069    fn signed_and_unsigned_widen_rather_than_reinterpret() {
1070        assert_eq!(LogicalType::Integer.promote(&LogicalType::UInteger), Some(LogicalType::BigInt));
1071        assert_eq!(
1072            LogicalType::TinyInt.promote(&LogicalType::UTinyInt),
1073            Some(LogicalType::SmallInt)
1074        );
1075        assert_eq!(LogicalType::BigInt.promote(&LogicalType::UBigInt), Some(LogicalType::HugeInt));
1076    }
1077
1078    #[test]
1079    fn promotion_does_not_care_which_side_a_type_is_on() {
1080        let types = [
1081            LogicalType::TinyInt,
1082            LogicalType::UInteger,
1083            LogicalType::BigInt,
1084            LogicalType::Double,
1085            LogicalType::Decimal { width: 10, scale: 2 },
1086            LogicalType::Null,
1087            LogicalType::Varchar,
1088            LogicalType::Date,
1089            LogicalType::Timestamp,
1090        ];
1091        for left in &types {
1092            for right in &types {
1093                assert_eq!(
1094                    left.promote(right),
1095                    right.promote(left),
1096                    "{left} and {right} promote differently depending on the order"
1097                );
1098            }
1099        }
1100    }
1101
1102    #[test]
1103    fn a_decimal_keeps_room_for_both_halves() {
1104        let left = LogicalType::Decimal { width: 5, scale: 4 };
1105        let right = LogicalType::Decimal { width: 5, scale: 1 };
1106        assert_eq!(left.promote(&right), Some(LogicalType::Decimal { width: 8, scale: 4 }));
1107    }
1108
1109    #[test]
1110    fn an_integer_next_to_a_decimal_widens_the_decimal() {
1111        let decimal = LogicalType::Decimal { width: 5, scale: 2 };
1112        assert_eq!(
1113            decimal.promote(&LogicalType::Integer),
1114            Some(LogicalType::Decimal { width: 12, scale: 2 })
1115        );
1116    }
1117
1118    /// A signed and an unsigned integer of the same width leave different room, at the top pair.
1119    ///
1120    /// 9,223,372,036,854,775,807 is nineteen digits and 18,446,744,073,709,551,615 is twenty, so
1121    /// the two do not promote with a decimal to the same type, and every narrower pair does.
1122    #[test]
1123    fn a_bigint_leaves_room_for_one_digit_fewer_than_a_ubigint() {
1124        let decimal = LogicalType::Decimal { width: 4, scale: 2 };
1125        assert_eq!(
1126            decimal.promote(&LogicalType::BigInt),
1127            Some(LogicalType::Decimal { width: 21, scale: 2 })
1128        );
1129        assert_eq!(
1130            decimal.promote(&LogicalType::UBigInt),
1131            Some(LogicalType::Decimal { width: 22, scale: 2 })
1132        );
1133        assert_eq!(decimal.promote(&LogicalType::Integer), decimal.promote(&LogicalType::UInteger));
1134    }
1135
1136    #[test]
1137    fn a_date_and_a_timestamp_meet_at_the_timestamp() {
1138        assert_eq!(
1139            LogicalType::Date.promote(&LogicalType::Timestamp),
1140            Some(LogicalType::Timestamp)
1141        );
1142        assert_eq!(
1143            LogicalType::TimestampS.promote(&LogicalType::TimestampNs),
1144            Some(LogicalType::TimestampNs)
1145        );
1146    }
1147
1148    /// An interval is a duration and a timestamp is a point, so there is no type that holds both
1149    /// and saying so is the binder's cue to raise instead of guessing.
1150    #[test]
1151    fn types_that_do_not_meet_say_so() {
1152        assert_eq!(LogicalType::Timestamp.promote(&LogicalType::Interval), None);
1153        assert_eq!(LogicalType::Integer.promote(&LogicalType::Varchar), None);
1154        assert_eq!(LogicalType::Boolean.promote(&LogicalType::Integer), None);
1155    }
1156
1157    #[test]
1158    fn a_list_promotes_by_its_element() {
1159        let left = LogicalType::list(LogicalType::Integer);
1160        let right = LogicalType::list(LogicalType::BigInt);
1161        assert_eq!(left.promote(&right), Some(LogicalType::list(LogicalType::BigInt)));
1162        assert_eq!(left.promote(&LogicalType::list(LogicalType::Varchar)), None);
1163    }
1164}
1165
1166#[cfg(test)]
1167mod tests {
1168    use super::{Field, LogicalType, PhysicalType};
1169
1170    /// Every type this crate knows about, used by the round trip test and by anything else that
1171    /// wants to be exhaustive without listing them again.
1172    fn every_type() -> Vec<LogicalType> {
1173        vec![
1174            LogicalType::Null,
1175            LogicalType::Boolean,
1176            LogicalType::TinyInt,
1177            LogicalType::SmallInt,
1178            LogicalType::Integer,
1179            LogicalType::BigInt,
1180            LogicalType::HugeInt,
1181            LogicalType::UTinyInt,
1182            LogicalType::USmallInt,
1183            LogicalType::UInteger,
1184            LogicalType::UBigInt,
1185            LogicalType::UHugeInt,
1186            LogicalType::Float,
1187            LogicalType::Double,
1188            LogicalType::Decimal { width: 18, scale: 3 },
1189            LogicalType::Decimal { width: 38, scale: 0 },
1190            LogicalType::Varchar,
1191            LogicalType::Blob,
1192            LogicalType::Bit,
1193            LogicalType::Uuid,
1194            LogicalType::Date,
1195            LogicalType::Time,
1196            LogicalType::TimeTz,
1197            LogicalType::Timestamp,
1198            LogicalType::TimestampS,
1199            LogicalType::TimestampMs,
1200            LogicalType::TimestampNs,
1201            LogicalType::TimestampTz,
1202            LogicalType::Interval,
1203            LogicalType::list(LogicalType::Integer),
1204            LogicalType::list(LogicalType::list(LogicalType::Varchar)),
1205            LogicalType::array(LogicalType::Double, 3),
1206            LogicalType::map(LogicalType::Varchar, LogicalType::Integer),
1207            LogicalType::Struct(vec![
1208                Field::new("a", LogicalType::Integer),
1209                Field::new("b", LogicalType::list(LogicalType::Varchar)),
1210            ]),
1211            LogicalType::Union(vec![
1212                Field::new("num", LogicalType::Integer),
1213                Field::new("str", LogicalType::Varchar),
1214            ]),
1215        ]
1216    }
1217
1218    #[test]
1219    fn every_type_survives_being_printed_and_read_back() {
1220        // The textual plan format in spec/04-architecture.md round trips, and a plan carries
1221        // types, so this is the bottom of that guarantee. Failing it means a plan that cannot be
1222        // reparsed, which is the whole reason the format exists.
1223        for ty in every_type() {
1224            let printed = ty.to_string();
1225            let parsed = LogicalType::parse(&printed)
1226                .unwrap_or_else(|e| panic!("{printed} did not parse back: {e}"));
1227            assert_eq!(parsed, ty, "{printed} parsed to something else");
1228        }
1229    }
1230
1231    #[test]
1232    fn a_field_name_that_needs_quoting_gets_quoted() {
1233        let ty = LogicalType::Struct(vec![
1234            Field::new("plain", LogicalType::Integer),
1235            Field::new("has space", LogicalType::Integer),
1236            Field::new("has\"quote", LogicalType::Integer),
1237            Field::new("2leading", LogicalType::Integer),
1238        ]);
1239        assert_eq!(
1240            ty.to_string(),
1241            "STRUCT(plain INTEGER, \"has space\" INTEGER, \"has\"\"quote\" INTEGER, \
1242             \"2leading\" INTEGER)"
1243        );
1244        assert_eq!(LogicalType::parse(&ty.to_string()).unwrap(), ty);
1245    }
1246
1247    #[test]
1248    fn the_duckdb_aliases_resolve() {
1249        let cases = [
1250            ("int4", LogicalType::Integer),
1251            ("INT", LogicalType::Integer),
1252            ("signed", LogicalType::Integer),
1253            ("int8", LogicalType::BigInt),
1254            ("float4", LogicalType::Float),
1255            ("float8", LogicalType::Double),
1256            ("double precision", LogicalType::Double),
1257            ("text", LogicalType::Varchar),
1258            ("varchar(10)", LogicalType::Varchar),
1259            ("character varying(255)", LogicalType::Varchar),
1260            ("bool", LogicalType::Boolean),
1261            ("datetime", LogicalType::Timestamp),
1262            ("numeric(9, 2)", LogicalType::Decimal { width: 9, scale: 2 }),
1263            ("decimal", LogicalType::Decimal { width: 18, scale: 3 }),
1264            ("timestamp without time zone", LogicalType::Timestamp),
1265            ("timestamp with time zone", LogicalType::TimestampTz),
1266            ("time with time zone", LogicalType::TimeTz),
1267        ];
1268        for (text, expected) in cases {
1269            assert_eq!(LogicalType::parse(text).unwrap(), expected, "{text}");
1270        }
1271    }
1272
1273    #[test]
1274    fn the_unsigned_names_count_bits_and_the_short_signed_ones_count_bytes() {
1275        // Read off `typeof(NULL::name)` in DuckDB v2.0.0-dev84237 cc7e7bac7f, one row per name, both
1276        // the ones that resolve and the ones that do not. The pattern is deliberately written out
1277        // rather than described, because the last time somebody described it they extended the signed
1278        // side onto the unsigned side and `uint8` came out eight bytes wide instead of one.
1279        let resolves = [
1280            ("int1", LogicalType::TinyInt),
1281            ("int2", LogicalType::SmallInt),
1282            ("int4", LogicalType::Integer),
1283            ("int8", LogicalType::BigInt),
1284            ("int16", LogicalType::SmallInt),
1285            ("int32", LogicalType::Integer),
1286            ("int64", LogicalType::BigInt),
1287            ("int128", LogicalType::HugeInt),
1288            ("uint8", LogicalType::UTinyInt),
1289            ("uint16", LogicalType::USmallInt),
1290            ("uint32", LogicalType::UInteger),
1291            ("uint64", LogicalType::UBigInt),
1292            ("uint128", LogicalType::UHugeInt),
1293        ];
1294        for (text, expected) in resolves {
1295            assert_eq!(LogicalType::parse(text).unwrap(), expected, "{text}");
1296        }
1297        // DuckDB answers `Catalog Error: Type with name uint1 does not exist!` for these, so a name
1298        // this engine invents to fill the gap is a query that works here and fails there.
1299        for text in ["uint1", "uint2", "uint4"] {
1300            assert!(LogicalType::parse(text).is_err(), "{text} should not be a type name");
1301        }
1302    }
1303
1304    #[test]
1305    fn the_four_timestamp_units_are_the_only_four_spellings() {
1306        let resolves = [
1307            ("timestamp_s", LogicalType::TimestampS),
1308            ("timestamp_ms", LogicalType::TimestampMs),
1309            ("timestamp_us", LogicalType::Timestamp),
1310            ("timestamp_ns", LogicalType::TimestampNs),
1311        ];
1312        for (text, expected) in resolves {
1313            assert_eq!(LogicalType::parse(text).unwrap(), expected, "{text}");
1314        }
1315        // The long forms read like they ought to exist and none of them does. `timestamp_us` is the
1316        // one that does and the one this engine used to be missing, which is the same mistake in the
1317        // other direction: a spelling somebody guessed at instead of measuring.
1318        for text in [
1319            "timestamp_sec",
1320            "timestamp_seconds",
1321            "timestamp_milliseconds",
1322            "timestamp_nanoseconds",
1323        ] {
1324            assert!(LogicalType::parse(text).is_err(), "{text} should not be a type name");
1325        }
1326    }
1327
1328    #[test]
1329    fn the_three_aliases_that_are_not_about_width_resolve() {
1330        // `integral` and `oid` are Postgres inheritance and `nvarchar` is somebody else's dialect,
1331        // and all three are in `duckdb_types()` so a query that casts to one has to work here.
1332        assert_eq!(LogicalType::parse("integral").unwrap(), LogicalType::Integer);
1333        assert_eq!(LogicalType::parse("oid").unwrap(), LogicalType::BigInt);
1334        assert_eq!(LogicalType::parse("nvarchar").unwrap(), LogicalType::Varchar);
1335    }
1336
1337    #[test]
1338    fn list_and_array_suffixes_bind_left_to_right() {
1339        assert_eq!(
1340            LogicalType::parse("INTEGER[][3]").unwrap(),
1341            LogicalType::array(LogicalType::list(LogicalType::Integer), 3)
1342        );
1343        assert_eq!(
1344            LogicalType::parse("STRUCT(a INT)[]").unwrap(),
1345            LogicalType::list(LogicalType::Struct(vec![Field::new("a", LogicalType::Integer)]))
1346        );
1347    }
1348
1349    #[test]
1350    fn a_decimal_is_stored_in_the_narrowest_integer_that_holds_it() {
1351        assert_eq!(LogicalType::decimal(4, 2).unwrap().physical(), PhysicalType::Int16);
1352        assert_eq!(LogicalType::decimal(9, 2).unwrap().physical(), PhysicalType::Int32);
1353        assert_eq!(LogicalType::decimal(18, 2).unwrap().physical(), PhysicalType::Int64);
1354        assert_eq!(LogicalType::decimal(38, 2).unwrap().physical(), PhysicalType::Int128);
1355    }
1356
1357    /// The name a failed cast gives a type, which is the integer it is stored in. The decimal
1358    /// buckets are the same ones `physical` uses, written as a width rather than as a layout,
1359    /// which is why a DECIMAL(11,0) and a DECIMAL(18,8) are both DECIMAL(18).
1360    #[test]
1361    fn a_message_names_the_type_by_what_it_is_stored_in() {
1362        assert_eq!(LogicalType::Boolean.physical_name(), "BOOL");
1363        assert_eq!(LogicalType::TinyInt.physical_name(), "INT8");
1364        assert_eq!(LogicalType::Integer.physical_name(), "INT32");
1365        assert_eq!(LogicalType::UBigInt.physical_name(), "UINT64");
1366        assert_eq!(LogicalType::HugeInt.physical_name(), "INT128");
1367        assert_eq!(LogicalType::Float.physical_name(), "FLOAT");
1368        assert_eq!(LogicalType::Varchar.physical_name(), "VARCHAR");
1369        assert_eq!(LogicalType::Date.physical_name(), "DATE");
1370        assert_eq!(LogicalType::decimal(4, 2).unwrap().physical_name(), "DECIMAL(4)");
1371        assert_eq!(LogicalType::decimal(11, 0).unwrap().physical_name(), "DECIMAL(18)");
1372        assert_eq!(LogicalType::decimal(18, 8).unwrap().physical_name(), "DECIMAL(18)");
1373        assert_eq!(LogicalType::decimal(38, 2).unwrap().physical_name(), "DECIMAL(38)");
1374        assert_eq!(LogicalType::Integer.decimal_storage(), None);
1375    }
1376
1377    #[test]
1378    fn a_decimal_outside_the_bounds_is_rejected_rather_than_clamped() {
1379        assert!(LogicalType::decimal(0, 0).is_err());
1380        assert!(LogicalType::decimal(39, 0).is_err());
1381        assert!(LogicalType::decimal(4, 5).is_err());
1382        assert!(LogicalType::parse("DECIMAL(39,0)").is_err());
1383    }
1384
1385    #[test]
1386    fn a_date_and_an_integer_share_a_layout_and_not_a_meaning() {
1387        assert_eq!(LogicalType::Date.physical(), LogicalType::Integer.physical());
1388        assert_ne!(LogicalType::Date, LogicalType::Integer);
1389        assert!(LogicalType::Date.is_temporal());
1390        assert!(!LogicalType::Date.is_numeric());
1391    }
1392
1393    #[test]
1394    fn nesting_reports_its_children_in_child_column_order() {
1395        let ty = LogicalType::map(LogicalType::Varchar, LogicalType::Integer);
1396        assert!(ty.is_nested());
1397        assert_eq!(ty.children(), vec![LogicalType::Varchar, LogicalType::Integer]);
1398        assert_eq!(LogicalType::Integer.children(), Vec::new());
1399    }
1400
1401    #[test]
1402    fn text_that_is_not_a_type_is_rejected_with_the_word_that_broke_it() {
1403        let error = LogicalType::parse("INTEGRE").unwrap_err();
1404        assert!(error.message().contains("INTEGRE"), "{error}");
1405        assert!(LogicalType::parse("INTEGER JUNK").is_err());
1406        assert!(LogicalType::parse("STRUCT(a)").is_err());
1407        assert!(LogicalType::parse("").is_err());
1408    }
1409}