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