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: Identifier<'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 = parser.consume_plain_identifier_unreserved()?;
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 { value: val, span })
930                }
931                _ => None,
932            }
933        }
934        _ => {
935            // Other index types can optionally have a name
936            match &parser.token {
937                Token::Ident(_, _)
938                    if !matches!(
939                        parser.token,
940                        Token::LParen | Token::Ident(_, Keyword::USING)
941                    ) =>
942                {
943                    Some(parser.consume_plain_identifier_restrict(Restrict::USING)?)
944                }
945                Token::String(s, _) => {
946                    let val = *s;
947                    let span = parser.consume();
948                    Some(Identifier { value: val, span })
949                }
950                _ => None,
951            }
952        }
953    };
954
955    // Parse optional USING BTREE/HASH/RTREE before column list
956    let mut index_options = Vec::new();
957    if matches!(parser.token, Token::Ident(_, Keyword::USING)) {
958        parse_index_type(parser, &mut index_options)?;
959    }
960
961    // Parse index columns
962    let (cols, cols_r_paren) = parse_index_cols(parser)?;
963
964    // Parse index options (USING, COMMENT, etc.) after column list
965    parse_index_options(parser, &mut index_options)?;
966
967    Ok(CreateDefinition::IndexDefinition {
968        constraint_span,
969        constraint_symbol,
970        index_type,
971        index_name,
972        cols,
973        cols_r_paren,
974        index_options,
975    })
976}
977
978/// Parse PARTITION BY RANGE|LIST|HASH (key [, ...])
979fn parse_partition_by<'a>(parser: &mut Parser<'a, '_>) -> Result<PartitionBy<'a>, ParseError> {
980    let partition_span = parser.consume_keyword(Keyword::PARTITION)?;
981    let by_span = parser.consume_keyword(Keyword::BY)?;
982    let partition_by_span = partition_span.join_span(&by_span);
983
984    let method = match &parser.token {
985        Token::Ident(_, Keyword::RANGE) => {
986            PartitionMethod::Range(parser.consume_keyword(Keyword::RANGE)?)
987        }
988        Token::Ident(_, Keyword::LIST) => {
989            PartitionMethod::List(parser.consume_keyword(Keyword::LIST)?)
990        }
991        Token::Ident(_, Keyword::HASH) => {
992            PartitionMethod::Hash(parser.consume_keyword(Keyword::HASH)?)
993        }
994        _ => parser.expected_failure("RANGE, LIST, or HASH")?,
995    };
996
997    parser.consume_token(Token::LParen)?;
998    let mut keys = Vec::new();
999    loop {
1000        // Key element is either a parenthesised expression or a bare column/expression
1001        let key = if matches!(parser.token, Token::LParen) {
1002            parser.consume_token(Token::LParen)?;
1003            let expr = parse_expression_unreserved(parser, PRIORITY_MAX)?;
1004            parser.consume_token(Token::RParen)?;
1005            expr
1006        } else {
1007            parse_expression_unreserved(parser, PRIORITY_MAX)?
1008        };
1009        keys.push(key);
1010        if parser.skip_token(Token::Comma).is_none() {
1011            break;
1012        }
1013    }
1014    parser.consume_token(Token::RParen)?;
1015
1016    Ok(PartitionBy {
1017        partition_by_span,
1018        method,
1019        keys,
1020    })
1021}
1022
1023/// Parse a single partition bound expression: value expression, MINVALUE, or MAXVALUE
1024fn parse_partition_bound_expr<'a>(
1025    parser: &mut Parser<'a, '_>,
1026) -> Result<PartitionBoundExpr<'a>, ParseError> {
1027    match &parser.token {
1028        Token::Ident(_, Keyword::MINVALUE) => Ok(PartitionBoundExpr::MinValue(
1029            parser.consume_keyword(Keyword::MINVALUE)?,
1030        )),
1031        Token::Ident(_, Keyword::MAXVALUE) => Ok(PartitionBoundExpr::MaxValue(
1032            parser.consume_keyword(Keyword::MAXVALUE)?,
1033        )),
1034        _ => Ok(PartitionBoundExpr::Expr(parse_expression_unreserved(
1035            parser,
1036            PRIORITY_MAX,
1037        )?)),
1038    }
1039}
1040
1041/// Parse a parenthesised list of partition bound expressions
1042fn parse_partition_bound_exprs<'a>(
1043    parser: &mut Parser<'a, '_>,
1044) -> Result<Vec<PartitionBoundExpr<'a>>, ParseError> {
1045    parser.consume_token(Token::LParen)?;
1046    let mut values = Vec::new();
1047    loop {
1048        values.push(parse_partition_bound_expr(parser)?);
1049        if parser.skip_token(Token::Comma).is_none() {
1050            break;
1051        }
1052    }
1053    parser.consume_token(Token::RParen)?;
1054    Ok(values)
1055}
1056
1057/// Parse a partition_bound_spec: IN (...) | FROM (...) TO (...) | WITH (MODULUS n, REMAINDER r)
1058fn parse_partition_bound_spec<'a>(
1059    parser: &mut Parser<'a, '_>,
1060) -> Result<PartitionBoundSpec<'a>, ParseError> {
1061    match &parser.token {
1062        Token::Ident(_, Keyword::IN) => {
1063            let in_span = parser.consume_keyword(Keyword::IN)?;
1064            let values = parse_partition_bound_exprs(parser)?;
1065            Ok(PartitionBoundSpec::In { in_span, values })
1066        }
1067        Token::Ident(_, Keyword::FROM) => {
1068            let from_span = parser.consume_keyword(Keyword::FROM)?;
1069            let from_values = parse_partition_bound_exprs(parser)?;
1070            let to_span = parser.consume_keyword(Keyword::TO)?;
1071            let to_values = parse_partition_bound_exprs(parser)?;
1072            Ok(PartitionBoundSpec::FromTo {
1073                from_span,
1074                from_values,
1075                to_span,
1076                to_values,
1077            })
1078        }
1079        Token::Ident(_, Keyword::WITH) => {
1080            let with_span = parser.consume_keyword(Keyword::WITH)?;
1081            parser.consume_token(Token::LParen)?;
1082            let modulus_span = parser.consume_keyword(Keyword::MODULUS)?;
1083            let modulus = parser.consume_int::<u64>()?;
1084            parser.consume_token(Token::Comma)?;
1085            let remainder_span = parser.consume_keyword(Keyword::REMAINDER)?;
1086            let remainder = parser.consume_int::<u64>()?;
1087            parser.consume_token(Token::RParen)?;
1088            Ok(PartitionBoundSpec::WithModulusRemainder {
1089                with_span,
1090                modulus_span,
1091                modulus,
1092                remainder_span,
1093                remainder,
1094            })
1095        }
1096        _ => parser.expected_failure("IN, FROM, or WITH"),
1097    }
1098}
1099
1100pub(crate) fn parse_create_table<'a>(
1101    parser: &mut Parser<'a, '_>,
1102    create_span: Span,
1103    create_options: Vec<CreateOption<'a>>,
1104    table_span: Span,
1105    if_not_exists: Option<Span>,
1106    identifier: QualifiedName<'a>,
1107) -> Result<CreateTable<'a>, ParseError> {
1108    parser.consume_token(Token::LParen)?;
1109
1110    let mut create_definitions = Vec::new();
1111    if !matches!(parser.token, Token::RParen) {
1112        loop {
1113            parser.recovered(
1114                "')' or ','",
1115                &|t| matches!(t, Token::RParen | Token::Comma),
1116                |parser| {
1117                    create_definitions.push(parse_create_definition(parser)?);
1118                    Ok(())
1119                },
1120            )?;
1121            if matches!(parser.token, Token::RParen) {
1122                break;
1123            }
1124            parser.consume_token(Token::Comma)?;
1125        }
1126    }
1127    let r_paren_span = parser.consume_token(Token::RParen)?;
1128
1129    let mut options = Vec::new();
1130    let mut table_as: Option<CreateTableAs<'_>> = None;
1131    let mut partition_by: Option<PartitionBy<'_>> = None;
1132    let delimiter_name = parser.lexer.delimiter_name();
1133    parser.recovered(
1134        delimiter_name,
1135        &|t| t == &Token::Eof || t == &Token::Delimiter,
1136        |parser| {
1137            loop {
1138                match &parser.token {
1139                    Token::Ident(_, Keyword::ENGINE) => {
1140                        let identifier = parser.consume_keyword(Keyword::ENGINE)?;
1141                        parser.skip_token(Token::Eq);
1142                        options.push(TableOption::Engine {
1143                            identifier,
1144                            value: parser.consume_plain_identifier_unreserved()?,
1145                        });
1146                    }
1147                    Token::Ident(_, Keyword::DEFAULT) => {
1148                        let default_span = parser.consume_keyword(Keyword::DEFAULT)?;
1149                        match &parser.token {
1150                            Token::Ident(_, Keyword::CHARSET) => {
1151                                let identifier = default_span
1152                                    .join_span(&parser.consume_keyword(Keyword::CHARSET)?);
1153                                parser.skip_token(Token::Eq);
1154                                options.push(TableOption::DefaultCharSet {
1155                                    identifier,
1156                                    value: parser.consume_plain_identifier_unreserved()?,
1157                                });
1158                            }
1159                            Token::Ident(_, Keyword::COLLATE) => {
1160                                let identifier = default_span
1161                                    .join_span(&parser.consume_keyword(Keyword::COLLATE)?);
1162                                parser.skip_token(Token::Eq);
1163                                options.push(TableOption::DefaultCollate {
1164                                    identifier,
1165                                    value: parser.consume_plain_identifier_unreserved()?,
1166                                });
1167                            }
1168                            _ => parser.expected_failure("'CHARSET' or 'COLLATE'")?,
1169                        }
1170                    }
1171                    Token::Ident(_, Keyword::CHARSET) => {
1172                        let identifier = parser.consume_keyword(Keyword::CHARSET)?;
1173                        parser.skip_token(Token::Eq);
1174                        options.push(TableOption::CharSet {
1175                            identifier,
1176                            value: parser.consume_plain_identifier_unreserved()?,
1177                        });
1178                    }
1179                    Token::Ident(_, Keyword::COLLATE) => {
1180                        let identifier = parser.consume_keyword(Keyword::COLLATE)?;
1181                        parser.skip_token(Token::Eq);
1182                        options.push(TableOption::Collate {
1183                            identifier,
1184                            value: parser.consume_plain_identifier_unreserved()?,
1185                        });
1186                    }
1187                    Token::Ident(_, Keyword::ROW_FORMAT) => {
1188                        let identifier = parser.consume_keyword(Keyword::ROW_FORMAT)?;
1189                        parser.skip_token(Token::Eq);
1190                        options.push(TableOption::RowFormat {
1191                            identifier,
1192                            value: parser.consume_plain_identifier_unreserved()?,
1193                        });
1194                        //TODO validate raw format is in the keyword set
1195                    }
1196                    Token::Ident(_, Keyword::KEY_BLOCK_SIZE) => {
1197                        let identifier = parser.consume_keywords(&[Keyword::KEY_BLOCK_SIZE])?;
1198                        parser.skip_token(Token::Eq);
1199                        options.push(TableOption::KeyBlockSize {
1200                            identifier,
1201                            value: parser.consume_int()?,
1202                        });
1203                    }
1204                    Token::Ident(_, Keyword::COMMENT) => {
1205                        let identifier = parser.consume_keyword(Keyword::COMMENT)?;
1206                        parser.skip_token(Token::Eq);
1207                        options.push(TableOption::Comment {
1208                            identifier,
1209                            value: parser.consume_string()?,
1210                        });
1211                    }
1212                    Token::Ident(_, Keyword::STRICT) => {
1213                        let identifier = parser.consume_keyword(Keyword::STRICT)?;
1214                        options.push(TableOption::Strict { identifier });
1215                    }
1216                    Token::Ident(_, Keyword::AUTO_INCREMENT) => {
1217                        let identifier = parser.consume_keyword(Keyword::AUTO_INCREMENT)?;
1218                        parser.skip_token(Token::Eq);
1219                        options.push(TableOption::AutoIncrement {
1220                            identifier,
1221                            value: parser.consume_int()?,
1222                        });
1223                    }
1224                    Token::Ident(_, Keyword::DATA) => {
1225                        let identifier =
1226                            parser.consume_keywords(&[Keyword::DATA, Keyword::DIRECTORY])?;
1227                        parser.skip_token(Token::Eq);
1228                        options.push(TableOption::DataDirectory {
1229                            identifier,
1230                            value: parser.consume_string()?,
1231                        });
1232                    }
1233                    Token::Ident(_, Keyword::INDEX) => {
1234                        let identifier =
1235                            parser.consume_keywords(&[Keyword::INDEX, Keyword::DIRECTORY])?;
1236                        parser.skip_token(Token::Eq);
1237                        options.push(TableOption::IndexDirectory {
1238                            identifier,
1239                            value: parser.consume_string()?,
1240                        });
1241                    }
1242                    Token::Ident(_, Keyword::INSERT_METHOD) => {
1243                        let identifier = parser.consume_keyword(Keyword::INSERT_METHOD)?;
1244                        parser.skip_token(Token::Eq);
1245                        options.push(TableOption::InsertMethod {
1246                            identifier,
1247                            value: parser.consume_plain_identifier_unreserved()?,
1248                        });
1249                    }
1250                    Token::Ident(_, Keyword::PACK_KEYS) => {
1251                        let identifier = parser.consume_keyword(Keyword::PACK_KEYS)?;
1252                        parser.skip_token(Token::Eq);
1253                        options.push(TableOption::PackKeys {
1254                            identifier,
1255                            value: parser.consume_int()?,
1256                        });
1257                    }
1258                    Token::Ident(_, Keyword::STATS_AUTO_RECALC) => {
1259                        let identifier = parser.consume_keyword(Keyword::STATS_AUTO_RECALC)?;
1260                        parser.skip_token(Token::Eq);
1261                        options.push(TableOption::StatsAutoRecalc {
1262                            identifier,
1263                            value: parser.consume_int()?,
1264                        });
1265                    }
1266                    Token::Ident(_, Keyword::STATS_PERSISTENT) => {
1267                        let identifier = parser.consume_keyword(Keyword::STATS_PERSISTENT)?;
1268                        parser.skip_token(Token::Eq);
1269                        options.push(TableOption::StatsPersistent {
1270                            identifier,
1271                            value: parser.consume_int()?,
1272                        });
1273                    }
1274                    Token::Ident(_, Keyword::STATS_SAMPLE_PAGES) => {
1275                        let identifier = parser.consume_keyword(Keyword::STATS_SAMPLE_PAGES)?;
1276                        parser.skip_token(Token::Eq);
1277                        options.push(TableOption::StatsSamplePages {
1278                            identifier,
1279                            value: parser.consume_int()?,
1280                        });
1281                    }
1282                    Token::Ident(_, Keyword::DELAY_KEY_WRITE) => {
1283                        let identifier = parser.consume_keyword(Keyword::DELAY_KEY_WRITE)?;
1284                        parser.skip_token(Token::Eq);
1285                        let (val, span) = parser.consume_int::<usize>()?;
1286                        options.push(TableOption::DelayKeyWrite {
1287                            identifier,
1288                            value: (val != 0, span),
1289                        });
1290                    }
1291                    Token::Ident(_, Keyword::COMPRESSION) => {
1292                        let identifier = parser.consume_keyword(Keyword::COMPRESSION)?;
1293                        parser.skip_token(Token::Eq);
1294                        options.push(TableOption::Compression {
1295                            identifier,
1296                            value: parser.consume_string()?,
1297                        });
1298                    }
1299                    Token::Ident(_, Keyword::ENCRYPTION) => {
1300                        let identifier = parser.consume_keyword(Keyword::ENCRYPTION)?;
1301                        parser.skip_token(Token::Eq);
1302                        // ENCRYPTION can be 'Y'/'N' string or YES/NO keyword
1303                        let value = match &parser.token {
1304                            Token::String(..) => {
1305                                let s = parser.consume_string()?;
1306                                let is_yes = s.as_str().eq_ignore_ascii_case("y")
1307                                    || s.as_str().eq_ignore_ascii_case("yes");
1308                                (is_yes, s.span())
1309                            }
1310                            _ => {
1311                                let id = parser.consume_plain_identifier_unreserved()?;
1312                                let is_yes = id.value.eq_ignore_ascii_case("yes");
1313                                (is_yes, id.span())
1314                            }
1315                        };
1316                        options.push(TableOption::Encryption { identifier, value });
1317                    }
1318                    Token::Ident(_, Keyword::MAX_ROWS) => {
1319                        let identifier = parser.consume_keyword(Keyword::MAX_ROWS)?;
1320                        parser.skip_token(Token::Eq);
1321                        options.push(TableOption::MaxRows {
1322                            identifier,
1323                            value: parser.consume_int()?,
1324                        });
1325                    }
1326                    Token::Ident(_, Keyword::MIN_ROWS) => {
1327                        let identifier = parser.consume_keyword(Keyword::MIN_ROWS)?;
1328                        parser.skip_token(Token::Eq);
1329                        options.push(TableOption::MinRows {
1330                            identifier,
1331                            value: parser.consume_int()?,
1332                        });
1333                    }
1334                    Token::Ident(_, Keyword::AUTOEXTEND_SIZE) => {
1335                        let identifier = parser.consume_keyword(Keyword::AUTOEXTEND_SIZE)?;
1336                        parser.skip_token(Token::Eq);
1337                        options.push(TableOption::AutoExtendSize {
1338                            identifier,
1339                            value: parser.consume_int()?,
1340                        });
1341                    }
1342                    Token::Ident(_, Keyword::AVG_ROW_LENGTH) => {
1343                        let identifier = parser.consume_keyword(Keyword::AVG_ROW_LENGTH)?;
1344                        parser.skip_token(Token::Eq);
1345                        options.push(TableOption::AvgRowLength {
1346                            identifier,
1347                            value: parser.consume_int()?,
1348                        });
1349                    }
1350                    Token::Ident(_, Keyword::CHECKSUM) => {
1351                        let identifier = parser.consume_keyword(Keyword::CHECKSUM)?;
1352                        parser.skip_token(Token::Eq);
1353                        let (val, span) = parser.consume_int::<usize>()?;
1354                        options.push(TableOption::Checksum {
1355                            identifier,
1356                            value: (val != 0, span),
1357                        });
1358                    }
1359                    Token::Ident(_, Keyword::CONNECTION) => {
1360                        let identifier = parser.consume_keyword(Keyword::CONNECTION)?;
1361                        parser.skip_token(Token::Eq);
1362                        options.push(TableOption::Connection {
1363                            identifier,
1364                            value: parser.consume_string()?,
1365                        });
1366                    }
1367                    Token::Ident(_, Keyword::ENGINE_ATTRIBUTE) => {
1368                        let identifier = parser.consume_keyword(Keyword::ENGINE_ATTRIBUTE)?;
1369                        parser.skip_token(Token::Eq);
1370                        options.push(TableOption::EngineAttribute {
1371                            identifier,
1372                            value: parser.consume_string()?,
1373                        });
1374                    }
1375                    Token::Ident(_, Keyword::PASSWORD) => {
1376                        let identifier = parser.consume_keyword(Keyword::PASSWORD)?;
1377                        parser.skip_token(Token::Eq);
1378                        options.push(TableOption::Password {
1379                            identifier,
1380                            value: parser.consume_string()?,
1381                        });
1382                    }
1383                    Token::Ident(_, Keyword::SECONDARY_ENGINE_ATTRIBUTE) => {
1384                        let identifier =
1385                            parser.consume_keyword(Keyword::SECONDARY_ENGINE_ATTRIBUTE)?;
1386                        parser.skip_token(Token::Eq);
1387                        options.push(TableOption::SecondaryEngineAttribute {
1388                            identifier,
1389                            value: parser.consume_string()?,
1390                        });
1391                    }
1392                    Token::Ident(_, Keyword::START) => {
1393                        let identifier =
1394                            parser.consume_keywords(&[Keyword::START, Keyword::TRANSACTION])?;
1395                        options.push(TableOption::StartTransaction { identifier });
1396                    }
1397                    Token::Ident(_, Keyword::TABLESPACE) => {
1398                        let identifier = parser.consume_keyword(Keyword::TABLESPACE)?;
1399                        options.push(TableOption::Tablespace {
1400                            identifier,
1401                            value: parser.consume_plain_identifier_unreserved()?,
1402                        });
1403                    }
1404                    Token::Ident(_, Keyword::STORAGE) => {
1405                        let identifier = parser.consume_keyword(Keyword::STORAGE)?;
1406                        options.push(TableOption::Storage {
1407                            identifier,
1408                            value: parser.consume_plain_identifier_unreserved()?,
1409                        });
1410                    }
1411                    Token::Ident(_, Keyword::UNION) => {
1412                        let identifier = parser.consume_keyword(Keyword::UNION)?;
1413                        parser.skip_token(Token::Eq);
1414                        parser.consume_token(Token::LParen)?;
1415                        let mut tables = Vec::new();
1416                        loop {
1417                            tables.push(parser.consume_plain_identifier_unreserved()?);
1418                            if parser.skip_token(Token::Comma).is_none() {
1419                                break;
1420                            }
1421                        }
1422                        parser.consume_token(Token::RParen)?;
1423                        options.push(TableOption::Union {
1424                            identifier,
1425                            value: tables,
1426                        });
1427                    }
1428                    Token::Ident(_, Keyword::INHERITS) => {
1429                        let identifier = parser.consume_keyword(Keyword::INHERITS)?;
1430                        parser.postgres_only(&identifier);
1431                        parser.consume_token(Token::LParen)?;
1432                        let mut tables = Vec::new();
1433                        parser.recovered("')'", &|t| t == &Token::RParen, |parser| {
1434                            loop {
1435                                tables.push(parse_qualified_name_unreserved(parser)?);
1436                                if parser.skip_token(Token::Comma).is_none() {
1437                                    break;
1438                                }
1439                            }
1440                            Ok(())
1441                        })?;
1442                        parser.consume_token(Token::RParen)?;
1443                        options.push(TableOption::Inherits {
1444                            identifier,
1445                            value: tables,
1446                        });
1447                    }
1448                    Token::Ident(_, Keyword::WITH) => {
1449                        let identifier = parser.consume_keyword(Keyword::WITH)?;
1450                        parser.postgres_only(&identifier);
1451                        parser.consume_token(Token::LParen)?;
1452                        let mut params = Vec::new();
1453                        parser.recovered("')'", &|t| t == &Token::RParen, |parser| {
1454                            loop {
1455                                let key = parser.consume_plain_identifier_unreserved()?;
1456                                parser.consume_token(Token::Eq)?;
1457                                let val = parse_expression_unreserved(parser, PRIORITY_MAX)?;
1458                                params.push((key, val));
1459                                if parser.skip_token(Token::Comma).is_none() {
1460                                    break;
1461                                }
1462                            }
1463                            Ok(())
1464                        })?;
1465                        parser.consume_token(Token::RParen)?;
1466                        options.push(TableOption::WithOptions {
1467                            identifier,
1468                            options: params,
1469                        });
1470                    }
1471                    Token::Ident(_, Keyword::ON) => {
1472                        let identifier =
1473                            parser.consume_keywords(&[Keyword::ON, Keyword::COMMIT])?;
1474                        parser.postgres_only(&identifier);
1475                        let action = match &parser.token {
1476                            Token::Ident(_, Keyword::PRESERVE) => OnCommitAction::PreserveRows(
1477                                parser.consume_keywords(&[Keyword::PRESERVE, Keyword::ROWS])?,
1478                            ),
1479                            Token::Ident(_, Keyword::DELETE) => OnCommitAction::DeleteRows(
1480                                parser.consume_keywords(&[Keyword::DELETE, Keyword::ROWS])?,
1481                            ),
1482                            Token::Ident(_, Keyword::DROP) => {
1483                                OnCommitAction::Drop(parser.consume_keyword(Keyword::DROP)?)
1484                            }
1485                            _ => parser.expected_failure("PRESERVE ROWS, DELETE ROWS, or DROP")?,
1486                        };
1487                        options.push(TableOption::OnCommit { identifier, action });
1488                    }
1489                    Token::Ident(_, Keyword::PARTITION) => {
1490                        partition_by = Some(parse_partition_by(parser)?);
1491                    }
1492                    Token::Ident(_, Keyword::IGNORE)
1493                    | Token::Ident(_, Keyword::REPLACE)
1494                    | Token::Ident(_, Keyword::AS) => {
1495                        let ignore_span = parser.skip_keyword(Keyword::IGNORE);
1496                        let replace_span = parser.skip_keyword(Keyword::REPLACE);
1497                        let as_span = parser.consume_keyword(Keyword::AS)?;
1498
1499                        if let Some(table_as) = &table_as {
1500                            parser.err("Multiple AS clauses not supported", table_as);
1501                        }
1502
1503                        let query = parse_compound_query(parser)?;
1504                        table_as = Some(CreateTableAs {
1505                            as_span,
1506                            replace_span,
1507                            ignore_span,
1508                            query,
1509                        });
1510                    }
1511                    Token::Comma => {
1512                        parser.consume_token(Token::Comma)?;
1513                    }
1514                    Token::Delimiter => break,
1515                    Token::Eof => break,
1516                    _ => {
1517                        parser.expected_failure("table option or delimiter")?;
1518                    }
1519                }
1520            }
1521            Ok(())
1522        },
1523    )?;
1524
1525    Ok(CreateTable {
1526        create_span,
1527        create_options,
1528        table_span,
1529        identifier,
1530        if_not_exists,
1531        create_definitions,
1532        r_paren_span,
1533        options,
1534        table_as,
1535        partition_by,
1536    })
1537}
1538
1539fn parse_create_table_partition_of<'a>(
1540    parser: &mut Parser<'a, '_>,
1541    create_span: Span,
1542    create_options: Vec<CreateOption<'a>>,
1543    table_span: Span,
1544    if_not_exists: Option<Span>,
1545    identifier: QualifiedName<'a>,
1546) -> Result<CreateTablePartitionOf<'a>, ParseError> {
1547    let partition_span = parser.consume_keyword(Keyword::PARTITION)?;
1548    let of_span = parser.consume_keyword(Keyword::OF)?;
1549    let partition_of_span = partition_span.join_span(&of_span);
1550
1551    let parent_table = parse_qualified_name_unreserved(parser)?;
1552
1553    // Optional column definitions / constraint overrides
1554    let mut create_definitions = Vec::new();
1555    if matches!(parser.token, Token::LParen) {
1556        parser.consume_token(Token::LParen)?;
1557        if !matches!(parser.token, Token::RParen) {
1558            loop {
1559                parser.recovered(
1560                    "')' or ','",
1561                    &|t| matches!(t, Token::RParen | Token::Comma),
1562                    |parser| {
1563                        create_definitions.push(parse_create_definition(parser)?);
1564                        Ok(())
1565                    },
1566                )?;
1567                if matches!(parser.token, Token::RParen) {
1568                    break;
1569                }
1570                parser.consume_token(Token::Comma)?;
1571            }
1572        }
1573        parser.consume_token(Token::RParen)?;
1574    }
1575
1576    // FOR VALUES … | DEFAULT
1577    let bound = match &parser.token {
1578        Token::Ident(_, Keyword::DEFAULT) => {
1579            PartitionOfBound::Default(parser.consume_keyword(Keyword::DEFAULT)?)
1580        }
1581        Token::Ident(_, Keyword::FOR) => {
1582            let for_values_span = parser.consume_keywords(&[Keyword::FOR, Keyword::VALUES])?;
1583            let spec = parse_partition_bound_spec(parser)?;
1584            PartitionOfBound::ForValues {
1585                for_values_span,
1586                spec,
1587            }
1588        }
1589        _ => parser.expected_failure("FOR VALUES or DEFAULT")?,
1590    };
1591
1592    // Optional sub-partitioning
1593    let partition_by = if matches!(parser.token, Token::Ident(_, Keyword::PARTITION)) {
1594        Some(parse_partition_by(parser)?)
1595    } else {
1596        None
1597    };
1598
1599    Ok(CreateTablePartitionOf {
1600        create_span,
1601        create_options,
1602        table_span,
1603        if_not_exists,
1604        identifier,
1605        partition_of_span,
1606        parent_table,
1607        create_definitions,
1608        bound,
1609        partition_by,
1610    })
1611}
1612
1613/// Entry point for `CREATE TABLE` dispatching.
1614///
1615/// Parses TABLE + optional IF NOT EXISTS + table identifier, then dispatches to either
1616/// `parse_create_table_partition_of` (for `PARTITION OF`) or `parse_create_table`.
1617pub(crate) fn parse_create_table_or_partition_of<'a>(
1618    parser: &mut Parser<'a, '_>,
1619    create_span: Span,
1620    create_options: Vec<CreateOption<'a>>,
1621) -> Result<Statement<'a>, ParseError> {
1622    let table_span = parser.consume_keyword(Keyword::TABLE)?;
1623
1624    let if_not_exists = if let Some(if_) = parser.skip_keyword(Keyword::IF) {
1625        Some(
1626            if_.start
1627                ..parser
1628                    .consume_keywords(&[Keyword::NOT, Keyword::EXISTS])?
1629                    .end,
1630        )
1631    } else {
1632        None
1633    };
1634    let identifier = parse_qualified_name_unreserved(parser)?;
1635
1636    if matches!(parser.token, Token::Ident(_, Keyword::PARTITION)) {
1637        Ok(Statement::CreateTablePartitionOf(Box::new(
1638            parse_create_table_partition_of(
1639                parser,
1640                create_span,
1641                create_options,
1642                table_span,
1643                if_not_exists,
1644                identifier,
1645            )?,
1646        )))
1647    } else if matches!(parser.token, Token::Ident(_, Keyword::AS))
1648        && !matches!(parser.peek(), Token::LParen)
1649    {
1650        // CREATE TABLE foo AS SELECT ... (CTAS — no column list)
1651        let as_span = parser.consume_keyword(Keyword::AS)?;
1652        let query = parse_compound_query(parser)?;
1653        Ok(Statement::CreateTable(Box::new(CreateTable {
1654            create_span: create_span.clone(),
1655            create_options,
1656            table_span,
1657            identifier,
1658            if_not_exists,
1659            create_definitions: alloc::vec![],
1660            r_paren_span: create_span.clone(),
1661            options: alloc::vec![],
1662            table_as: Some(CreateTableAs {
1663                as_span,
1664                replace_span: None,
1665                ignore_span: None,
1666                query,
1667            }),
1668            partition_by: None,
1669        })))
1670    } else {
1671        Ok(Statement::CreateTable(Box::new(parse_create_table(
1672            parser,
1673            create_span,
1674            create_options,
1675            table_span,
1676            if_not_exists,
1677            identifier,
1678        )?)))
1679    }
1680}