Skip to main content

qusql_parse/
create_table.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 crate::{
14    DataType, Expression, Identifier, QualifiedName, SString, Span, Spanned, Statement,
15    alter_table::{
16        ForeignKeyMatch, ForeignKeyOn, ForeignKeyOnAction, ForeignKeyOnType, IndexCol, IndexOption,
17        IndexType, parse_index_cols, parse_index_options, parse_index_type,
18    },
19    create_option::CreateOption,
20    data_type::{DataTypeContext, parse_data_type},
21    expression::{PRIORITY_MAX, parse_expression_unreserved},
22    keywords::{Keyword, Restrict},
23    lexer::{StringType, Token},
24    parser::{ParseError, Parser},
25    qualified_name::parse_qualified_name_unreserved,
26    statement::parse_compound_query,
27};
28use alloc::boxed::Box;
29use alloc::vec::Vec;
30
31/// Action for ON COMMIT clause on temporary tables
32#[derive(Clone, Debug)]
33pub enum OnCommitAction {
34    PreserveRows(Span),
35    DeleteRows(Span),
36    Drop(Span),
37}
38
39impl Spanned for OnCommitAction {
40    fn span(&self) -> Span {
41        match self {
42            OnCommitAction::PreserveRows(s) => s.span(),
43            OnCommitAction::DeleteRows(s) => s.span(),
44            OnCommitAction::Drop(s) => s.span(),
45        }
46    }
47}
48
49/// Options on created table
50#[derive(Clone, Debug)]
51pub enum TableOption<'a> {
52    AutoExtendSize {
53        identifier: Span,
54        value: (usize, Span),
55    },
56    AutoIncrement {
57        identifier: Span,
58        value: (u64, Span),
59    },
60    AvgRowLength {
61        identifier: Span,
62        value: (usize, Span),
63    },
64    CharSet {
65        identifier: Span,
66        value: Identifier<'a>,
67    },
68    DefaultCharSet {
69        identifier: Span,
70        value: Identifier<'a>,
71    },
72    Checksum {
73        identifier: Span,
74        value: (bool, Span),
75    },
76    Collate {
77        identifier: Span,
78        value: Identifier<'a>,
79    },
80    DefaultCollate {
81        identifier: Span,
82        value: Identifier<'a>,
83    },
84    Comment {
85        identifier: Span,
86        value: SString<'a>,
87    },
88    Compression {
89        identifier: Span,
90        value: SString<'a>,
91    },
92    Connection {
93        identifier: Span,
94        value: SString<'a>,
95    },
96    DataDirectory {
97        identifier: Span,
98        value: SString<'a>,
99    },
100    IndexDirectory {
101        identifier: Span,
102        value: SString<'a>,
103    },
104    DelayKeyWrite {
105        identifier: Span,
106        value: (bool, Span),
107    },
108    Encryption {
109        identifier: Span,
110        value: (bool, Span),
111    },
112    Engine {
113        identifier: Span,
114        value: Identifier<'a>,
115    },
116    EngineAttribute {
117        identifier: Span,
118        value: SString<'a>,
119    },
120    InsertMethod {
121        identifier: Span,
122        value: Identifier<'a>,
123    },
124    KeyBlockSize {
125        identifier: Span,
126        value: (usize, Span),
127    },
128    MaxRows {
129        identifier: Span,
130        value: (usize, Span),
131    },
132    MinRows {
133        identifier: Span,
134        value: (usize, Span),
135    },
136    PackKeys {
137        identifier: Span,
138        value: (usize, Span),
139    },
140    Password {
141        identifier: Span,
142        value: SString<'a>,
143    },
144    RowFormat {
145        identifier: Span,
146        value: Identifier<'a>,
147    },
148    SecondaryEngineAttribute {
149        identifier: Span,
150        value: SString<'a>,
151    },
152    StartTransaction {
153        identifier: Span,
154    },
155    StatsAutoRecalc {
156        identifier: Span,
157        value: (usize, Span),
158    },
159    StatsPersistent {
160        identifier: Span,
161        value: (usize, Span),
162    },
163    StatsSamplePages {
164        identifier: Span,
165        value: (usize, Span),
166    },
167    Storage {
168        identifier: Span,
169        value: Identifier<'a>,
170    },
171    Strict {
172        identifier: Span,
173    },
174    Tablespace {
175        identifier: Span,
176        value: Identifier<'a>,
177    },
178    Union {
179        identifier: Span,
180        value: Vec<Identifier<'a>>,
181    },
182    Inherits {
183        identifier: Span,
184        value: Vec<QualifiedName<'a>>,
185    },
186    /// PostgreSQL WITH (storage_parameter = value, ...) table options
187    WithOptions {
188        identifier: Span,
189        options: Vec<(Identifier<'a>, Expression<'a>)>,
190    },
191    /// PostgreSQL ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP }
192    OnCommit {
193        identifier: Span,
194        action: OnCommitAction,
195    },
196}
197
198impl<'a> Spanned for TableOption<'a> {
199    fn span(&self) -> Span {
200        match &self {
201            TableOption::AutoExtendSize { identifier, value } => identifier.span().join_span(value),
202            TableOption::AutoIncrement { identifier, value } => identifier.span().join_span(value),
203            TableOption::AvgRowLength { identifier, value } => identifier.span().join_span(value),
204            TableOption::CharSet { identifier, value } => identifier.span().join_span(value),
205            TableOption::DefaultCharSet { identifier, value } => identifier.span().join_span(value),
206            TableOption::Checksum { identifier, value } => identifier.span().join_span(value),
207            TableOption::Collate { identifier, value } => identifier.span().join_span(value),
208            TableOption::DefaultCollate { identifier, value } => identifier.span().join_span(value),
209            TableOption::Comment { identifier, value } => identifier.span().join_span(value),
210            TableOption::Compression { identifier, value } => identifier.span().join_span(value),
211            TableOption::Connection { identifier, value } => identifier.span().join_span(value),
212            TableOption::DataDirectory { identifier, value } => identifier.span().join_span(value),
213            TableOption::IndexDirectory { identifier, value } => identifier.span().join_span(value),
214            TableOption::DelayKeyWrite { identifier, value } => identifier.span().join_span(value),
215            TableOption::Encryption { identifier, value } => identifier.span().join_span(value),
216            TableOption::Engine { identifier, value } => identifier.span().join_span(value),
217            TableOption::EngineAttribute { identifier, value } => {
218                identifier.span().join_span(value)
219            }
220            TableOption::InsertMethod { identifier, value } => identifier.span().join_span(value),
221            TableOption::KeyBlockSize { identifier, value } => identifier.span().join_span(value),
222            TableOption::MaxRows { identifier, value } => identifier.span().join_span(value),
223            TableOption::MinRows { identifier, value } => identifier.span().join_span(value),
224            TableOption::PackKeys { identifier, value } => identifier.span().join_span(value),
225            TableOption::Password { identifier, value } => identifier.span().join_span(value),
226            TableOption::RowFormat { identifier, value } => identifier.span().join_span(value),
227            TableOption::SecondaryEngineAttribute { identifier, value } => {
228                identifier.span().join_span(value)
229            }
230            TableOption::StartTransaction { identifier } => identifier.span(),
231            TableOption::StatsAutoRecalc { identifier, value } => {
232                identifier.span().join_span(value)
233            }
234            TableOption::StatsPersistent { identifier, value } => {
235                identifier.span().join_span(value)
236            }
237            TableOption::StatsSamplePages { identifier, value } => {
238                identifier.span().join_span(value)
239            }
240            TableOption::Storage { identifier, value } => identifier.span().join_span(value),
241            TableOption::Strict { identifier } => identifier.span(),
242            TableOption::Tablespace { identifier, value } => identifier.span().join_span(value),
243            TableOption::Union { identifier, value } => {
244                if let Some(last) = value.last() {
245                    identifier.span().join_span(last)
246                } else {
247                    identifier.span()
248                }
249            }
250            TableOption::Inherits { identifier, value } => {
251                if let Some(last) = value.last() {
252                    identifier.span().join_span(last)
253                } else {
254                    identifier.span()
255                }
256            }
257            TableOption::WithOptions {
258                identifier,
259                options,
260            } => {
261                if let Some((_, last)) = options.last() {
262                    identifier.span().join_span(last)
263                } else {
264                    identifier.span()
265                }
266            }
267            TableOption::OnCommit { identifier, action } => identifier.span().join_span(action),
268        }
269    }
270}
271
272/// Definition in create table
273#[derive(Clone, Debug)]
274pub enum CreateDefinition<'a> {
275    ColumnDefinition {
276        /// Name of column
277        identifier: Identifier<'a>,
278        /// Datatype and options for column
279        data_type: DataType<'a>,
280    },
281    /// Index definition (PRIMARY KEY, UNIQUE, INDEX, KEY, FULLTEXT, SPATIAL)
282    IndexDefinition {
283        /// Optional "CONSTRAINT" span
284        constraint_span: Option<Span>,
285        /// Optional constraint symbol
286        constraint_symbol: Option<Identifier<'a>>,
287        /// The type of index
288        index_type: IndexType,
289        /// Optional index name
290        index_name: Option<Identifier<'a>>,
291        /// Columns in the index
292        cols: Vec<IndexCol<'a>>,
293        /// Index options
294        index_options: Vec<IndexOption<'a>>,
295    },
296    /// Foreign key definition
297    ForeignKeyDefinition {
298        /// Optional "CONSTRAINT" span
299        constraint_span: Option<Span>,
300        /// Optional constraint symbol
301        constraint_symbol: Option<Identifier<'a>>,
302        /// Span of "FOREIGN KEY"
303        foreign_key_span: Span,
304        /// Optional index name
305        index_name: Option<Identifier<'a>>,
306        /// Columns in this table
307        cols: Vec<IndexCol<'a>>,
308        /// Span of "REFERENCES"
309        references_span: Span,
310        /// Referenced table name
311        references_table: Identifier<'a>,
312        /// Referenced columns
313        references_cols: Vec<Identifier<'a>>,
314        /// Optional MATCH FULL / MATCH SIMPLE / MATCH PARTIAL
315        match_type: Option<ForeignKeyMatch>,
316        /// ON UPDATE/DELETE actions
317        ons: Vec<ForeignKeyOn>,
318    },
319    /// Check constraint definition
320    CheckConstraintDefinition {
321        /// Optional "CONSTRAINT" span
322        constraint_span: Option<Span>,
323        /// Optional constraint symbol
324        constraint_symbol: Option<Identifier<'a>>,
325        /// Span of "CHECK"
326        check_span: Span,
327        /// Check expression
328        expression: Expression<'a>,
329        /// Optional ENFORCED/NOT ENFORCED
330        enforced: Option<(bool, Span)>,
331    },
332    /// PostgreSQL LIKE table definition: copies column definitions from another table
333    LikeTable {
334        /// Span of "LIKE"
335        like_span: Span,
336        /// Source table name
337        source_table: QualifiedName<'a>,
338    },
339}
340
341impl<'a> Spanned for CreateDefinition<'a> {
342    fn span(&self) -> Span {
343        match &self {
344            CreateDefinition::ColumnDefinition {
345                identifier,
346                data_type,
347            } => identifier.span().join_span(data_type),
348            CreateDefinition::IndexDefinition {
349                constraint_span,
350                constraint_symbol,
351                index_type,
352                index_name,
353                cols,
354                index_options,
355            } => index_type
356                .span()
357                .join_span(constraint_span)
358                .join_span(constraint_symbol)
359                .join_span(index_name)
360                .join_span(cols)
361                .join_span(index_options),
362            CreateDefinition::ForeignKeyDefinition {
363                constraint_span,
364                constraint_symbol,
365                foreign_key_span,
366                index_name,
367                cols,
368                references_span,
369                references_table,
370                references_cols,
371                match_type,
372                ons,
373            } => foreign_key_span
374                .span()
375                .join_span(constraint_span)
376                .join_span(constraint_symbol)
377                .join_span(index_name)
378                .join_span(cols)
379                .join_span(references_span)
380                .join_span(references_table)
381                .join_span(references_cols)
382                .join_span(match_type)
383                .join_span(ons),
384            CreateDefinition::CheckConstraintDefinition {
385                constraint_span,
386                constraint_symbol,
387                check_span,
388                expression,
389                enforced,
390            } => check_span
391                .span()
392                .join_span(constraint_span)
393                .join_span(constraint_symbol)
394                .join_span(expression)
395                .join_span(enforced),
396            CreateDefinition::LikeTable {
397                like_span,
398                source_table,
399            } => like_span.join_span(source_table),
400        }
401    }
402}
403
404#[derive(Clone, Debug)]
405pub struct CreateTableAs<'a> {
406    pub ignore_span: Option<Span>,
407    pub replace_span: Option<Span>,
408    pub as_span: Span,
409    pub query: Statement<'a>,
410}
411
412impl Spanned for CreateTableAs<'_> {
413    fn span(&self) -> Span {
414        self.as_span
415            .join_span(&self.replace_span)
416            .join_span(&self.ignore_span)
417            .join_span(&self.query)
418    }
419}
420
421/// The partitioning method for PARTITION BY
422#[derive(Clone, Debug)]
423pub enum PartitionMethod {
424    Range(Span),
425    List(Span),
426    Hash(Span),
427}
428
429impl Spanned for PartitionMethod {
430    fn span(&self) -> Span {
431        match self {
432            PartitionMethod::Range(s) => s.span(),
433            PartitionMethod::List(s) => s.span(),
434            PartitionMethod::Hash(s) => s.span(),
435        }
436    }
437}
438
439/// PARTITION BY clause, appended to a CREATE TABLE statement
440#[derive(Clone, Debug)]
441pub struct PartitionBy<'a> {
442    /// Span of "PARTITION BY"
443    pub partition_by_span: Span,
444    /// The partitioning method: RANGE, LIST, or HASH
445    pub method: PartitionMethod,
446    /// The partition key expressions
447    pub keys: Vec<Expression<'a>>,
448}
449
450impl<'a> Spanned for PartitionBy<'a> {
451    fn span(&self) -> Span {
452        self.partition_by_span
453            .join_span(&self.method)
454            .join_span(&self.keys)
455    }
456}
457
458/// A single value in a partition bound specification: an expression, MINVALUE, or MAXVALUE
459#[derive(Clone, Debug)]
460pub enum PartitionBoundExpr<'a> {
461    Expr(Expression<'a>),
462    MinValue(Span),
463    MaxValue(Span),
464}
465
466impl<'a> Spanned for PartitionBoundExpr<'a> {
467    fn span(&self) -> Span {
468        match self {
469            PartitionBoundExpr::Expr(e) => e.span(),
470            PartitionBoundExpr::MinValue(s) => s.span(),
471            PartitionBoundExpr::MaxValue(s) => s.span(),
472        }
473    }
474}
475
476/// The partition bound specification for CREATE TABLE ... PARTITION OF
477#[derive(Clone, Debug)]
478pub enum PartitionBoundSpec<'a> {
479    /// FOR VALUES IN (expr [, ...]) — list partitioning
480    In {
481        in_span: Span,
482        values: Vec<PartitionBoundExpr<'a>>,
483    },
484    /// FOR VALUES FROM (...) TO (...) — range partitioning
485    FromTo {
486        from_span: Span,
487        from_values: Vec<PartitionBoundExpr<'a>>,
488        to_span: Span,
489        to_values: Vec<PartitionBoundExpr<'a>>,
490    },
491    /// FOR VALUES WITH (MODULUS n, REMAINDER r) — hash partitioning
492    WithModulusRemainder {
493        with_span: Span,
494        modulus_span: Span,
495        modulus: (u64, Span),
496        remainder_span: Span,
497        remainder: (u64, Span),
498    },
499}
500
501impl<'a> Spanned for PartitionBoundSpec<'a> {
502    fn span(&self) -> Span {
503        match self {
504            PartitionBoundSpec::In { in_span, values } => in_span.join_span(values),
505            PartitionBoundSpec::FromTo {
506                from_span,
507                from_values: _,
508                to_span,
509                to_values,
510            } => from_span.join_span(to_span).join_span(to_values),
511            PartitionBoundSpec::WithModulusRemainder {
512                with_span,
513                modulus_span,
514                modulus,
515                remainder_span,
516                remainder,
517            } => with_span
518                .join_span(modulus_span)
519                .join_span(modulus)
520                .join_span(remainder_span)
521                .join_span(remainder),
522        }
523    }
524}
525
526/// The bound clause of a PARTITION OF statement
527#[derive(Clone, Debug)]
528pub enum PartitionOfBound<'a> {
529    /// FOR VALUES partition_bound_spec
530    ForValues {
531        for_values_span: Span,
532        spec: PartitionBoundSpec<'a>,
533    },
534    /// DEFAULT partition
535    Default(Span),
536}
537
538impl<'a> Spanned for PartitionOfBound<'a> {
539    fn span(&self) -> Span {
540        match self {
541            PartitionOfBound::ForValues {
542                for_values_span,
543                spec,
544            } => for_values_span.join_span(spec),
545            PartitionOfBound::Default(s) => s.span(),
546        }
547    }
548}
549
550/// CREATE TABLE name PARTITION OF parent_table (PostgreSQL)
551#[derive(Clone, Debug)]
552pub struct CreateTablePartitionOf<'a> {
553    /// Span of "CREATE"
554    pub create_span: Span,
555    /// Options specified after "CREATE"
556    pub create_options: Vec<CreateOption<'a>>,
557    /// Span of "TABLE"
558    pub table_span: Span,
559    /// Span of "IF NOT EXISTS" if specified
560    pub if_not_exists: Option<Span>,
561    /// Name of the new partition table
562    pub identifier: QualifiedName<'a>,
563    /// Span of "PARTITION OF"
564    pub partition_of_span: Span,
565    /// The parent partitioned table name
566    pub parent_table: QualifiedName<'a>,
567    /// Optional column definitions / constraint overrides
568    pub create_definitions: Vec<CreateDefinition<'a>>,
569    /// FOR VALUES ... | DEFAULT
570    pub bound: PartitionOfBound<'a>,
571    /// Optional sub-partitioning specification
572    pub partition_by: Option<PartitionBy<'a>>,
573}
574
575impl<'a> Spanned for CreateTablePartitionOf<'a> {
576    fn span(&self) -> Span {
577        self.create_span
578            .join_span(&self.create_options)
579            .join_span(&self.table_span)
580            .join_span(&self.if_not_exists)
581            .join_span(&self.identifier)
582            .join_span(&self.partition_of_span)
583            .join_span(&self.parent_table)
584            .join_span(&self.create_definitions)
585            .join_span(&self.bound)
586            .join_span(&self.partition_by)
587    }
588}
589
590/// Represent a create table statement
591/// ```
592/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, CreateTable, Statement, Issues};
593/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
594/// let sql = "CREATE TABLE `parts` (
595///         `id` int(11) NOT NULL COMMENT 'THIS IS THE ID FIELD',
596///         `hash` varchar(64) COLLATE utf8_bin NOT NULL,
597///         `destination` varchar(64) COLLATE utf8_bin NOT NULL,
598///         `part` varchar(64) COLLATE utf8_bin NOT NULL,
599///         `success` tinyint(1) NOT NULL
600///     ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
601///
602/// let mut issues = Issues::new(sql);
603/// let mut stmts = parse_statements(sql, &mut issues, &options);
604///
605/// # assert!(issues.is_ok());
606/// let create: CreateTable = match stmts.pop() {
607///     Some(Statement::CreateTable(c)) => *c,
608///     _ => panic!("We should get an create table statement")
609/// };
610///
611/// assert!(create.identifier.identifier.as_str() == "parts");
612/// println!("{:#?}", create.create_definitions)
613/// ```
614
615#[derive(Clone, Debug)]
616pub struct CreateTable<'a> {
617    /// Span of "CREATE"
618    pub create_span: Span,
619    /// Options specified after "CREATE"
620    pub create_options: Vec<CreateOption<'a>>,
621    /// Span of "TABLE"
622    pub table_span: Span,
623    /// Name of the table
624    pub identifier: QualifiedName<'a>,
625    /// Span of "IF NOT EXISTS" if specified
626    pub if_not_exists: Option<Span>,
627    /// Definitions of table members
628    pub create_definitions: Vec<CreateDefinition<'a>>,
629    /// Options specified after the table creation
630    pub options: Vec<TableOption<'a>>,
631    /// Create table as
632    pub table_as: Option<CreateTableAs<'a>>,
633    /// Optional PARTITION BY clause (PostgreSQL declarative partitioning)
634    pub partition_by: Option<PartitionBy<'a>>,
635}
636
637impl<'a> Spanned for CreateTable<'a> {
638    fn span(&self) -> Span {
639        self.create_span
640            .join_span(&self.create_options)
641            .join_span(&self.table_span)
642            .join_span(&self.identifier)
643            .join_span(&self.if_not_exists)
644            .join_span(&self.create_definitions)
645            .join_span(&self.options)
646            .join_span(&self.table_as)
647            .join_span(&self.partition_by)
648    }
649}
650
651/// Parse a foreign key definition
652fn parse_foreign_key_definition<'a>(
653    parser: &mut Parser<'a, '_>,
654    constraint_span: Option<Span>,
655    constraint_symbol: Option<Identifier<'a>>,
656) -> Result<CreateDefinition<'a>, ParseError> {
657    let foreign_span = parser.consume_keyword(Keyword::FOREIGN)?;
658    let key_span = parser.consume_keyword(Keyword::KEY)?;
659    let foreign_key_span = foreign_span.join_span(&key_span);
660
661    // Parse optional index name
662    let index_name = if let Token::Ident(_, _) = parser.token {
663        if !matches!(parser.token, Token::LParen) {
664            Some(parser.consume_plain_identifier_unreserved()?)
665        } else {
666            None
667        }
668    } else {
669        None
670    };
671
672    // Parse columns
673    let cols = parse_index_cols(parser)?;
674
675    // Parse REFERENCES
676    let references_span = parser.consume_keyword(Keyword::REFERENCES)?;
677    let references_table = parser.consume_plain_identifier_unreserved()?;
678
679    // Parse referenced columns
680    parser.consume_token(Token::LParen)?;
681    let mut references_cols = Vec::new();
682    loop {
683        references_cols.push(parser.consume_plain_identifier_unreserved()?);
684        if parser.skip_token(Token::Comma).is_none() {
685            break;
686        }
687    }
688    parser.consume_token(Token::RParen)?;
689
690    let match_type = if parser.skip_keyword(Keyword::MATCH).is_some() {
691        match &parser.token {
692            Token::Ident(_, Keyword::FULL) => Some(ForeignKeyMatch::Full(parser.consume())),
693            Token::Ident(_, Keyword::SIMPLE) => Some(ForeignKeyMatch::Simple(parser.consume())),
694            Token::Ident(_, Keyword::PARTIAL) => Some(ForeignKeyMatch::Partial(parser.consume())),
695            _ => None,
696        }
697    } else {
698        None
699    };
700
701    // Parse ON UPDATE/DELETE actions
702    let mut ons = Vec::new();
703    while parser.skip_keyword(Keyword::ON).is_some() {
704        let on_type = match &parser.token {
705            Token::Ident(_, Keyword::UPDATE) => {
706                ForeignKeyOnType::Update(parser.consume_keyword(Keyword::UPDATE)?)
707            }
708            Token::Ident(_, Keyword::DELETE) => {
709                ForeignKeyOnType::Delete(parser.consume_keyword(Keyword::DELETE)?)
710            }
711            _ => parser.expected_failure("UPDATE or DELETE")?,
712        };
713
714        let on_action = match &parser.token {
715            Token::Ident(_, Keyword::CASCADE) => {
716                ForeignKeyOnAction::Cascade(parser.consume_keyword(Keyword::CASCADE)?)
717            }
718            Token::Ident(_, Keyword::RESTRICT) => {
719                ForeignKeyOnAction::Restrict(parser.consume_keyword(Keyword::RESTRICT)?)
720            }
721            Token::Ident(_, Keyword::SET) => {
722                let set_span = parser.consume_keyword(Keyword::SET)?;
723                if parser.skip_keyword(Keyword::NULL).is_some() {
724                    ForeignKeyOnAction::SetNull(set_span)
725                } else if parser.skip_keyword(Keyword::DEFAULT).is_some() {
726                    ForeignKeyOnAction::SetDefault(set_span)
727                } else {
728                    parser.expected_failure("NULL or DEFAULT after SET")?
729                }
730            }
731            Token::Ident(_, Keyword::NO) => {
732                let no_span = parser.consume_keyword(Keyword::NO)?;
733                parser.consume_keyword(Keyword::ACTION)?;
734                ForeignKeyOnAction::NoAction(no_span)
735            }
736            _ => {
737                parser.expected_failure("CASCADE, RESTRICT, SET NULL, SET DEFAULT, or NO ACTION")?
738            }
739        };
740
741        ons.push(ForeignKeyOn {
742            type_: on_type,
743            action: on_action,
744        });
745    }
746
747    Ok(CreateDefinition::ForeignKeyDefinition {
748        constraint_span,
749        constraint_symbol,
750        foreign_key_span,
751        index_name,
752        cols,
753        references_span,
754        references_table,
755        references_cols,
756        match_type,
757        ons,
758    })
759}
760
761/// Parse a check constraint definition
762fn parse_check_constraint_definition<'a>(
763    parser: &mut Parser<'a, '_>,
764    constraint_span: Option<Span>,
765    constraint_symbol: Option<Identifier<'a>>,
766) -> Result<CreateDefinition<'a>, ParseError> {
767    let check_span = parser.consume_keyword(Keyword::CHECK)?;
768
769    // Parse the check expression
770    parser.consume_token(Token::LParen)?;
771    let expression = parse_expression_unreserved(parser, PRIORITY_MAX)?;
772    parser.consume_token(Token::RParen)?;
773
774    // Parse optional ENFORCED / NOT ENFORCED
775    // Note: ENFORCED keyword may not be in the keyword enum, so we skip this for now
776    let enforced = None;
777
778    Ok(CreateDefinition::CheckConstraintDefinition {
779        constraint_span,
780        constraint_symbol,
781        check_span,
782        expression,
783        enforced,
784    })
785}
786
787pub(crate) fn parse_create_definition<'a>(
788    parser: &mut Parser<'a, '_>,
789) -> Result<CreateDefinition<'a>, ParseError> {
790    // Check for optional CONSTRAINT keyword
791    let constraint_span = parser.skip_keyword(Keyword::CONSTRAINT);
792
793    // Parse optional constraint symbol (name) if CONSTRAINT was present
794    let constraint_symbol = if constraint_span.is_some() {
795        if let Token::Ident(_, keyword) = parser.token {
796            // Check if the next token is a constraint keyword, meaning no symbol was provided
797            match keyword {
798                Keyword::PRIMARY
799                | Keyword::UNIQUE
800                | Keyword::FULLTEXT
801                | Keyword::SPATIAL
802                | Keyword::INDEX
803                | Keyword::KEY
804                | Keyword::FOREIGN
805                | Keyword::CHECK => None,
806                _ => Some(parser.consume_plain_identifier_unreserved()?),
807            }
808        } else {
809            None
810        }
811    } else {
812        None
813    };
814
815    let index_type = match &parser.token {
816        Token::Ident(_, Keyword::PRIMARY) => {
817            let span = parser.consume_keywords(&[Keyword::PRIMARY, Keyword::KEY])?;
818            IndexType::Primary(span)
819        }
820        Token::Ident(_, Keyword::UNIQUE) => {
821            let span = parser.consume_keyword(Keyword::UNIQUE)?;
822            let span = if let Some(s) = parser.skip_keyword(Keyword::INDEX) {
823                span.join_span(&s)
824            } else if let Some(s) = parser.skip_keyword(Keyword::KEY) {
825                span.join_span(&s)
826            } else {
827                span
828            };
829            IndexType::Unique(span)
830        }
831        Token::Ident(_, Keyword::FULLTEXT) => {
832            let span = parser.consume_keyword(Keyword::FULLTEXT)?;
833            let span = if let Some(s) = parser.skip_keyword(Keyword::INDEX) {
834                span.join_span(&s)
835            } else if let Some(s) = parser.skip_keyword(Keyword::KEY) {
836                span.join_span(&s)
837            } else {
838                span
839            };
840            IndexType::FullText(span)
841        }
842        Token::Ident(_, Keyword::SPATIAL) => {
843            let span = parser.consume_keyword(Keyword::SPATIAL)?;
844            let span = if let Some(s) = parser.skip_keyword(Keyword::INDEX) {
845                span.join_span(&s)
846            } else if let Some(s) = parser.skip_keyword(Keyword::KEY) {
847                span.join_span(&s)
848            } else {
849                span
850            };
851            IndexType::Spatial(span)
852        }
853        Token::Ident(_, Keyword::INDEX) => {
854            IndexType::Index(parser.consume_keyword(Keyword::INDEX)?)
855        }
856        Token::Ident(_, Keyword::KEY) => IndexType::Index(parser.consume_keyword(Keyword::KEY)?),
857        Token::Ident(_, Keyword::FOREIGN) => {
858            return parse_foreign_key_definition(parser, constraint_span, constraint_symbol);
859        }
860        Token::Ident(_, Keyword::CHECK) => {
861            return parse_check_constraint_definition(parser, constraint_span, constraint_symbol);
862        }
863        Token::Ident(_, Keyword::LIKE) if parser.options.dialect.is_postgresql() => {
864            // PostgreSQL: LIKE source_table - copies column definitions
865            let like_span = parser.consume_keyword(Keyword::LIKE)?;
866            parser.postgres_only(&like_span);
867            let source_table = parse_qualified_name_unreserved(parser)?;
868            return Ok(CreateDefinition::LikeTable {
869                like_span,
870                source_table,
871            });
872        }
873        Token::String(_, StringType::DoubleQuoted) if parser.options.dialect.is_postgresql() => {
874            // PostgreSQL allows double-quoted identifiers as column names
875            // If we had CONSTRAINT keyword, this is an error
876            if constraint_span.is_some() {
877                parser.expected_failure(
878                    "PRIMARY, UNIQUE, INDEX, KEY, FULLTEXT, SPATIAL, FOREIGN, or CHECK",
879                )?
880            }
881            return Ok(CreateDefinition::ColumnDefinition {
882                identifier: parser.consume_plain_identifier_unreserved()?,
883                data_type: parse_data_type(parser, DataTypeContext::Column)?,
884            });
885        }
886        Token::Ident(_, _) => {
887            // If we had CONSTRAINT keyword, this is an error
888            if constraint_span.is_some() {
889                parser.expected_failure(
890                    "PRIMARY, UNIQUE, INDEX, KEY, FULLTEXT, SPATIAL, FOREIGN, or CHECK",
891                )?
892            }
893            return Ok(CreateDefinition::ColumnDefinition {
894                identifier: parser.consume_plain_identifier_unreserved()?,
895                data_type: parse_data_type(parser, DataTypeContext::Column)?,
896            });
897        }
898        _ => return parser.expected_failure("identifier"),
899    };
900
901    // Parse optional index name
902    let index_name = match &index_type {
903        IndexType::Primary(_) => {
904            // PRIMARY KEY may optionally have a name before the column list
905            match &parser.token {
906                Token::Ident(_, _) if !matches!(parser.token, Token::LParen) => {
907                    Some(parser.consume_plain_identifier_unreserved()?)
908                }
909                Token::String(s, _) => {
910                    let val = *s;
911                    let span = parser.consume();
912                    Some(Identifier { value: val, span })
913                }
914                _ => None,
915            }
916        }
917        _ => {
918            // Other index types can optionally have a name
919            match &parser.token {
920                Token::Ident(_, _)
921                    if !matches!(
922                        parser.token,
923                        Token::LParen | Token::Ident(_, Keyword::USING)
924                    ) =>
925                {
926                    Some(parser.consume_plain_identifier_restrict(Restrict::USING)?)
927                }
928                Token::String(s, _) => {
929                    let val = *s;
930                    let span = parser.consume();
931                    Some(Identifier { value: val, span })
932                }
933                _ => None,
934            }
935        }
936    };
937
938    // Parse optional USING BTREE/HASH/RTREE before column list
939    let mut index_options = Vec::new();
940    if matches!(parser.token, Token::Ident(_, Keyword::USING)) {
941        parse_index_type(parser, &mut index_options)?;
942    }
943
944    // Parse index columns
945    let cols = parse_index_cols(parser)?;
946
947    // Parse index options (USING, COMMENT, etc.) after column list
948    parse_index_options(parser, &mut index_options)?;
949
950    Ok(CreateDefinition::IndexDefinition {
951        constraint_span,
952        constraint_symbol,
953        index_type,
954        index_name,
955        cols,
956        index_options,
957    })
958}
959
960/// Parse PARTITION BY RANGE|LIST|HASH (key [, ...])
961fn parse_partition_by<'a>(parser: &mut Parser<'a, '_>) -> Result<PartitionBy<'a>, ParseError> {
962    let partition_span = parser.consume_keyword(Keyword::PARTITION)?;
963    let by_span = parser.consume_keyword(Keyword::BY)?;
964    let partition_by_span = partition_span.join_span(&by_span);
965
966    let method = match &parser.token {
967        Token::Ident(_, Keyword::RANGE) => {
968            PartitionMethod::Range(parser.consume_keyword(Keyword::RANGE)?)
969        }
970        Token::Ident(_, Keyword::LIST) => {
971            PartitionMethod::List(parser.consume_keyword(Keyword::LIST)?)
972        }
973        Token::Ident(_, Keyword::HASH) => {
974            PartitionMethod::Hash(parser.consume_keyword(Keyword::HASH)?)
975        }
976        _ => parser.expected_failure("RANGE, LIST, or HASH")?,
977    };
978
979    parser.consume_token(Token::LParen)?;
980    let mut keys = Vec::new();
981    loop {
982        // Key element is either a parenthesised expression or a bare column/expression
983        let key = if matches!(parser.token, Token::LParen) {
984            parser.consume_token(Token::LParen)?;
985            let expr = parse_expression_unreserved(parser, PRIORITY_MAX)?;
986            parser.consume_token(Token::RParen)?;
987            expr
988        } else {
989            parse_expression_unreserved(parser, PRIORITY_MAX)?
990        };
991        keys.push(key);
992        if parser.skip_token(Token::Comma).is_none() {
993            break;
994        }
995    }
996    parser.consume_token(Token::RParen)?;
997
998    Ok(PartitionBy {
999        partition_by_span,
1000        method,
1001        keys,
1002    })
1003}
1004
1005/// Parse a single partition bound expression: value expression, MINVALUE, or MAXVALUE
1006fn parse_partition_bound_expr<'a>(
1007    parser: &mut Parser<'a, '_>,
1008) -> Result<PartitionBoundExpr<'a>, ParseError> {
1009    match &parser.token {
1010        Token::Ident(_, Keyword::MINVALUE) => Ok(PartitionBoundExpr::MinValue(
1011            parser.consume_keyword(Keyword::MINVALUE)?,
1012        )),
1013        Token::Ident(_, Keyword::MAXVALUE) => Ok(PartitionBoundExpr::MaxValue(
1014            parser.consume_keyword(Keyword::MAXVALUE)?,
1015        )),
1016        _ => Ok(PartitionBoundExpr::Expr(parse_expression_unreserved(
1017            parser,
1018            PRIORITY_MAX,
1019        )?)),
1020    }
1021}
1022
1023/// Parse a parenthesised list of partition bound expressions
1024fn parse_partition_bound_exprs<'a>(
1025    parser: &mut Parser<'a, '_>,
1026) -> Result<Vec<PartitionBoundExpr<'a>>, ParseError> {
1027    parser.consume_token(Token::LParen)?;
1028    let mut values = Vec::new();
1029    loop {
1030        values.push(parse_partition_bound_expr(parser)?);
1031        if parser.skip_token(Token::Comma).is_none() {
1032            break;
1033        }
1034    }
1035    parser.consume_token(Token::RParen)?;
1036    Ok(values)
1037}
1038
1039/// Parse a partition_bound_spec: IN (...) | FROM (...) TO (...) | WITH (MODULUS n, REMAINDER r)
1040fn parse_partition_bound_spec<'a>(
1041    parser: &mut Parser<'a, '_>,
1042) -> Result<PartitionBoundSpec<'a>, ParseError> {
1043    match &parser.token {
1044        Token::Ident(_, Keyword::IN) => {
1045            let in_span = parser.consume_keyword(Keyword::IN)?;
1046            let values = parse_partition_bound_exprs(parser)?;
1047            Ok(PartitionBoundSpec::In { in_span, values })
1048        }
1049        Token::Ident(_, Keyword::FROM) => {
1050            let from_span = parser.consume_keyword(Keyword::FROM)?;
1051            let from_values = parse_partition_bound_exprs(parser)?;
1052            let to_span = parser.consume_keyword(Keyword::TO)?;
1053            let to_values = parse_partition_bound_exprs(parser)?;
1054            Ok(PartitionBoundSpec::FromTo {
1055                from_span,
1056                from_values,
1057                to_span,
1058                to_values,
1059            })
1060        }
1061        Token::Ident(_, Keyword::WITH) => {
1062            let with_span = parser.consume_keyword(Keyword::WITH)?;
1063            parser.consume_token(Token::LParen)?;
1064            let modulus_span = parser.consume_keyword(Keyword::MODULUS)?;
1065            let modulus = parser.consume_int::<u64>()?;
1066            parser.consume_token(Token::Comma)?;
1067            let remainder_span = parser.consume_keyword(Keyword::REMAINDER)?;
1068            let remainder = parser.consume_int::<u64>()?;
1069            parser.consume_token(Token::RParen)?;
1070            Ok(PartitionBoundSpec::WithModulusRemainder {
1071                with_span,
1072                modulus_span,
1073                modulus,
1074                remainder_span,
1075                remainder,
1076            })
1077        }
1078        _ => parser.expected_failure("IN, FROM, or WITH"),
1079    }
1080}
1081
1082pub(crate) fn parse_create_table<'a>(
1083    parser: &mut Parser<'a, '_>,
1084    create_span: Span,
1085    create_options: Vec<CreateOption<'a>>,
1086    table_span: Span,
1087    if_not_exists: Option<Span>,
1088    identifier: QualifiedName<'a>,
1089) -> Result<CreateTable<'a>, ParseError> {
1090    parser.consume_token(Token::LParen)?;
1091
1092    let mut create_definitions = Vec::new();
1093    if !matches!(parser.token, Token::RParen) {
1094        loop {
1095            parser.recovered(
1096                "')' or ','",
1097                &|t| matches!(t, Token::RParen | Token::Comma),
1098                |parser| {
1099                    create_definitions.push(parse_create_definition(parser)?);
1100                    Ok(())
1101                },
1102            )?;
1103            if matches!(parser.token, Token::RParen) {
1104                break;
1105            }
1106            parser.consume_token(Token::Comma)?;
1107        }
1108    }
1109    parser.consume_token(Token::RParen)?;
1110
1111    let mut options = Vec::new();
1112    let mut table_as: Option<CreateTableAs<'_>> = None;
1113    let mut partition_by: Option<PartitionBy<'_>> = None;
1114    let delimiter_name = parser.lexer.delimiter_name();
1115    parser.recovered(
1116        delimiter_name,
1117        &|t| t == &Token::Eof || t == &Token::Delimiter,
1118        |parser| {
1119            loop {
1120                match &parser.token {
1121                    Token::Ident(_, Keyword::ENGINE) => {
1122                        let identifier = parser.consume_keyword(Keyword::ENGINE)?;
1123                        parser.skip_token(Token::Eq);
1124                        options.push(TableOption::Engine {
1125                            identifier,
1126                            value: parser.consume_plain_identifier_unreserved()?,
1127                        });
1128                    }
1129                    Token::Ident(_, Keyword::DEFAULT) => {
1130                        let default_span = parser.consume_keyword(Keyword::DEFAULT)?;
1131                        match &parser.token {
1132                            Token::Ident(_, Keyword::CHARSET) => {
1133                                let identifier = default_span
1134                                    .join_span(&parser.consume_keyword(Keyword::CHARSET)?);
1135                                parser.skip_token(Token::Eq);
1136                                options.push(TableOption::DefaultCharSet {
1137                                    identifier,
1138                                    value: parser.consume_plain_identifier_unreserved()?,
1139                                });
1140                            }
1141                            Token::Ident(_, Keyword::COLLATE) => {
1142                                let identifier = default_span
1143                                    .join_span(&parser.consume_keyword(Keyword::COLLATE)?);
1144                                parser.skip_token(Token::Eq);
1145                                options.push(TableOption::DefaultCollate {
1146                                    identifier,
1147                                    value: parser.consume_plain_identifier_unreserved()?,
1148                                });
1149                            }
1150                            _ => parser.expected_failure("'CHARSET' or 'COLLATE'")?,
1151                        }
1152                    }
1153                    Token::Ident(_, Keyword::CHARSET) => {
1154                        let identifier = parser.consume_keyword(Keyword::CHARSET)?;
1155                        parser.skip_token(Token::Eq);
1156                        options.push(TableOption::CharSet {
1157                            identifier,
1158                            value: parser.consume_plain_identifier_unreserved()?,
1159                        });
1160                    }
1161                    Token::Ident(_, Keyword::COLLATE) => {
1162                        let identifier = parser.consume_keyword(Keyword::COLLATE)?;
1163                        parser.skip_token(Token::Eq);
1164                        options.push(TableOption::Collate {
1165                            identifier,
1166                            value: parser.consume_plain_identifier_unreserved()?,
1167                        });
1168                    }
1169                    Token::Ident(_, Keyword::ROW_FORMAT) => {
1170                        let identifier = parser.consume_keyword(Keyword::ROW_FORMAT)?;
1171                        parser.skip_token(Token::Eq);
1172                        options.push(TableOption::RowFormat {
1173                            identifier,
1174                            value: parser.consume_plain_identifier_unreserved()?,
1175                        });
1176                        //TODO validate raw format is in the keyword set
1177                    }
1178                    Token::Ident(_, Keyword::KEY_BLOCK_SIZE) => {
1179                        let identifier = parser.consume_keywords(&[Keyword::KEY_BLOCK_SIZE])?;
1180                        parser.skip_token(Token::Eq);
1181                        options.push(TableOption::KeyBlockSize {
1182                            identifier,
1183                            value: parser.consume_int()?,
1184                        });
1185                    }
1186                    Token::Ident(_, Keyword::COMMENT) => {
1187                        let identifier = parser.consume_keyword(Keyword::COMMENT)?;
1188                        parser.skip_token(Token::Eq);
1189                        options.push(TableOption::Comment {
1190                            identifier,
1191                            value: parser.consume_string()?,
1192                        });
1193                    }
1194                    Token::Ident(_, Keyword::STRICT) => {
1195                        let identifier = parser.consume_keyword(Keyword::STRICT)?;
1196                        options.push(TableOption::Strict { identifier });
1197                    }
1198                    Token::Ident(_, Keyword::AUTO_INCREMENT) => {
1199                        let identifier = parser.consume_keyword(Keyword::AUTO_INCREMENT)?;
1200                        parser.skip_token(Token::Eq);
1201                        options.push(TableOption::AutoIncrement {
1202                            identifier,
1203                            value: parser.consume_int()?,
1204                        });
1205                    }
1206                    Token::Ident(_, Keyword::DATA) => {
1207                        let identifier =
1208                            parser.consume_keywords(&[Keyword::DATA, Keyword::DIRECTORY])?;
1209                        parser.skip_token(Token::Eq);
1210                        options.push(TableOption::DataDirectory {
1211                            identifier,
1212                            value: parser.consume_string()?,
1213                        });
1214                    }
1215                    Token::Ident(_, Keyword::INDEX) => {
1216                        let identifier =
1217                            parser.consume_keywords(&[Keyword::INDEX, Keyword::DIRECTORY])?;
1218                        parser.skip_token(Token::Eq);
1219                        options.push(TableOption::IndexDirectory {
1220                            identifier,
1221                            value: parser.consume_string()?,
1222                        });
1223                    }
1224                    Token::Ident(_, Keyword::INSERT_METHOD) => {
1225                        let identifier = parser.consume_keyword(Keyword::INSERT_METHOD)?;
1226                        parser.skip_token(Token::Eq);
1227                        options.push(TableOption::InsertMethod {
1228                            identifier,
1229                            value: parser.consume_plain_identifier_unreserved()?,
1230                        });
1231                    }
1232                    Token::Ident(_, Keyword::PACK_KEYS) => {
1233                        let identifier = parser.consume_keyword(Keyword::PACK_KEYS)?;
1234                        parser.skip_token(Token::Eq);
1235                        options.push(TableOption::PackKeys {
1236                            identifier,
1237                            value: parser.consume_int()?,
1238                        });
1239                    }
1240                    Token::Ident(_, Keyword::STATS_AUTO_RECALC) => {
1241                        let identifier = parser.consume_keyword(Keyword::STATS_AUTO_RECALC)?;
1242                        parser.skip_token(Token::Eq);
1243                        options.push(TableOption::StatsAutoRecalc {
1244                            identifier,
1245                            value: parser.consume_int()?,
1246                        });
1247                    }
1248                    Token::Ident(_, Keyword::STATS_PERSISTENT) => {
1249                        let identifier = parser.consume_keyword(Keyword::STATS_PERSISTENT)?;
1250                        parser.skip_token(Token::Eq);
1251                        options.push(TableOption::StatsPersistent {
1252                            identifier,
1253                            value: parser.consume_int()?,
1254                        });
1255                    }
1256                    Token::Ident(_, Keyword::STATS_SAMPLE_PAGES) => {
1257                        let identifier = parser.consume_keyword(Keyword::STATS_SAMPLE_PAGES)?;
1258                        parser.skip_token(Token::Eq);
1259                        options.push(TableOption::StatsSamplePages {
1260                            identifier,
1261                            value: parser.consume_int()?,
1262                        });
1263                    }
1264                    Token::Ident(_, Keyword::DELAY_KEY_WRITE) => {
1265                        let identifier = parser.consume_keyword(Keyword::DELAY_KEY_WRITE)?;
1266                        parser.skip_token(Token::Eq);
1267                        let (val, span) = parser.consume_int::<usize>()?;
1268                        options.push(TableOption::DelayKeyWrite {
1269                            identifier,
1270                            value: (val != 0, span),
1271                        });
1272                    }
1273                    Token::Ident(_, Keyword::COMPRESSION) => {
1274                        let identifier = parser.consume_keyword(Keyword::COMPRESSION)?;
1275                        parser.skip_token(Token::Eq);
1276                        options.push(TableOption::Compression {
1277                            identifier,
1278                            value: parser.consume_string()?,
1279                        });
1280                    }
1281                    Token::Ident(_, Keyword::ENCRYPTION) => {
1282                        let identifier = parser.consume_keyword(Keyword::ENCRYPTION)?;
1283                        parser.skip_token(Token::Eq);
1284                        // ENCRYPTION can be 'Y'/'N' string or YES/NO keyword
1285                        let value = match &parser.token {
1286                            Token::String(..) => {
1287                                let s = parser.consume_string()?;
1288                                let is_yes = s.as_str().eq_ignore_ascii_case("y")
1289                                    || s.as_str().eq_ignore_ascii_case("yes");
1290                                (is_yes, s.span())
1291                            }
1292                            _ => {
1293                                let id = parser.consume_plain_identifier_unreserved()?;
1294                                let is_yes = id.value.eq_ignore_ascii_case("yes");
1295                                (is_yes, id.span())
1296                            }
1297                        };
1298                        options.push(TableOption::Encryption { identifier, value });
1299                    }
1300                    Token::Ident(_, Keyword::MAX_ROWS) => {
1301                        let identifier = parser.consume_keyword(Keyword::MAX_ROWS)?;
1302                        parser.skip_token(Token::Eq);
1303                        options.push(TableOption::MaxRows {
1304                            identifier,
1305                            value: parser.consume_int()?,
1306                        });
1307                    }
1308                    Token::Ident(_, Keyword::MIN_ROWS) => {
1309                        let identifier = parser.consume_keyword(Keyword::MIN_ROWS)?;
1310                        parser.skip_token(Token::Eq);
1311                        options.push(TableOption::MinRows {
1312                            identifier,
1313                            value: parser.consume_int()?,
1314                        });
1315                    }
1316                    Token::Ident(_, Keyword::AUTOEXTEND_SIZE) => {
1317                        let identifier = parser.consume_keyword(Keyword::AUTOEXTEND_SIZE)?;
1318                        parser.skip_token(Token::Eq);
1319                        options.push(TableOption::AutoExtendSize {
1320                            identifier,
1321                            value: parser.consume_int()?,
1322                        });
1323                    }
1324                    Token::Ident(_, Keyword::AVG_ROW_LENGTH) => {
1325                        let identifier = parser.consume_keyword(Keyword::AVG_ROW_LENGTH)?;
1326                        parser.skip_token(Token::Eq);
1327                        options.push(TableOption::AvgRowLength {
1328                            identifier,
1329                            value: parser.consume_int()?,
1330                        });
1331                    }
1332                    Token::Ident(_, Keyword::CHECKSUM) => {
1333                        let identifier = parser.consume_keyword(Keyword::CHECKSUM)?;
1334                        parser.skip_token(Token::Eq);
1335                        let (val, span) = parser.consume_int::<usize>()?;
1336                        options.push(TableOption::Checksum {
1337                            identifier,
1338                            value: (val != 0, span),
1339                        });
1340                    }
1341                    Token::Ident(_, Keyword::CONNECTION) => {
1342                        let identifier = parser.consume_keyword(Keyword::CONNECTION)?;
1343                        parser.skip_token(Token::Eq);
1344                        options.push(TableOption::Connection {
1345                            identifier,
1346                            value: parser.consume_string()?,
1347                        });
1348                    }
1349                    Token::Ident(_, Keyword::ENGINE_ATTRIBUTE) => {
1350                        let identifier = parser.consume_keyword(Keyword::ENGINE_ATTRIBUTE)?;
1351                        parser.skip_token(Token::Eq);
1352                        options.push(TableOption::EngineAttribute {
1353                            identifier,
1354                            value: parser.consume_string()?,
1355                        });
1356                    }
1357                    Token::Ident(_, Keyword::PASSWORD) => {
1358                        let identifier = parser.consume_keyword(Keyword::PASSWORD)?;
1359                        parser.skip_token(Token::Eq);
1360                        options.push(TableOption::Password {
1361                            identifier,
1362                            value: parser.consume_string()?,
1363                        });
1364                    }
1365                    Token::Ident(_, Keyword::SECONDARY_ENGINE_ATTRIBUTE) => {
1366                        let identifier =
1367                            parser.consume_keyword(Keyword::SECONDARY_ENGINE_ATTRIBUTE)?;
1368                        parser.skip_token(Token::Eq);
1369                        options.push(TableOption::SecondaryEngineAttribute {
1370                            identifier,
1371                            value: parser.consume_string()?,
1372                        });
1373                    }
1374                    Token::Ident(_, Keyword::START) => {
1375                        let identifier =
1376                            parser.consume_keywords(&[Keyword::START, Keyword::TRANSACTION])?;
1377                        options.push(TableOption::StartTransaction { identifier });
1378                    }
1379                    Token::Ident(_, Keyword::TABLESPACE) => {
1380                        let identifier = parser.consume_keyword(Keyword::TABLESPACE)?;
1381                        options.push(TableOption::Tablespace {
1382                            identifier,
1383                            value: parser.consume_plain_identifier_unreserved()?,
1384                        });
1385                    }
1386                    Token::Ident(_, Keyword::STORAGE) => {
1387                        let identifier = parser.consume_keyword(Keyword::STORAGE)?;
1388                        options.push(TableOption::Storage {
1389                            identifier,
1390                            value: parser.consume_plain_identifier_unreserved()?,
1391                        });
1392                    }
1393                    Token::Ident(_, Keyword::UNION) => {
1394                        let identifier = parser.consume_keyword(Keyword::UNION)?;
1395                        parser.skip_token(Token::Eq);
1396                        parser.consume_token(Token::LParen)?;
1397                        let mut tables = Vec::new();
1398                        loop {
1399                            tables.push(parser.consume_plain_identifier_unreserved()?);
1400                            if parser.skip_token(Token::Comma).is_none() {
1401                                break;
1402                            }
1403                        }
1404                        parser.consume_token(Token::RParen)?;
1405                        options.push(TableOption::Union {
1406                            identifier,
1407                            value: tables,
1408                        });
1409                    }
1410                    Token::Ident(_, Keyword::INHERITS) => {
1411                        let identifier = parser.consume_keyword(Keyword::INHERITS)?;
1412                        parser.postgres_only(&identifier);
1413                        parser.consume_token(Token::LParen)?;
1414                        let mut tables = Vec::new();
1415                        parser.recovered("')'", &|t| t == &Token::RParen, |parser| {
1416                            loop {
1417                                tables.push(parse_qualified_name_unreserved(parser)?);
1418                                if parser.skip_token(Token::Comma).is_none() {
1419                                    break;
1420                                }
1421                            }
1422                            Ok(())
1423                        })?;
1424                        parser.consume_token(Token::RParen)?;
1425                        options.push(TableOption::Inherits {
1426                            identifier,
1427                            value: tables,
1428                        });
1429                    }
1430                    Token::Ident(_, Keyword::WITH) => {
1431                        let identifier = parser.consume_keyword(Keyword::WITH)?;
1432                        parser.postgres_only(&identifier);
1433                        parser.consume_token(Token::LParen)?;
1434                        let mut params = Vec::new();
1435                        parser.recovered("')'", &|t| t == &Token::RParen, |parser| {
1436                            loop {
1437                                let key = parser.consume_plain_identifier_unreserved()?;
1438                                parser.consume_token(Token::Eq)?;
1439                                let val = parse_expression_unreserved(parser, PRIORITY_MAX)?;
1440                                params.push((key, val));
1441                                if parser.skip_token(Token::Comma).is_none() {
1442                                    break;
1443                                }
1444                            }
1445                            Ok(())
1446                        })?;
1447                        parser.consume_token(Token::RParen)?;
1448                        options.push(TableOption::WithOptions {
1449                            identifier,
1450                            options: params,
1451                        });
1452                    }
1453                    Token::Ident(_, Keyword::ON) => {
1454                        let identifier =
1455                            parser.consume_keywords(&[Keyword::ON, Keyword::COMMIT])?;
1456                        parser.postgres_only(&identifier);
1457                        let action = match &parser.token {
1458                            Token::Ident(_, Keyword::PRESERVE) => OnCommitAction::PreserveRows(
1459                                parser.consume_keywords(&[Keyword::PRESERVE, Keyword::ROWS])?,
1460                            ),
1461                            Token::Ident(_, Keyword::DELETE) => OnCommitAction::DeleteRows(
1462                                parser.consume_keywords(&[Keyword::DELETE, Keyword::ROWS])?,
1463                            ),
1464                            Token::Ident(_, Keyword::DROP) => {
1465                                OnCommitAction::Drop(parser.consume_keyword(Keyword::DROP)?)
1466                            }
1467                            _ => parser.expected_failure("PRESERVE ROWS, DELETE ROWS, or DROP")?,
1468                        };
1469                        options.push(TableOption::OnCommit { identifier, action });
1470                    }
1471                    Token::Ident(_, Keyword::PARTITION) => {
1472                        partition_by = Some(parse_partition_by(parser)?);
1473                    }
1474                    Token::Ident(_, Keyword::IGNORE)
1475                    | Token::Ident(_, Keyword::REPLACE)
1476                    | Token::Ident(_, Keyword::AS) => {
1477                        let ignore_span = parser.skip_keyword(Keyword::IGNORE);
1478                        let replace_span = parser.skip_keyword(Keyword::REPLACE);
1479                        let as_span = parser.consume_keyword(Keyword::AS)?;
1480
1481                        if let Some(table_as) = &table_as {
1482                            parser.err("Multiple AS clauses not supported", table_as);
1483                        }
1484
1485                        let query = parse_compound_query(parser)?;
1486                        table_as = Some(CreateTableAs {
1487                            as_span,
1488                            replace_span,
1489                            ignore_span,
1490                            query,
1491                        });
1492                    }
1493                    Token::Comma => {
1494                        parser.consume_token(Token::Comma)?;
1495                    }
1496                    Token::Delimiter => break,
1497                    Token::Eof => break,
1498                    _ => {
1499                        parser.expected_failure("table option or delimiter")?;
1500                    }
1501                }
1502            }
1503            Ok(())
1504        },
1505    )?;
1506
1507    Ok(CreateTable {
1508        create_span,
1509        create_options,
1510        table_span,
1511        identifier,
1512        if_not_exists,
1513        options,
1514        create_definitions,
1515        table_as,
1516        partition_by,
1517    })
1518}
1519
1520fn parse_create_table_partition_of<'a>(
1521    parser: &mut Parser<'a, '_>,
1522    create_span: Span,
1523    create_options: Vec<CreateOption<'a>>,
1524    table_span: Span,
1525    if_not_exists: Option<Span>,
1526    identifier: QualifiedName<'a>,
1527) -> Result<CreateTablePartitionOf<'a>, ParseError> {
1528    let partition_span = parser.consume_keyword(Keyword::PARTITION)?;
1529    let of_span = parser.consume_keyword(Keyword::OF)?;
1530    let partition_of_span = partition_span.join_span(&of_span);
1531
1532    let parent_table = parse_qualified_name_unreserved(parser)?;
1533
1534    // Optional column definitions / constraint overrides
1535    let mut create_definitions = Vec::new();
1536    if matches!(parser.token, Token::LParen) {
1537        parser.consume_token(Token::LParen)?;
1538        if !matches!(parser.token, Token::RParen) {
1539            loop {
1540                parser.recovered(
1541                    "')' or ','",
1542                    &|t| matches!(t, Token::RParen | Token::Comma),
1543                    |parser| {
1544                        create_definitions.push(parse_create_definition(parser)?);
1545                        Ok(())
1546                    },
1547                )?;
1548                if matches!(parser.token, Token::RParen) {
1549                    break;
1550                }
1551                parser.consume_token(Token::Comma)?;
1552            }
1553        }
1554        parser.consume_token(Token::RParen)?;
1555    }
1556
1557    // FOR VALUES … | DEFAULT
1558    let bound = match &parser.token {
1559        Token::Ident(_, Keyword::DEFAULT) => {
1560            PartitionOfBound::Default(parser.consume_keyword(Keyword::DEFAULT)?)
1561        }
1562        Token::Ident(_, Keyword::FOR) => {
1563            let for_values_span = parser.consume_keywords(&[Keyword::FOR, Keyword::VALUES])?;
1564            let spec = parse_partition_bound_spec(parser)?;
1565            PartitionOfBound::ForValues {
1566                for_values_span,
1567                spec,
1568            }
1569        }
1570        _ => parser.expected_failure("FOR VALUES or DEFAULT")?,
1571    };
1572
1573    // Optional sub-partitioning
1574    let partition_by = if matches!(parser.token, Token::Ident(_, Keyword::PARTITION)) {
1575        Some(parse_partition_by(parser)?)
1576    } else {
1577        None
1578    };
1579
1580    Ok(CreateTablePartitionOf {
1581        create_span,
1582        create_options,
1583        table_span,
1584        if_not_exists,
1585        identifier,
1586        partition_of_span,
1587        parent_table,
1588        create_definitions,
1589        bound,
1590        partition_by,
1591    })
1592}
1593
1594/// Entry point for `CREATE TABLE` dispatching.
1595///
1596/// Parses TABLE + optional IF NOT EXISTS + table identifier, then dispatches to either
1597/// `parse_create_table_partition_of` (for `PARTITION OF`) or `parse_create_table`.
1598pub(crate) fn parse_create_table_or_partition_of<'a>(
1599    parser: &mut Parser<'a, '_>,
1600    create_span: Span,
1601    create_options: Vec<CreateOption<'a>>,
1602) -> Result<Statement<'a>, ParseError> {
1603    let table_span = parser.consume_keyword(Keyword::TABLE)?;
1604
1605    let if_not_exists = if let Some(if_) = parser.skip_keyword(Keyword::IF) {
1606        Some(
1607            if_.start
1608                ..parser
1609                    .consume_keywords(&[Keyword::NOT, Keyword::EXISTS])?
1610                    .end,
1611        )
1612    } else {
1613        None
1614    };
1615    let identifier = parse_qualified_name_unreserved(parser)?;
1616
1617    if matches!(parser.token, Token::Ident(_, Keyword::PARTITION)) {
1618        Ok(Statement::CreateTablePartitionOf(Box::new(
1619            parse_create_table_partition_of(
1620                parser,
1621                create_span,
1622                create_options,
1623                table_span,
1624                if_not_exists,
1625                identifier,
1626            )?,
1627        )))
1628    } else if matches!(parser.token, Token::Ident(_, Keyword::AS))
1629        && !matches!(parser.peek(), Token::LParen)
1630    {
1631        // CREATE TABLE foo AS SELECT ... (CTAS — no column list)
1632        let as_span = parser.consume_keyword(Keyword::AS)?;
1633        let query = parse_compound_query(parser)?;
1634        Ok(Statement::CreateTable(Box::new(CreateTable {
1635            create_span,
1636            create_options,
1637            table_span,
1638            identifier,
1639            if_not_exists,
1640            options: alloc::vec![],
1641            create_definitions: alloc::vec![],
1642            table_as: Some(CreateTableAs {
1643                as_span,
1644                replace_span: None,
1645                ignore_span: None,
1646                query,
1647            }),
1648            partition_by: None,
1649        })))
1650    } else {
1651        Ok(Statement::CreateTable(Box::new(parse_create_table(
1652            parser,
1653            create_span,
1654            create_options,
1655            table_span,
1656            if_not_exists,
1657            identifier,
1658        )?)))
1659    }
1660}