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