Skip to content

Commit b5f4edb

Browse files
Accept CREATE SEQUENCE options in any order
1 parent 777a166 commit b5f4edb

3 files changed

Lines changed: 175 additions & 79 deletions

File tree

src/ast/mod.rs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4759,12 +4759,8 @@ pub enum Statement {
47594759
if_not_exists: bool,
47604760
/// Sequence name.
47614761
name: ObjectName,
4762-
/// Optional data type for the sequence.
4763-
data_type: Option<DataType>,
4764-
/// Sequence options (INCREMENT, MINVALUE, etc.).
4762+
/// Sequence options, in the order they were written.
47654763
sequence_options: Vec<SequenceOptions>,
4766-
/// Optional `OWNED BY` target.
4767-
owned_by: Option<ObjectName>,
47684764
},
47694765
/// A `CREATE DOMAIN` statement.
47704766
CreateDomain(CreateDomain),
@@ -6208,32 +6204,19 @@ impl fmt::Display for Statement {
62086204
temporary,
62096205
if_not_exists,
62106206
name,
6211-
data_type,
62126207
sequence_options,
6213-
owned_by,
62146208
} => {
6215-
let as_type: String = if let Some(dt) = data_type.as_ref() {
6216-
//Cannot use format!(" AS {}", dt), due to format! is not available in --target thumbv6m-none-eabi
6217-
// " AS ".to_owned() + &dt.to_string()
6218-
[" AS ", &dt.to_string()].concat()
6219-
} else {
6220-
"".to_string()
6221-
};
62226209
write!(
62236210
f,
6224-
"CREATE {temporary}SEQUENCE {if_not_exists}{name}{as_type}",
6211+
"CREATE {temporary}SEQUENCE {if_not_exists}{name}",
62256212
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
62266213
temporary = if *temporary { "TEMPORARY " } else { "" },
62276214
name = name,
6228-
as_type = as_type
62296215
)?;
62306216
for sequence_option in sequence_options {
62316217
write!(f, "{sequence_option}")?;
62326218
}
6233-
if let Some(ob) = owned_by.as_ref() {
6234-
write!(f, " OWNED BY {ob}")?;
6235-
}
6236-
write!(f, "")
6219+
Ok(())
62376220
}
62386221
Statement::CreateStage {
62396222
or_replace,
@@ -6519,14 +6502,21 @@ impl fmt::Display for Statement {
65196502

65206503
/// Can use to describe options in create sequence or table column type identity
65216504
/// ```sql
6522-
/// [ INCREMENT [ BY ] increment ]
6505+
/// [ AS data_type ] [ INCREMENT [ BY ] increment ]
65236506
/// [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
65246507
/// [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ]
6508+
/// [ OWNED BY { table_name.column_name | NONE } ]
65256509
/// ```
6510+
///
6511+
/// The options form an unordered list, so they are stored in the order written.
6512+
/// `AS` and `OWNED BY` are only accepted by `CREATE SEQUENCE`, not by an
6513+
/// identity column.
65266514
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
65276515
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
65286516
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
65296517
pub enum SequenceOptions {
6518+
/// `AS <data_type>` option.
6519+
DataType(DataType),
65306520
/// `INCREMENT [BY] <expr>` option; second value indicates presence of `BY` keyword.
65316521
IncrementBy(Expr, bool),
65326522
/// `MINVALUE <expr>` or `NO MINVALUE`.
@@ -6539,11 +6529,16 @@ pub enum SequenceOptions {
65396529
Cache(Expr),
65406530
/// `CYCLE` or `NO CYCLE` option.
65416531
Cycle(bool),
6532+
/// `OWNED BY <object_name>`, or `OWNED BY NONE` when no target is given.
6533+
OwnedBy(Option<ObjectName>),
65426534
}
65436535

65446536
impl fmt::Display for SequenceOptions {
65456537
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65466538
match self {
6539+
SequenceOptions::DataType(data_type) => {
6540+
write!(f, " AS {data_type}")
6541+
}
65476542
SequenceOptions::IncrementBy(increment, by) => {
65486543
write!(
65496544
f,
@@ -6578,6 +6573,12 @@ impl fmt::Display for SequenceOptions {
65786573
SequenceOptions::Cycle(no) => {
65796574
write!(f, " {}CYCLE", if *no { "NO " } else { "" })
65806575
}
6576+
SequenceOptions::OwnedBy(Some(object_name)) => {
6577+
write!(f, " OWNED BY {object_name}")
6578+
}
6579+
SequenceOptions::OwnedBy(None) => {
6580+
write!(f, " OWNED BY NONE")
6581+
}
65816582
}
65826583
}
65836584
}

src/parser/mod.rs

Lines changed: 75 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use alloc::{
2323
};
2424
use core::{
2525
fmt::{self, Display},
26+
mem::discriminant,
2627
str::FromStr,
2728
};
2829
#[cfg(feature = "std")]
@@ -9819,7 +9820,7 @@ impl<'a> Parser<'a> {
98199820
if self.parse_keywords(&[Keyword::ALWAYS, Keyword::AS, Keyword::IDENTITY]) {
98209821
let mut sequence_options = vec![];
98219822
if self.expect_token(&Token::LParen).is_ok() {
9822-
sequence_options = self.parse_create_sequence_options()?;
9823+
sequence_options = self.parse_sequence_options(false)?;
98239824
self.expect_token(&Token::RParen)?;
98249825
}
98259826
Ok(Some(ColumnOption::Generated {
@@ -9837,7 +9838,7 @@ impl<'a> Parser<'a> {
98379838
]) {
98389839
let mut sequence_options = vec![];
98399840
if self.expect_token(&Token::LParen).is_ok() {
9840-
sequence_options = self.parse_create_sequence_options()?;
9841+
sequence_options = self.parse_sequence_options(false)?;
98419842
self.expect_token(&Token::RParen)?;
98429843
}
98439844
Ok(Some(ColumnOption::Generated {
@@ -10975,7 +10976,7 @@ impl<'a> Parser<'a> {
1097510976

1097610977
if self.peek_token_ref().token == Token::LParen {
1097710978
self.expect_token(&Token::LParen)?;
10978-
sequence_options = Some(self.parse_create_sequence_options()?);
10979+
sequence_options = Some(self.parse_sequence_options(false)?);
1097910980
self.expect_token(&Token::RParen)?;
1098010981
}
1098110982

@@ -20316,72 +20317,88 @@ impl<'a> Parser<'a> {
2031620317
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
2031720318
//name
2031820319
let name = self.parse_object_name(false)?;
20319-
//[ AS data_type ]
20320-
let mut data_type: Option<DataType> = None;
20321-
if self.parse_keywords(&[Keyword::AS]) {
20322-
data_type = Some(self.parse_data_type()?)
20323-
}
20324-
let sequence_options = self.parse_create_sequence_options()?;
20325-
// [ OWNED BY { table_name.column_name | NONE } ]
20326-
let owned_by = if self.parse_keywords(&[Keyword::OWNED, Keyword::BY]) {
20327-
if self.parse_keywords(&[Keyword::NONE]) {
20328-
Some(ObjectName::from(vec![Ident::new("NONE")]))
20329-
} else {
20330-
Some(self.parse_object_name(false)?)
20331-
}
20332-
} else {
20333-
None
20334-
};
20320+
let sequence_options = self.parse_sequence_options(true)?;
2033520321
Ok(Statement::CreateSequence {
2033620322
temporary,
2033720323
if_not_exists,
2033820324
name,
20339-
data_type,
2034020325
sequence_options,
20341-
owned_by,
2034220326
})
2034320327
}
2034420328

20345-
fn parse_create_sequence_options(&mut self) -> Result<Vec<SequenceOptions>, ParserError> {
20346-
let mut sequence_options = vec![];
20347-
//[ INCREMENT [ BY ] increment ]
20348-
if self.parse_keywords(&[Keyword::INCREMENT]) {
20349-
if self.parse_keywords(&[Keyword::BY]) {
20350-
sequence_options.push(SequenceOptions::IncrementBy(self.parse_number()?, true));
20329+
/// Parse the sequence options shared by `CREATE SEQUENCE` and identity
20330+
/// columns. The options form an unordered list, each allowed at most once.
20331+
///
20332+
/// `AS <data_type>` and `OWNED BY` are options of `CREATE SEQUENCE` only, so
20333+
/// `allow_type_and_owner` gates them off for an identity column.
20334+
fn parse_sequence_options(
20335+
&mut self,
20336+
allow_type_and_owner: bool,
20337+
) -> Result<Vec<SequenceOptions>, ParserError> {
20338+
let mut sequence_options: Vec<SequenceOptions> = vec![];
20339+
loop {
20340+
let (option, name) = if self.parse_keyword(Keyword::INCREMENT) {
20341+
//[ INCREMENT [ BY ] increment ]
20342+
let by = self.parse_keyword(Keyword::BY);
20343+
(
20344+
SequenceOptions::IncrementBy(self.parse_number()?, by),
20345+
"INCREMENT",
20346+
)
20347+
} else if self.parse_keyword(Keyword::MINVALUE) {
20348+
//[ MINVALUE minvalue | NO MINVALUE ]
20349+
(
20350+
SequenceOptions::MinValue(Some(self.parse_number()?)),
20351+
"MINVALUE | NO MINVALUE",
20352+
)
20353+
} else if self.parse_keywords(&[Keyword::NO, Keyword::MINVALUE]) {
20354+
(SequenceOptions::MinValue(None), "MINVALUE | NO MINVALUE")
20355+
} else if self.parse_keyword(Keyword::MAXVALUE) {
20356+
//[ MAXVALUE maxvalue | NO MAXVALUE ]
20357+
(
20358+
SequenceOptions::MaxValue(Some(self.parse_number()?)),
20359+
"MAXVALUE | NO MAXVALUE",
20360+
)
20361+
} else if self.parse_keywords(&[Keyword::NO, Keyword::MAXVALUE]) {
20362+
(SequenceOptions::MaxValue(None), "MAXVALUE | NO MAXVALUE")
20363+
} else if self.parse_keyword(Keyword::START) {
20364+
//[ START [ WITH ] start ]
20365+
let with = self.parse_keyword(Keyword::WITH);
20366+
(
20367+
SequenceOptions::StartWith(self.parse_number()?, with),
20368+
"START",
20369+
)
20370+
} else if self.parse_keyword(Keyword::CACHE) {
20371+
//[ CACHE cache ]
20372+
(SequenceOptions::Cache(self.parse_number()?), "CACHE")
20373+
} else if self.parse_keywords(&[Keyword::NO, Keyword::CYCLE]) {
20374+
// [ [ NO ] CYCLE ]
20375+
(SequenceOptions::Cycle(true), "CYCLE | NO CYCLE")
20376+
} else if self.parse_keyword(Keyword::CYCLE) {
20377+
(SequenceOptions::Cycle(false), "CYCLE | NO CYCLE")
20378+
} else if allow_type_and_owner && self.parse_keyword(Keyword::AS) {
20379+
//[ AS data_type ]
20380+
(SequenceOptions::DataType(self.parse_data_type()?), "AS")
20381+
} else if allow_type_and_owner && self.parse_keywords(&[Keyword::OWNED, Keyword::BY]) {
20382+
// [ OWNED BY { table_name.column_name | NONE } ]
20383+
let owner = if self.parse_keyword(Keyword::NONE) {
20384+
None
20385+
} else {
20386+
Some(self.parse_object_name(false)?)
20387+
};
20388+
(SequenceOptions::OwnedBy(owner), "OWNED BY")
2035120389
} else {
20352-
sequence_options.push(SequenceOptions::IncrementBy(self.parse_number()?, false));
20353-
}
20354-
}
20355-
//[ MINVALUE minvalue | NO MINVALUE ]
20356-
if self.parse_keyword(Keyword::MINVALUE) {
20357-
sequence_options.push(SequenceOptions::MinValue(Some(self.parse_number()?)));
20358-
} else if self.parse_keywords(&[Keyword::NO, Keyword::MINVALUE]) {
20359-
sequence_options.push(SequenceOptions::MinValue(None));
20360-
}
20361-
//[ MAXVALUE maxvalue | NO MAXVALUE ]
20362-
if self.parse_keywords(&[Keyword::MAXVALUE]) {
20363-
sequence_options.push(SequenceOptions::MaxValue(Some(self.parse_number()?)));
20364-
} else if self.parse_keywords(&[Keyword::NO, Keyword::MAXVALUE]) {
20365-
sequence_options.push(SequenceOptions::MaxValue(None));
20366-
}
20390+
break;
20391+
};
2036720392

20368-
//[ START [ WITH ] start ]
20369-
if self.parse_keywords(&[Keyword::START]) {
20370-
if self.parse_keywords(&[Keyword::WITH]) {
20371-
sequence_options.push(SequenceOptions::StartWith(self.parse_number()?, true));
20372-
} else {
20373-
sequence_options.push(SequenceOptions::StartWith(self.parse_number()?, false));
20393+
if sequence_options
20394+
.iter()
20395+
.any(|seen| discriminant(seen) == discriminant(&option))
20396+
{
20397+
return Err(ParserError::ParserError(format!(
20398+
"{name} specified more than once"
20399+
)));
2037420400
}
20375-
}
20376-
//[ CACHE cache ]
20377-
if self.parse_keywords(&[Keyword::CACHE]) {
20378-
sequence_options.push(SequenceOptions::Cache(self.parse_number()?));
20379-
}
20380-
// [ [ NO ] CYCLE ]
20381-
if self.parse_keywords(&[Keyword::NO, Keyword::CYCLE]) {
20382-
sequence_options.push(SequenceOptions::Cycle(true));
20383-
} else if self.parse_keywords(&[Keyword::CYCLE]) {
20384-
sequence_options.push(SequenceOptions::Cycle(false));
20401+
sequence_options.push(option);
2038520402
}
2038620403

2038720404
Ok(sequence_options)

tests/sqlparser_postgres.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9852,3 +9852,81 @@ fn parse_alter_table_constraint_check_no_inherit() {
98529852
}
98539853
pg_and_generic().verified_stmt("ALTER TABLE docs ADD CONSTRAINT c CHECK (id > 0) NO INHERIT");
98549854
}
9855+
9856+
#[test]
9857+
fn parse_create_sequence_options_in_any_order() {
9858+
// PostgreSQL treats the sequence options as an unordered list, so every
9859+
// permutation below is accepted and round-trips in the order written.
9860+
pg().verified_stmt("CREATE SEQUENCE sa START WITH 1 INCREMENT BY 1");
9861+
pg().verified_stmt("CREATE SEQUENCE sb INCREMENT BY 1 START WITH 1");
9862+
pg().verified_stmt("CREATE SEQUENCE sc CACHE 1 START WITH 1");
9863+
pg().verified_stmt("CREATE SEQUENCE sd CYCLE INCREMENT BY 2");
9864+
pg().verified_stmt("CREATE SEQUENCE se NO MAXVALUE NO MINVALUE NO CYCLE INCREMENT 1");
9865+
pg().verified_stmt(
9866+
"CREATE SEQUENCE sf AS INTEGER START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1",
9867+
);
9868+
// `AS` and `OWNED BY` are options too, so they interleave with the rest.
9869+
pg().verified_stmt("CREATE SEQUENCE sg START WITH 1 AS BIGINT");
9870+
pg().verified_stmt("CREATE SEQUENCE sh OWNED BY t.c START WITH 5");
9871+
pg().verified_stmt("CREATE SEQUENCE si OWNED BY NONE INCREMENT BY 2");
9872+
pg().verified_stmt("CREATE SEQUENCE sj CACHE 1 AS BIGINT OWNED BY a.b START WITH 3");
9873+
9874+
// This is what `pg_dump -s` emits for a table with a SERIAL column.
9875+
pg().one_statement_parses_to(
9876+
r#"CREATE SEQUENCE public.accounts_id_seq
9877+
AS integer
9878+
START WITH 1
9879+
INCREMENT BY 1
9880+
NO MINVALUE
9881+
NO MAXVALUE
9882+
CACHE 1"#,
9883+
"CREATE SEQUENCE public.accounts_id_seq AS INTEGER START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1",
9884+
);
9885+
9886+
// The parsed options keep the order they were written in, and `OWNED BY NONE`
9887+
// is distinct from being owned by something called `NONE`.
9888+
let Statement::CreateSequence {
9889+
sequence_options, ..
9890+
} = pg().verified_stmt("CREATE SEQUENCE sk CACHE 1 OWNED BY NONE AS BIGINT INCREMENT BY 3")
9891+
else {
9892+
panic!("expected a CREATE SEQUENCE statement");
9893+
};
9894+
assert!(matches!(
9895+
sequence_options.as_slice(),
9896+
[
9897+
SequenceOptions::Cache(_),
9898+
SequenceOptions::OwnedBy(None),
9899+
SequenceOptions::DataType(DataType::BigInt(None)),
9900+
SequenceOptions::IncrementBy(_, true),
9901+
]
9902+
));
9903+
9904+
// Each option may still appear only once.
9905+
for (sql, expected) in [
9906+
(
9907+
"CREATE SEQUENCE s START WITH 1 START 2",
9908+
"START specified more than once",
9909+
),
9910+
(
9911+
"CREATE SEQUENCE s MINVALUE 1 NO MINVALUE",
9912+
"MINVALUE | NO MINVALUE specified more than once",
9913+
),
9914+
(
9915+
"CREATE SEQUENCE s CYCLE NO CYCLE",
9916+
"CYCLE | NO CYCLE specified more than once",
9917+
),
9918+
(
9919+
"CREATE SEQUENCE s AS INT AS BIGINT",
9920+
"AS specified more than once",
9921+
),
9922+
(
9923+
"CREATE SEQUENCE s OWNED BY a.b OWNED BY NONE",
9924+
"OWNED BY specified more than once",
9925+
),
9926+
] {
9927+
assert_eq!(
9928+
pg().parse_sql_statements(sql).unwrap_err(),
9929+
ParserError::ParserError(expected.to_string()),
9930+
);
9931+
}
9932+
}

0 commit comments

Comments
 (0)