Skip to main content

qusql_parse/
data_type.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13use alloc::{boxed::Box, vec::Vec};
14
15use crate::{
16    Identifier, InvalidExpression, SString, Span, Spanned,
17    alter_table::{ForeignKeyMatch, ForeignKeyOn, ForeignKeyOnAction, ForeignKeyOnType},
18    create::parse_sequence_options,
19    expression::{Expression, PRIORITY_MAX, parse_expression_unreserved},
20    keywords::Keyword,
21    lexer::Token,
22    parser::{ParseError, Parser},
23    qualified_name::{QualifiedName, parse_qualified_name_unreserved},
24    span::OptSpanned,
25};
26
27/// A property on a datatype
28#[derive(Debug, Clone)]
29pub enum DataTypeProperty<'a> {
30    Signed(Span),
31    Unsigned(Span),
32    Zerofill(Span),
33    Null(Span),
34    NotNull(Span),
35    Default(Expression<'a>),
36    Comment(SString<'a>),
37    Charset(Identifier<'a>),
38    Collate(Identifier<'a>),
39    Virtual(Span),
40    Persistent(Span),
41    Stored(Span),
42    Unique(Span),
43    UniqueKey(Span),
44    GeneratedAlways(Span),
45    /// PostgreSQL GENERATED ALWAYS AS (expr) STORED - computed column
46    GeneratedAlwaysAsExpr {
47        generated_always_as_span: Span,
48        expr: Expression<'a>,
49        stored_span: Option<Span>,
50    },
51    AutoIncrement(Span),
52    PrimaryKey(Span),
53    As((Span, Expression<'a>)),
54    Check((Span, Expression<'a>)),
55    OnUpdate((Span, Expression<'a>)),
56    References {
57        /// Span of the `REFERENCES` keyword
58        span: Span,
59        /// Referenced table
60        table: Identifier<'a>,
61        /// Referenced columns (may be empty if omitted)
62        columns: Vec<Identifier<'a>>,
63        /// Optional MATCH FULL / MATCH SIMPLE / MATCH PARTIAL
64        match_type: Option<ForeignKeyMatch>,
65        /// Optional ON DELETE / ON UPDATE actions
66        ons: Vec<ForeignKeyOn>,
67    },
68}
69
70impl<'a> Spanned for DataTypeProperty<'a> {
71    fn span(&self) -> Span {
72        match &self {
73            DataTypeProperty::Signed(v) => v.span(),
74            DataTypeProperty::Unsigned(v) => v.span(),
75            DataTypeProperty::Zerofill(v) => v.span(),
76            DataTypeProperty::Null(v) => v.span(),
77            DataTypeProperty::NotNull(v) => v.span(),
78            DataTypeProperty::Default(v) => v.span(),
79            DataTypeProperty::Comment(v) => v.span(),
80            DataTypeProperty::Charset(v) => v.span(),
81            DataTypeProperty::Collate(v) => v.span(),
82            DataTypeProperty::Virtual(v) => v.span(),
83            DataTypeProperty::Persistent(v) => v.span(),
84            DataTypeProperty::Stored(v) => v.span(),
85            DataTypeProperty::Unique(v) => v.span(),
86            DataTypeProperty::UniqueKey(v) => v.span(),
87            DataTypeProperty::GeneratedAlways(v) => v.span(),
88            DataTypeProperty::GeneratedAlwaysAsExpr {
89                generated_always_as_span: span,
90                expr,
91                stored_span,
92            } => span.join_span(expr).join_span(stored_span),
93            DataTypeProperty::AutoIncrement(v) => v.span(),
94            DataTypeProperty::As((s, v)) => s.join_span(v),
95            DataTypeProperty::Check((s, v)) => s.join_span(v),
96            DataTypeProperty::PrimaryKey(v) => v.span(),
97            DataTypeProperty::OnUpdate((s, v)) => s.join_span(v),
98            DataTypeProperty::References {
99                span,
100                table,
101                columns,
102                match_type,
103                ons,
104            } => span
105                .join_span(table)
106                .join_span(columns)
107                .join_span(match_type)
108                .join_span(ons),
109        }
110    }
111}
112
113#[derive(Debug, Clone)]
114pub struct Timestamp {
115    pub width: Option<(usize, Span)>,
116    pub with_time_zone: Option<Span>,
117}
118
119impl OptSpanned for Timestamp {
120    fn opt_span(&self) -> Option<Span> {
121        self.width.opt_span().opt_join_span(&self.with_time_zone)
122    }
123}
124
125/// Subtype for a built-in PostgreSQL range or multirange type.
126#[derive(Debug, Clone)]
127pub enum RangeSubtype {
128    Int4,
129    Int8,
130    Num,
131    Ts,
132    Tstz,
133    Date,
134}
135
136/// A single field qualifier for an `INTERVAL` type.
137#[derive(Debug, Clone)]
138pub enum IntervalField {
139    Year,
140    Month,
141    Day,
142    Hour,
143    Minute,
144    Second,
145}
146
147/// Interior of `Type::Interval`.
148#[derive(Debug, Clone)]
149pub struct Interval {
150    /// The first (or only) field qualifier, e.g. `YEAR` in `INTERVAL YEAR TO MONTH`.
151    pub start_field: Option<(IntervalField, Span)>,
152    /// The upper-bound field, e.g. `MONTH` in `INTERVAL YEAR TO MONTH`.
153    pub end_field: Option<(IntervalField, Span)>,
154    /// Fractional-seconds precision; only meaningful when the rightmost unit is `SECOND`.
155    pub precision: Option<(usize, Span)>,
156}
157
158impl OptSpanned for Interval {
159    fn opt_span(&self) -> Option<Span> {
160        let s = self.start_field.as_ref().map(|(_, s)| s.clone());
161        let e = self.end_field.as_ref().map(|(_, s)| s.clone());
162        s.opt_join_span(&e).opt_join_span(&self.precision)
163    }
164}
165
166/// Type of datatype
167#[derive(Debug, Clone)]
168pub enum Type<'a> {
169    Array(Box<Type<'a>>, Span),
170    BigInt(Option<(usize, Span)>),
171    BigSerial,
172    Binary(Option<(usize, Span)>),
173    Bit(usize, Span),
174    Blob(Option<(usize, Span)>),
175    Boolean,
176    Box,
177    Bytea,
178    Char(Option<(usize, Span)>),
179    Cidr,
180    Circle,
181    Date,
182    DateTime(Option<(usize, Span)>),
183    Decimal(Option<(usize, usize, Span)>),
184    Double(Option<(usize, usize, Span)>),
185    Enum(Vec<SString<'a>>),
186    Float(Option<(usize, usize, Span)>),
187    Float8,
188    Inet4,
189    Inet6,
190    InetAddr,
191    Int(Option<(usize, Span)>),
192    Integer(Option<(usize, Span)>),
193    Interval(Interval),
194    Json,
195    Jsonb,
196    Line,
197    LongBlob(Option<(usize, Span)>),
198    LongText(Option<(usize, Span)>),
199    Lseg,
200    Macaddr,
201    Macaddr8,
202    MediumBlob(Option<(usize, Span)>),
203    MediumInt(Option<(usize, Span)>),
204    MediumText(Option<(usize, Span)>),
205    Money,
206    Named(QualifiedName<'a>),
207    Path,
208    Numeric(Option<(usize, usize, Span)>),
209    Range(RangeSubtype),
210    MultiRange(RangeSubtype),
211    Serial,
212    Set(Vec<SString<'a>>),
213    Point,
214    Polygon,
215    SmallInt(Option<(usize, Span)>),
216    SmallSerial,
217    Table(Span, Vec<(Identifier<'a>, DataType<'a>)>),
218    Text(Option<(usize, Span)>),
219    Time(Option<(usize, Span)>),
220    Timestamp(Timestamp),
221    Timestamptz,
222    Timetz(Option<(usize, Span)>),
223    TsQuery,
224    TsVector,
225    TinyBlob(Option<(usize, Span)>),
226    TinyInt(Option<(usize, Span)>),
227    TinyText(Option<(usize, Span)>),
228    VarBinary((usize, Span)),
229    VarBit(Option<(usize, Span)>),
230    VarChar(Option<(usize, Span)>),
231    Uuid,
232    Xml,
233}
234
235impl<'a> OptSpanned for Type<'a> {
236    fn opt_span(&self) -> Option<Span> {
237        match &self {
238            Type::Array(inner, span) => Some(span.join_span(inner.as_ref())),
239            Type::BigInt(v) => v.opt_span(),
240            Type::BigSerial => None,
241            Type::Binary(v) => v.opt_span(),
242            Type::Bit(_, b) => b.opt_span(),
243            Type::Blob(v) => v.opt_span(),
244            Type::Boolean => None,
245            Type::Box => None,
246            Type::Bytea => None,
247            Type::Char(v) => v.opt_span(),
248            Type::Cidr => None,
249            Type::Circle => None,
250            Type::Date => None,
251            Type::DateTime(v) => v.opt_span(),
252            Type::Decimal(v) => v.opt_span(),
253            Type::Double(v) => v.opt_span(),
254            Type::Enum(v) => v.opt_span(),
255            Type::Float(v) => v.opt_span(),
256            Type::Float8 => None,
257            Type::Inet4 => None,
258            Type::Inet6 => None,
259            Type::InetAddr => None,
260            Type::Int(v) => v.opt_span(),
261            Type::Integer(v) => v.opt_span(),
262            Type::Interval(v) => v.opt_span(),
263            Type::Json => None,
264            Type::Jsonb => None,
265            Type::Line => None,
266            Type::LongBlob(v) => v.opt_span(),
267            Type::LongText(v) => v.opt_span(),
268            Type::Lseg => None,
269            Type::Macaddr => None,
270            Type::Macaddr8 => None,
271            Type::MediumBlob(v) => v.opt_span(),
272            Type::MediumInt(v) => v.opt_span(),
273            Type::MediumText(v) => v.opt_span(),
274            Type::Money => None,
275            Type::Named(v) => Some(v.span()),
276            Type::Path => None,
277            Type::Numeric(v) => v.opt_span(),
278            Type::Range(_) => None,
279            Type::MultiRange(_) => None,
280            Type::Serial => None,
281            Type::Set(v) => v.opt_span(),
282            Type::Point => None,
283            Type::Polygon => None,
284            Type::SmallInt(v) => v.opt_span(),
285            Type::SmallSerial => None,
286            Type::Table(span, _) => Some(span.clone()),
287            Type::Text(v) => v.opt_span(),
288            Type::Time(v) => v.opt_span(),
289            Type::Timestamp(v) => v.opt_span(),
290            Type::Timestamptz => None,
291            Type::Timetz(v) => v.opt_span(),
292            Type::TsQuery => None,
293            Type::TsVector => None,
294            Type::TinyBlob(v) => v.opt_span(),
295            Type::TinyInt(v) => v.opt_span(),
296            Type::TinyText(v) => v.opt_span(),
297            Type::VarBinary(v) => v.opt_span(),
298            Type::VarBit(v) => v.opt_span(),
299            Type::VarChar(v) => v.opt_span(),
300            Type::Uuid => None,
301            Type::Xml => None,
302        }
303    }
304}
305
306/// Type of data
307#[derive(Debug, Clone)]
308pub struct DataType<'a> {
309    /// Span of type_ identifier
310    pub identifier: Span,
311    /// Type with width
312    pub type_: Type<'a>,
313    /// Properties on type
314    pub properties: Vec<DataTypeProperty<'a>>,
315}
316
317impl<'a> Spanned for DataType<'a> {
318    fn span(&self) -> Span {
319        self.identifier
320            .join_span(&self.type_)
321            .join_span(&self.properties)
322    }
323}
324fn parse_width(parser: &mut Parser<'_, '_>) -> Result<Option<(usize, Span)>, ParseError> {
325    if !matches!(parser.token, Token::LParen) {
326        return Ok(None);
327    }
328    parser.consume_token(Token::LParen)?;
329    let value = parser.recovered(")", &|t| t == &Token::RParen, |parser| parser.consume_int())?;
330    parser.consume_token(Token::RParen)?;
331    Ok(Some(value))
332}
333
334fn parse_width_req(parser: &mut Parser<'_, '_>) -> Result<(usize, Span), ParseError> {
335    if !matches!(parser.token, Token::LParen) {
336        return parser.expected_failure("'('");
337    }
338    Ok(parse_width(parser)?.expect("width"))
339}
340
341fn parse_precision_scale(
342    parser: &mut Parser<'_, '_>,
343) -> Result<Option<(usize, usize, Span)>, ParseError> {
344    if !matches!(parser.token, Token::LParen) {
345        return Ok(None);
346    }
347    let left = parser.consume_token(Token::LParen)?;
348    let (precision, s1) = parser.consume_int()?;
349    let scale = if parser.skip_token(Token::Comma).is_some() {
350        let (v, _) = parser.consume_int()?;
351        v
352    } else {
353        0
354    };
355    let right = parser.consume_token(Token::RParen)?;
356    let span = left.join_span(&s1).join_span(&right);
357    Ok(Some((precision, scale, span)))
358}
359
360fn parse_enum_set_values<'a>(parser: &mut Parser<'a, '_>) -> Result<Vec<SString<'a>>, ParseError> {
361    parser.consume_token(Token::LParen)?;
362    let mut ans = Vec::new();
363    parser.recovered(")", &|t| t == &Token::RParen, |parser| {
364        loop {
365            ans.push(parser.consume_string()?);
366            match &parser.token {
367                Token::Comma => {
368                    parser.consume_token(Token::Comma)?;
369                }
370                Token::RParen => break,
371                _ => parser.expected_failure("',' or ')'")?,
372            }
373        }
374        Ok(())
375    })?;
376    parser.consume_token(Token::RParen)?;
377    Ok(ans)
378}
379
380fn parse_interval_field(
381    parser: &mut Parser<'_, '_>,
382) -> Result<Option<(IntervalField, Span)>, ParseError> {
383    let (field, kw) = match &parser.token {
384        Token::Ident(_, Keyword::YEAR) => (IntervalField::Year, Keyword::YEAR),
385        Token::Ident(_, Keyword::MONTH) => (IntervalField::Month, Keyword::MONTH),
386        Token::Ident(_, Keyword::DAY) => (IntervalField::Day, Keyword::DAY),
387        Token::Ident(_, Keyword::HOUR) => (IntervalField::Hour, Keyword::HOUR),
388        Token::Ident(_, Keyword::MINUTE) => (IntervalField::Minute, Keyword::MINUTE),
389        Token::Ident(_, Keyword::SECOND) => (IntervalField::Second, Keyword::SECOND),
390        _ => return Ok(None),
391    };
392    let span = parser.consume_keyword(kw)?;
393    Ok(Some((field, span)))
394}
395
396/// The context in which a data type is being parsed.
397///
398/// This controls which [`DataTypeProperty`] variants are syntactically accepted
399/// and enables future per-context validation.
400#[derive(Clone, Copy, PartialEq, Eq)]
401pub(crate) enum DataTypeContext {
402    /// Column definition in `CREATE TABLE`, `ALTER TABLE ADD/MODIFY COLUMN`,
403    /// or composite type attributes (`ALTER TYPE … ADD/ALTER ATTRIBUTE`).
404    /// All properties are allowed, including the `AS (expr)` generated-column syntax.
405    Column,
406    /// Function or procedure parameter type (`CREATE FUNCTION f(a INT …)`).
407    /// The `AS (expr)` generated-column syntax is not accepted here.
408    FunctionParam,
409    /// Function return type (`RETURNS …`), or a `JSON_TABLE` column path type.
410    /// The `AS (expr)` generated-column syntax is not accepted here.
411    FunctionReturn,
412    /// Type reference in an expression context: `CAST(… AS type)`, `expr::type`,
413    /// `CONVERT(expr, type)`, or operator/function argument types in `DROP`.
414    /// The `AS (expr)` generated-column syntax is not accepted here.
415    TypeRef,
416}
417
418pub(crate) fn parse_data_type<'a>(
419    parser: &mut Parser<'a, '_>,
420    ctx: DataTypeContext,
421) -> Result<DataType<'a>, ParseError> {
422    let (identifier, type_) = match &parser.token {
423        Token::Ident(_, Keyword::BOOLEAN) => {
424            (parser.consume_keyword(Keyword::BOOLEAN)?, Type::Boolean)
425        }
426        Token::Ident(_, Keyword::BOOL) => (parser.consume_keyword(Keyword::BOOL)?, Type::Boolean),
427        Token::Ident(_, Keyword::TINYINT) => (
428            parser.consume_keyword(Keyword::TINYINT)?,
429            Type::TinyInt(parse_width(parser)?),
430        ),
431        Token::Ident(_, Keyword::SMALLINT) => (
432            parser.consume_keyword(Keyword::SMALLINT)?,
433            Type::SmallInt(parse_width(parser)?),
434        ),
435        Token::Ident(_, Keyword::MEDIUMINT) => (
436            parser.consume_keyword(Keyword::MEDIUMINT)?,
437            Type::MediumInt(parse_width(parser)?),
438        ),
439        Token::Ident(_, Keyword::INTEGER) => (
440            parser.consume_keyword(Keyword::INTEGER)?,
441            Type::Integer(parse_width(parser)?),
442        ),
443        Token::Ident(_, Keyword::INT) => (
444            parser.consume_keyword(Keyword::INT)?,
445            Type::Int(parse_width(parser)?),
446        ),
447        Token::Ident(_, Keyword::BIGINT) => (
448            parser.consume_keyword(Keyword::BIGINT)?,
449            Type::BigInt(parse_width(parser)?),
450        ),
451        Token::Ident(_, Keyword::INET4) => (parser.consume_keyword(Keyword::INET4)?, Type::Inet4),
452        Token::Ident(_, Keyword::INET6) => (parser.consume_keyword(Keyword::INET6)?, Type::Inet6),
453        Token::Ident(_, Keyword::INET) if parser.options.dialect.is_postgresql() => {
454            (parser.consume_keyword(Keyword::INET)?, Type::InetAddr)
455        }
456        Token::Ident(_, Keyword::CIDR) if parser.options.dialect.is_postgresql() => {
457            (parser.consume_keyword(Keyword::CIDR)?, Type::Cidr)
458        }
459        Token::Ident(_, Keyword::MACADDR) if parser.options.dialect.is_postgresql() => {
460            (parser.consume_keyword(Keyword::MACADDR)?, Type::Macaddr)
461        }
462        Token::Ident(_, Keyword::MACADDR8) if parser.options.dialect.is_postgresql() => {
463            (parser.consume_keyword(Keyword::MACADDR8)?, Type::Macaddr8)
464        }
465        Token::Ident(_, Keyword::INT2) => {
466            (parser.consume_keyword(Keyword::INT2)?, Type::SmallInt(None))
467        }
468        Token::Ident(_, Keyword::INT4) => (parser.consume_keyword(Keyword::INT4)?, Type::Int(None)),
469        Token::Ident(_, Keyword::INT8) => {
470            (parser.consume_keyword(Keyword::INT8)?, Type::BigInt(None))
471        }
472        Token::Ident(_, Keyword::FLOAT4) => {
473            (parser.consume_keyword(Keyword::FLOAT4)?, Type::Float(None))
474        }
475        Token::Ident(_, Keyword::SERIAL) if parser.options.dialect.is_postgresql() => {
476            (parser.consume_keyword(Keyword::SERIAL)?, Type::Serial)
477        }
478        Token::Ident(_, Keyword::SMALLSERIAL) if parser.options.dialect.is_postgresql() => (
479            parser.consume_keyword(Keyword::SMALLSERIAL)?,
480            Type::SmallSerial,
481        ),
482        Token::Ident(_, Keyword::BIGSERIAL) if parser.options.dialect.is_postgresql() => {
483            (parser.consume_keyword(Keyword::BIGSERIAL)?, Type::BigSerial)
484        }
485        Token::Ident(_, Keyword::MONEY) if parser.options.dialect.is_postgresql() => {
486            (parser.consume_keyword(Keyword::MONEY)?, Type::Money)
487        }
488        Token::Ident(_, Keyword::TINYTEXT) => (
489            parser.consume_keyword(Keyword::TINYTEXT)?,
490            Type::TinyText(parse_width(parser)?),
491        ),
492        Token::Ident(_, Keyword::CHAR) => (
493            parser.consume_keyword(Keyword::CHAR)?,
494            Type::Char(parse_width(parser)?),
495        ),
496        Token::Ident(_, Keyword::CHARACTER) => {
497            let char_span = parser.consume_keyword(Keyword::CHARACTER)?;
498            if let Some(varying_span) = parser.skip_keyword(Keyword::VARYING) {
499                (
500                    char_span.join_span(&varying_span),
501                    Type::VarChar(parse_width(parser)?),
502                )
503            } else {
504                (char_span, Type::Char(parse_width(parser)?))
505            }
506        }
507        Token::Ident(_, Keyword::BPCHAR) => (
508            parser.consume_keyword(Keyword::BPCHAR)?,
509            Type::Char(parse_width(parser)?),
510        ),
511        Token::Ident(_, Keyword::NCHAR) => (
512            parser.consume_keyword(Keyword::NCHAR)?,
513            Type::Char(parse_width(parser)?),
514        ),
515        Token::Ident(_, Keyword::TEXT) => (
516            parser.consume_keyword(Keyword::TEXT)?,
517            Type::Text(parse_width(parser)?),
518        ),
519        Token::Ident(_, Keyword::MEDIUMTEXT) => (
520            parser.consume_keyword(Keyword::MEDIUMTEXT)?,
521            Type::MediumText(parse_width(parser)?),
522        ),
523        Token::Ident(_, Keyword::LONGTEXT) => (
524            parser.consume_keyword(Keyword::LONGTEXT)?,
525            Type::LongText(parse_width(parser)?),
526        ),
527        Token::Ident(_, Keyword::VARCHAR) => (
528            parser.consume_keyword(Keyword::VARCHAR)?,
529            Type::VarChar(parse_width(parser)?),
530        ),
531        Token::Ident(_, Keyword::VARCHARACTER) => (
532            parser.consume_keyword(Keyword::VARCHARACTER)?,
533            Type::VarChar(parse_width(parser)?),
534        ),
535        Token::Ident(_, Keyword::NVARCHAR) => (
536            parser.consume_keyword(Keyword::NVARCHAR)?,
537            Type::VarChar(parse_width(parser)?),
538        ),
539        Token::Ident(_, Keyword::TINYBLOB) => (
540            parser.consume_keyword(Keyword::TINYBLOB)?,
541            Type::TinyBlob(parse_width(parser)?),
542        ),
543        Token::Ident(_, Keyword::BLOB) => (
544            parser.consume_keyword(Keyword::BLOB)?,
545            Type::Blob(parse_width(parser)?),
546        ),
547        Token::Ident(_, Keyword::MEDIUMBLOB) => (
548            parser.consume_keyword(Keyword::MEDIUMBLOB)?,
549            Type::MediumBlob(parse_width(parser)?),
550        ),
551        Token::Ident(_, Keyword::LONGBLOB) => (
552            parser.consume_keyword(Keyword::LONGBLOB)?,
553            Type::LongBlob(parse_width(parser)?),
554        ),
555        Token::Ident(_, Keyword::VARBINARY) => (
556            parser.consume_keyword(Keyword::VARBINARY)?,
557            Type::VarBinary(parse_width_req(parser)?),
558        ),
559        Token::Ident(_, Keyword::BINARY) => (
560            parser.consume_keyword(Keyword::BINARY)?,
561            Type::Binary(parse_width(parser)?),
562        ),
563        Token::Ident(_, Keyword::FLOAT8) => {
564            (parser.consume_keyword(Keyword::FLOAT8)?, Type::Float8)
565        }
566        Token::Ident(_, Keyword::REAL) => {
567            let i = parser.consume_keyword(Keyword::REAL)?;
568            if parser.options.dialect.is_sqlite() {
569                (i, Type::Double(None))
570            } else {
571                (i, Type::Float(None))
572            }
573        }
574        Token::Ident(_, Keyword::FLOAT) => {
575            let i = parser.consume_keyword(Keyword::FLOAT)?;
576            (i, Type::Float(parse_precision_scale(parser)?))
577        }
578        Token::Ident(_, Keyword::DOUBLE) => {
579            let i = if parser.options.dialect.is_postgresql() {
580                parser.consume_keywords(&[Keyword::DOUBLE, Keyword::PRECISION])?
581            } else {
582                let double_span = parser.consume_keyword(Keyword::DOUBLE)?;
583                // MySQL also supports optional PRECISION keyword
584                if let Some(precision_span) = parser.skip_keyword(Keyword::PRECISION) {
585                    double_span.join_span(&precision_span)
586                } else {
587                    double_span
588                }
589            };
590            (i, Type::Double(parse_precision_scale(parser)?))
591        }
592        Token::Ident(_, Keyword::NUMERIC) => {
593            let numeric = parser.consume_keyword(Keyword::NUMERIC)?;
594            (numeric, Type::Numeric(parse_precision_scale(parser)?))
595        }
596        Token::Ident(_, Keyword::DECIMAL) => {
597            let decimal = parser.consume_keyword(Keyword::DECIMAL)?;
598            (decimal, Type::Decimal(parse_precision_scale(parser)?))
599        }
600        Token::Ident(_, Keyword::DEC) => {
601            let dec = parser.consume_keyword(Keyword::DEC)?;
602            (dec, Type::Decimal(parse_precision_scale(parser)?))
603        }
604        Token::Ident(_, Keyword::DATETIME) => (
605            parser.consume_keyword(Keyword::DATETIME)?,
606            Type::DateTime(parse_width(parser)?),
607        ),
608        Token::Ident(_, Keyword::TIMETZ) if parser.options.dialect.is_postgresql() => {
609            (parser.consume_keyword(Keyword::TIMETZ)?, Type::Timetz(None))
610        }
611        Token::Ident(_, Keyword::TIME) => {
612            let time_span = parser.consume_keyword(Keyword::TIME)?;
613            let width = parse_width(parser)?;
614            if parser.options.dialect.is_postgresql() {
615                if parser.skip_keyword(Keyword::WITH).is_some() {
616                    parser.consume_keywords(&[Keyword::TIME, Keyword::ZONE])?;
617                    (time_span, Type::Timetz(width))
618                } else if parser.skip_keyword(Keyword::WITHOUT).is_some() {
619                    parser.consume_keywords(&[Keyword::TIME, Keyword::ZONE])?;
620                    (time_span, Type::Time(width))
621                } else {
622                    (time_span, Type::Time(width))
623                }
624            } else {
625                (time_span, Type::Time(width))
626            }
627        }
628        Token::Ident(_, Keyword::TIMESTAMPTZ) => (
629            parser.consume_keyword(Keyword::TIMESTAMPTZ)?,
630            Type::Timestamptz,
631        ),
632        Token::Ident(_, Keyword::TIMESTAMP) => {
633            let timestamp_span = parser.consume_keyword(Keyword::TIMESTAMP)?;
634            let width = parse_width(parser)?;
635            let with_time_zone = if let Some(with_span) = parser.skip_keyword(Keyword::WITH) {
636                Some(
637                    with_span.join_span(&parser.consume_keywords(&[Keyword::TIME, Keyword::ZONE])?),
638                )
639            } else {
640                if parser.skip_keyword(Keyword::WITHOUT).is_some() {
641                    parser.consume_keywords(&[Keyword::TIME, Keyword::ZONE])?;
642                }
643                None
644            };
645            let timestamp = Timestamp {
646                width,
647                with_time_zone,
648            };
649            (timestamp_span, Type::Timestamp(timestamp))
650        }
651        Token::Ident(_, Keyword::DATE) => (parser.consume_keyword(Keyword::DATE)?, Type::Date),
652        Token::Ident(_, Keyword::BOX) if parser.options.dialect.is_postgresql() => {
653            (parser.consume_keyword(Keyword::BOX)?, Type::Box)
654        }
655        Token::Ident(_, Keyword::CIRCLE) if parser.options.dialect.is_postgresql() => {
656            (parser.consume_keyword(Keyword::CIRCLE)?, Type::Circle)
657        }
658        Token::Ident(_, Keyword::LINE) if parser.options.dialect.is_postgresql() => {
659            (parser.consume_keyword(Keyword::LINE)?, Type::Line)
660        }
661        Token::Ident(_, Keyword::LSEG) if parser.options.dialect.is_postgresql() => {
662            (parser.consume_keyword(Keyword::LSEG)?, Type::Lseg)
663        }
664        Token::Ident(_, Keyword::PATH) if parser.options.dialect.is_postgresql() => {
665            (parser.consume_keyword(Keyword::PATH)?, Type::Path)
666        }
667        Token::Ident(_, Keyword::POINT) if parser.options.dialect.is_postgresql() => {
668            (parser.consume_keyword(Keyword::POINT)?, Type::Point)
669        }
670        Token::Ident(_, Keyword::POLYGON) if parser.options.dialect.is_postgresql() => {
671            (parser.consume_keyword(Keyword::POLYGON)?, Type::Polygon)
672        }
673        Token::Ident(_, Keyword::INTERVAL) if parser.options.dialect.is_postgresql() => {
674            let interval_span = parser.consume_keyword(Keyword::INTERVAL)?;
675            let start_field = parse_interval_field(parser)?;
676            let end_field = if start_field.is_some() && parser.skip_keyword(Keyword::TO).is_some() {
677                parse_interval_field(parser)?
678            } else {
679                None
680            };
681            let precision = parse_width(parser)?;
682            (
683                interval_span,
684                Type::Interval(Interval {
685                    start_field,
686                    end_field,
687                    precision,
688                }),
689            )
690        }
691        Token::Ident(_, Keyword::ENUM) => (
692            parser.consume_keyword(Keyword::ENUM)?,
693            Type::Enum(parse_enum_set_values(parser)?),
694        ),
695        Token::Ident(_, Keyword::SET) => (
696            parser.consume_keyword(Keyword::SET)?,
697            Type::Set(parse_enum_set_values(parser)?),
698        ),
699        Token::Ident(_, Keyword::JSON) => (parser.consume_keyword(Keyword::JSON)?, Type::Json),
700        Token::Ident(_, Keyword::JSONB) if parser.options.dialect.is_postgresql() => {
701            (parser.consume_keyword(Keyword::JSONB)?, Type::Jsonb)
702        }
703        Token::Ident(_, Keyword::BYTEA) => (parser.consume_keyword(Keyword::BYTEA)?, Type::Bytea),
704        Token::Ident(_, Keyword::INT4RANGE) if parser.options.dialect.is_postgresql() => (
705            parser.consume_keyword(Keyword::INT4RANGE)?,
706            Type::Range(RangeSubtype::Int4),
707        ),
708        Token::Ident(_, Keyword::INT8RANGE) if parser.options.dialect.is_postgresql() => (
709            parser.consume_keyword(Keyword::INT8RANGE)?,
710            Type::Range(RangeSubtype::Int8),
711        ),
712        Token::Ident(_, Keyword::NUMRANGE) if parser.options.dialect.is_postgresql() => (
713            parser.consume_keyword(Keyword::NUMRANGE)?,
714            Type::Range(RangeSubtype::Num),
715        ),
716        Token::Ident(_, Keyword::TSRANGE) if parser.options.dialect.is_postgresql() => (
717            parser.consume_keyword(Keyword::TSRANGE)?,
718            Type::Range(RangeSubtype::Ts),
719        ),
720        Token::Ident(_, Keyword::TSTZRANGE) if parser.options.dialect.is_postgresql() => (
721            parser.consume_keyword(Keyword::TSTZRANGE)?,
722            Type::Range(RangeSubtype::Tstz),
723        ),
724        Token::Ident(_, Keyword::DATERANGE) if parser.options.dialect.is_postgresql() => (
725            parser.consume_keyword(Keyword::DATERANGE)?,
726            Type::Range(RangeSubtype::Date),
727        ),
728        Token::Ident(_, Keyword::INT4MULTIRANGE) if parser.options.dialect.is_postgresql() => (
729            parser.consume_keyword(Keyword::INT4MULTIRANGE)?,
730            Type::MultiRange(RangeSubtype::Int4),
731        ),
732        Token::Ident(_, Keyword::INT8MULTIRANGE) if parser.options.dialect.is_postgresql() => (
733            parser.consume_keyword(Keyword::INT8MULTIRANGE)?,
734            Type::MultiRange(RangeSubtype::Int8),
735        ),
736        Token::Ident(_, Keyword::NUMMULTIRANGE) if parser.options.dialect.is_postgresql() => (
737            parser.consume_keyword(Keyword::NUMMULTIRANGE)?,
738            Type::MultiRange(RangeSubtype::Num),
739        ),
740        Token::Ident(_, Keyword::TSMULTIRANGE) if parser.options.dialect.is_postgresql() => (
741            parser.consume_keyword(Keyword::TSMULTIRANGE)?,
742            Type::MultiRange(RangeSubtype::Ts),
743        ),
744        Token::Ident(_, Keyword::TSTZMULTIRANGE) if parser.options.dialect.is_postgresql() => (
745            parser.consume_keyword(Keyword::TSTZMULTIRANGE)?,
746            Type::MultiRange(RangeSubtype::Tstz),
747        ),
748        Token::Ident(_, Keyword::DATEMULTIRANGE) if parser.options.dialect.is_postgresql() => (
749            parser.consume_keyword(Keyword::DATEMULTIRANGE)?,
750            Type::MultiRange(RangeSubtype::Date),
751        ),
752        Token::Ident(_, Keyword::UUID) if parser.options.dialect.is_postgresql() => {
753            (parser.consume_keyword(Keyword::UUID)?, Type::Uuid)
754        }
755        Token::Ident(_, Keyword::XML) if parser.options.dialect.is_postgresql() => {
756            (parser.consume_keyword(Keyword::XML)?, Type::Xml)
757        }
758        Token::Ident(_, Keyword::TSQUERY) if parser.options.dialect.is_postgresql() => {
759            (parser.consume_keyword(Keyword::TSQUERY)?, Type::TsQuery)
760        }
761        Token::Ident(_, Keyword::TSVECTOR) if parser.options.dialect.is_postgresql() => {
762            (parser.consume_keyword(Keyword::TSVECTOR)?, Type::TsVector)
763        }
764        Token::Ident(_, Keyword::BIT) => {
765            let t = parser.consume_keyword(Keyword::BIT)?;
766            if parser.options.dialect.is_postgresql() {
767                if parser.skip_keyword(Keyword::VARYING).is_some() {
768                    (t, Type::VarBit(parse_width(parser)?))
769                } else {
770                    let width = parse_width(parser)?;
771                    let (w, ws) = width.unwrap_or((1, t.clone()));
772                    (t, Type::Bit(w, ws))
773                }
774            } else {
775                let (w, ws) = parse_width_req(parser)?;
776                (t, Type::Bit(w, ws))
777            }
778        }
779        Token::Ident(_, Keyword::VARBIT) => {
780            let t = parser.consume_keyword(Keyword::VARBIT)?;
781            parser.postgres_only(&t);
782            (t, Type::VarBit(parse_width(parser)?))
783        }
784        Token::Ident(_, Keyword::TABLE)
785            if parser.options.dialect.is_postgresql() && ctx == DataTypeContext::FunctionReturn =>
786        {
787            let table_span = parser.consume_keyword(Keyword::TABLE)?;
788            let lparen = parser.consume_token(Token::LParen)?;
789            let mut columns = Vec::new();
790            parser.recovered(")", &|t| t == &Token::RParen, |parser| {
791                loop {
792                    let name = parser.consume_plain_identifier_unreserved()?;
793                    let col_type = parse_data_type(parser, DataTypeContext::FunctionParam)?;
794                    columns.push((name, col_type));
795                    if parser.skip_token(Token::Comma).is_none() {
796                        break;
797                    }
798                }
799                Ok(())
800            })?;
801            let rparen = parser.consume_token(Token::RParen)?;
802            let paren_span = lparen.join_span(&rparen);
803            (table_span, Type::Table(paren_span, columns))
804        }
805        Token::String(_, _) | Token::Ident(_, _) if parser.options.dialect.is_postgresql() => {
806            let qname = parse_qualified_name_unreserved(parser)?;
807            let span = qname.span();
808            (span, Type::Named(qname))
809        }
810        _ => parser.expected_failure("type")?,
811    };
812
813    // Check for PostgreSQL array type syntax: TYPE[] or TYPE[][] etc.
814    let (identifier, type_) = {
815        let mut identifier = identifier;
816        let mut type_ = type_;
817        while parser.options.dialect.is_postgresql() && matches!(parser.token, Token::LBracket) {
818            let lbracket = parser.consume_token(Token::LBracket)?;
819            let rbracket = parser.consume_token(Token::RBracket)?;
820            let array_span = lbracket.join_span(&rbracket);
821            identifier = identifier.join_span(&array_span);
822            type_ = Type::Array(Box::new(type_), array_span);
823        }
824        (identifier, type_)
825    };
826
827    let mut properties = Vec::new();
828    loop {
829        // Each arm tuple is (&parser.token, ctx).  The wildcard arm stops
830        // property parsing for both "unknown token" and "property not allowed
831        // in this context", so callers never silently swallow tokens.
832        use DataTypeContext::*;
833        match (&parser.token, ctx) {
834            // ── Properties valid in column definitions and function signatures ──
835            (Token::Ident(_, Keyword::SIGNED), Column | FunctionParam | FunctionReturn) => {
836                properties.push(DataTypeProperty::Signed(
837                    parser.consume_keyword(Keyword::SIGNED)?,
838                ));
839            }
840            (Token::Ident(_, Keyword::UNSIGNED), Column | FunctionParam | FunctionReturn) => {
841                properties.push(DataTypeProperty::Unsigned(
842                    parser.consume_keyword(Keyword::UNSIGNED)?,
843                ));
844            }
845            (Token::Ident(_, Keyword::ZEROFILL), Column | FunctionParam | FunctionReturn) => {
846                properties.push(DataTypeProperty::Zerofill(
847                    parser.consume_keyword(Keyword::ZEROFILL)?,
848                ));
849            }
850            (
851                Token::Ident(_, Keyword::CHARACTER),
852                Column | FunctionParam | FunctionReturn | TypeRef,
853            ) => {
854                parser.consume_keywords(&[Keyword::CHARACTER, Keyword::SET])?;
855                properties.push(DataTypeProperty::Charset(
856                    parser.consume_plain_identifier_unreserved()?,
857                ));
858            }
859            (
860                Token::Ident(_, Keyword::CHARSET),
861                Column | FunctionParam | FunctionReturn | TypeRef,
862            ) => {
863                parser.consume_keyword(Keyword::CHARSET)?;
864                properties.push(DataTypeProperty::Charset(
865                    parser.consume_plain_identifier_unreserved()?,
866                ));
867            }
868            (
869                Token::Ident(_, Keyword::COLLATE),
870                Column | FunctionParam | FunctionReturn | TypeRef,
871            ) => {
872                parser.consume_keyword(Keyword::COLLATE)?;
873                properties.push(DataTypeProperty::Collate(
874                    parser.consume_plain_identifier_unreserved()?,
875                ));
876            }
877
878            // ── Column-only properties ──
879            (Token::Ident(_, Keyword::NULL), Column) => {
880                properties.push(DataTypeProperty::Null(
881                    parser.consume_keyword(Keyword::NULL)?,
882                ));
883            }
884            (Token::Ident(_, Keyword::NOT), Column) => {
885                let start = parser.consume_keyword(Keyword::NOT)?.start;
886                properties.push(DataTypeProperty::NotNull(
887                    start..parser.consume_keyword(Keyword::NULL)?.end,
888                ));
889            }
890            (Token::Ident(_, Keyword::COMMENT), Column) => {
891                parser.consume_keyword(Keyword::COMMENT)?;
892                properties.push(DataTypeProperty::Comment(parser.consume_string()?));
893            }
894            (Token::Ident(_, Keyword::DEFAULT), Column) => {
895                parser.consume_keyword(Keyword::DEFAULT)?;
896                properties.push(DataTypeProperty::Default(parse_expression_unreserved(
897                    parser,
898                    PRIORITY_MAX,
899                )?));
900            }
901            (Token::Ident(_, Keyword::AUTO_INCREMENT), Column) => {
902                properties.push(DataTypeProperty::AutoIncrement(
903                    parser.consume_keyword(Keyword::AUTO_INCREMENT)?,
904                ));
905            }
906            (Token::Ident(_, Keyword::VIRTUAL), Column) => {
907                properties.push(DataTypeProperty::Virtual(
908                    parser.consume_keyword(Keyword::VIRTUAL)?,
909                ));
910            }
911            (Token::Ident(_, Keyword::PERSISTENT), Column) => {
912                properties.push(DataTypeProperty::Persistent(
913                    parser.consume_keyword(Keyword::PERSISTENT)?,
914                ));
915            }
916            (Token::Ident(_, Keyword::STORED), Column) => {
917                properties.push(DataTypeProperty::Stored(
918                    parser.consume_keyword(Keyword::STORED)?,
919                ));
920            }
921            (Token::Ident(_, Keyword::UNIQUE), Column) => {
922                let span = parser.consume_keyword(Keyword::UNIQUE)?;
923                if let Some(s2) = parser.skip_keyword(Keyword::KEY) {
924                    properties.push(DataTypeProperty::UniqueKey(s2.join_span(&span)));
925                } else {
926                    properties.push(DataTypeProperty::Unique(span));
927                }
928            }
929            (Token::Ident(_, Keyword::GENERATED), Column) => {
930                if parser.options.dialect.is_postgresql() {
931                    let generated_always_as_span = parser.consume_keywords(&[
932                        Keyword::GENERATED,
933                        Keyword::ALWAYS,
934                        Keyword::AS,
935                    ])?;
936                    if matches!(parser.token, Token::LParen) {
937                        // GENERATED ALWAYS AS (expr) STORED - computed column
938                        let l_paren = parser.consume_token(Token::LParen)?;
939                        let e = parser.recovered(")", &|t| t == &Token::RParen, |parser| {
940                            Ok(Some(parse_expression_unreserved(parser, PRIORITY_MAX)?))
941                        })?;
942                        let r_paren = parser.consume_token(Token::RParen)?;
943                        let expr = e.unwrap_or_else(|| {
944                            Expression::Invalid(Box::new(InvalidExpression {
945                                span: l_paren.join_span(&r_paren),
946                            }))
947                        });
948                        let stored_span = parser.skip_keyword(Keyword::STORED);
949                        properties.push(DataTypeProperty::GeneratedAlwaysAsExpr {
950                            generated_always_as_span,
951                            expr,
952                            stored_span,
953                        });
954                    } else {
955                        let identity_span = parser.consume_keyword(Keyword::IDENTITY)?;
956                        // Parse optional sequence options in parentheses
957                        if parser.skip_token(Token::LParen).is_some() {
958                            let _ = parse_sequence_options(parser);
959                            parser.consume_token(Token::RParen)?;
960                        }
961                        properties.push(DataTypeProperty::GeneratedAlways(
962                            generated_always_as_span.join_span(&identity_span),
963                        ));
964                    }
965                } else {
966                    properties.push(DataTypeProperty::GeneratedAlways(
967                        parser.consume_keywords(&[Keyword::GENERATED, Keyword::ALWAYS])?,
968                    ));
969                }
970            }
971            (Token::Ident(_, Keyword::AS), Column) => {
972                let span = parser.consume_keyword(Keyword::AS)?;
973                let s1 = parser.consume_token(Token::LParen)?;
974                let e = parser.recovered(")", &|t| t == &Token::RParen, |parser| {
975                    Ok(Some(parse_expression_unreserved(parser, PRIORITY_MAX)?))
976                })?;
977                let s2 = parser.consume_token(Token::RParen)?;
978                let e = e.unwrap_or_else(|| {
979                    Expression::Invalid(Box::new(InvalidExpression {
980                        span: s1.join_span(&s2),
981                    }))
982                });
983                properties.push(DataTypeProperty::As((span, e)));
984            }
985            (Token::Ident(_, Keyword::PRIMARY), Column) => {
986                properties.push(DataTypeProperty::PrimaryKey(
987                    parser.consume_keywords(&[Keyword::PRIMARY, Keyword::KEY])?,
988                ));
989            }
990            (Token::Ident(_, Keyword::CHECK), Column) => {
991                let span = parser.consume_keyword(Keyword::CHECK)?;
992                let s1 = parser.consume_token(Token::LParen)?;
993                let e = parser.recovered(")", &|t| t == &Token::RParen, |parser| {
994                    Ok(Some(parse_expression_unreserved(parser, PRIORITY_MAX)?))
995                })?;
996                let s2 = parser.consume_token(Token::RParen)?;
997                let e = e.unwrap_or_else(|| {
998                    Expression::Invalid(Box::new(InvalidExpression {
999                        span: s1.join_span(&s2),
1000                    }))
1001                });
1002                properties.push(DataTypeProperty::Check((span, e)));
1003            }
1004            (Token::Ident(_, Keyword::ON), Column) => {
1005                let span = parser.consume_keywords(&[Keyword::ON, Keyword::UPDATE])?;
1006                let expr = parse_expression_unreserved(parser, PRIORITY_MAX)?;
1007                properties.push(DataTypeProperty::OnUpdate((span, expr)));
1008            }
1009            (Token::Ident(_, Keyword::REFERENCES), Column) => {
1010                let span = parser.consume_keyword(Keyword::REFERENCES)?;
1011                let table = parser.consume_plain_identifier_unreserved()?;
1012                let mut columns = Vec::new();
1013                if matches!(parser.token, Token::LParen) {
1014                    parser.consume_token(Token::LParen)?;
1015                    loop {
1016                        columns.push(parser.consume_plain_identifier_unreserved()?);
1017                        if parser.skip_token(Token::Comma).is_none() {
1018                            break;
1019                        }
1020                    }
1021                    parser.consume_token(Token::RParen)?;
1022                }
1023                let match_type = if parser.skip_keyword(Keyword::MATCH).is_some() {
1024                    match &parser.token {
1025                        Token::Ident(_, Keyword::FULL) => {
1026                            Some(ForeignKeyMatch::Full(parser.consume()))
1027                        }
1028                        Token::Ident(_, Keyword::SIMPLE) => {
1029                            Some(ForeignKeyMatch::Simple(parser.consume()))
1030                        }
1031                        Token::Ident(_, Keyword::PARTIAL) => {
1032                            Some(ForeignKeyMatch::Partial(parser.consume()))
1033                        }
1034                        _ => None,
1035                    }
1036                } else {
1037                    None
1038                };
1039                let mut ons = Vec::new();
1040                while parser.skip_keyword(Keyword::ON).is_some() {
1041                    let on_type = match &parser.token {
1042                        Token::Ident(_, Keyword::UPDATE) => {
1043                            ForeignKeyOnType::Update(parser.consume_keyword(Keyword::UPDATE)?)
1044                        }
1045                        Token::Ident(_, Keyword::DELETE) => {
1046                            ForeignKeyOnType::Delete(parser.consume_keyword(Keyword::DELETE)?)
1047                        }
1048                        _ => parser.expected_failure("UPDATE or DELETE")?,
1049                    };
1050                    let on_action = match &parser.token {
1051                        Token::Ident(_, Keyword::CASCADE) => {
1052                            ForeignKeyOnAction::Cascade(parser.consume_keyword(Keyword::CASCADE)?)
1053                        }
1054                        Token::Ident(_, Keyword::RESTRICT) => {
1055                            ForeignKeyOnAction::Restrict(parser.consume_keyword(Keyword::RESTRICT)?)
1056                        }
1057                        Token::Ident(_, Keyword::SET) => {
1058                            let set_span = parser.consume_keyword(Keyword::SET)?;
1059                            if parser.skip_keyword(Keyword::NULL).is_some() {
1060                                ForeignKeyOnAction::SetNull(set_span)
1061                            } else if parser.skip_keyword(Keyword::DEFAULT).is_some() {
1062                                ForeignKeyOnAction::SetDefault(set_span)
1063                            } else {
1064                                parser.expected_failure("NULL or DEFAULT after SET")?
1065                            }
1066                        }
1067                        Token::Ident(_, Keyword::NO) => {
1068                            let no_span = parser.consume_keyword(Keyword::NO)?;
1069                            parser.consume_keyword(Keyword::ACTION)?;
1070                            ForeignKeyOnAction::NoAction(no_span)
1071                        }
1072                        _ => parser.expected_failure(
1073                            "CASCADE, RESTRICT, SET NULL, SET DEFAULT, or NO ACTION",
1074                        )?,
1075                    };
1076                    ons.push(ForeignKeyOn {
1077                        type_: on_type,
1078                        action: on_action,
1079                    });
1080                }
1081                properties.push(DataTypeProperty::References {
1082                    span,
1083                    table,
1084                    columns,
1085                    match_type,
1086                    ons,
1087                });
1088            }
1089
1090            // End of properties (or property not valid in this context)
1091            _ => break,
1092        }
1093    }
1094    Ok(DataType {
1095        identifier,
1096        type_,
1097        properties,
1098    })
1099}