Skip to content

Commit f131023

Browse files
Accept CREATE SEQUENCE options in any order
1 parent d398304 commit f131023

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
@@ -4787,12 +4787,8 @@ pub enum Statement {
47874787
if_not_exists: bool,
47884788
/// Sequence name.
47894789
name: ObjectName,
4790-
/// Optional data type for the sequence.
4791-
data_type: Option<DataType>,
4792-
/// Sequence options (INCREMENT, MINVALUE, etc.).
4790+
/// Sequence options, in the order they were written.
47934791
sequence_options: Vec<SequenceOptions>,
4794-
/// Optional `OWNED BY` target.
4795-
owned_by: Option<ObjectName>,
47964792
},
47974793
/// A `CREATE DOMAIN` statement.
47984794
CreateDomain(CreateDomain),
@@ -6236,32 +6232,19 @@ impl fmt::Display for Statement {
62366232
temporary,
62376233
if_not_exists,
62386234
name,
6239-
data_type,
62406235
sequence_options,
6241-
owned_by,
62426236
} => {
6243-
let as_type: String = if let Some(dt) = data_type.as_ref() {
6244-
//Cannot use format!(" AS {}", dt), due to format! is not available in --target thumbv6m-none-eabi
6245-
// " AS ".to_owned() + &dt.to_string()
6246-
[" AS ", &dt.to_string()].concat()
6247-
} else {
6248-
"".to_string()
6249-
};
62506237
write!(
62516238
f,
6252-
"CREATE {temporary}SEQUENCE {if_not_exists}{name}{as_type}",
6239+
"CREATE {temporary}SEQUENCE {if_not_exists}{name}",
62536240
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
62546241
temporary = if *temporary { "TEMPORARY " } else { "" },
62556242
name = name,
6256-
as_type = as_type
62576243
)?;
62586244
for sequence_option in sequence_options {
62596245
write!(f, "{sequence_option}")?;
62606246
}
6261-
if let Some(ob) = owned_by.as_ref() {
6262-
write!(f, " OWNED BY {ob}")?;
6263-
}
6264-
write!(f, "")
6247+
Ok(())
62656248
}
62666249
Statement::CreateStage {
62676250
or_replace,
@@ -6547,14 +6530,21 @@ impl fmt::Display for Statement {
65476530

65486531
/// Can use to describe options in create sequence or table column type identity
65496532
/// ```sql
6550-
/// [ INCREMENT [ BY ] increment ]
6533+
/// [ AS data_type ] [ INCREMENT [ BY ] increment ]
65516534
/// [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
65526535
/// [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ]
6536+
/// [ OWNED BY { table_name.column_name | NONE } ]
65536537
/// ```
6538+
///
6539+
/// The options form an unordered list, so they are stored in the order written.
6540+
/// `AS` and `OWNED BY` are only accepted by `CREATE SEQUENCE`, not by an
6541+
/// identity column.
65546542
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
65556543
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
65566544
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
65576545
pub enum SequenceOptions {
6546+
/// `AS <data_type>` option.
6547+
DataType(DataType),
65586548
/// `INCREMENT [BY] <expr>` option; second value indicates presence of `BY` keyword.
65596549
IncrementBy(Expr, bool),
65606550
/// `MINVALUE <expr>` or `NO MINVALUE`.
@@ -6567,11 +6557,16 @@ pub enum SequenceOptions {
65676557
Cache(Expr),
65686558
/// `CYCLE` or `NO CYCLE` option.
65696559
Cycle(bool),
6560+
/// `OWNED BY <object_name>`, or `OWNED BY NONE` when no target is given.
6561+
OwnedBy(Option<ObjectName>),
65706562
}
65716563

65726564
impl fmt::Display for SequenceOptions {
65736565
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65746566
match self {
6567+
SequenceOptions::DataType(data_type) => {
6568+
write!(f, " AS {data_type}")
6569+
}
65756570
SequenceOptions::IncrementBy(increment, by) => {
65766571
write!(
65776572
f,
@@ -6606,6 +6601,12 @@ impl fmt::Display for SequenceOptions {
66066601
SequenceOptions::Cycle(no) => {
66076602
write!(f, " {}CYCLE", if *no { "NO " } else { "" })
66086603
}
6604+
SequenceOptions::OwnedBy(Some(object_name)) => {
6605+
write!(f, " OWNED BY {object_name}")
6606+
}
6607+
SequenceOptions::OwnedBy(None) => {
6608+
write!(f, " OWNED BY NONE")
6609+
}
66096610
}
66106611
}
66116612
}

‎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")]
@@ -9829,7 +9830,7 @@ impl<'a> Parser<'a> {
98299830
if self.parse_keywords(&[Keyword::ALWAYS, Keyword::AS, Keyword::IDENTITY]) {
98309831
let mut sequence_options = vec![];
98319832
if self.expect_token(&Token::LParen).is_ok() {
9832-
sequence_options = self.parse_create_sequence_options()?;
9833+
sequence_options = self.parse_sequence_options(false)?;
98339834
self.expect_token(&Token::RParen)?;
98349835
}
98359836
Ok(Some(ColumnOption::Generated {
@@ -9847,7 +9848,7 @@ impl<'a> Parser<'a> {
98479848
]) {
98489849
let mut sequence_options = vec![];
98499850
if self.expect_token(&Token::LParen).is_ok() {
9850-
sequence_options = self.parse_create_sequence_options()?;
9851+
sequence_options = self.parse_sequence_options(false)?;
98519852
self.expect_token(&Token::RParen)?;
98529853
}
98539854
Ok(Some(ColumnOption::Generated {
@@ -10985,7 +10986,7 @@ impl<'a> Parser<'a> {
1098510986

1098610987
if self.peek_token_ref().token == Token::LParen {
1098710988
self.expect_token(&Token::LParen)?;
10988-
sequence_options = Some(self.parse_create_sequence_options()?);
10989+
sequence_options = Some(self.parse_sequence_options(false)?);
1098910990
self.expect_token(&Token::RParen)?;
1099010991
}
1099110992

@@ -20310,72 +20311,88 @@ impl<'a> Parser<'a> {
2031020311
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
2031120312
//name
2031220313
let name = self.parse_object_name(false)?;
20313-
//[ AS data_type ]
20314-
let mut data_type: Option<DataType> = None;
20315-
if self.parse_keywords(&[Keyword::AS]) {
20316-
data_type = Some(self.parse_data_type()?)
20317-
}
20318-
let sequence_options = self.parse_create_sequence_options()?;
20319-
// [ OWNED BY { table_name.column_name | NONE } ]
20320-
let owned_by = if self.parse_keywords(&[Keyword::OWNED, Keyword::BY]) {
20321-
if self.parse_keywords(&[Keyword::NONE]) {
20322-
Some(ObjectName::from(vec![Ident::new("NONE")]))
20323-
} else {
20324-
Some(self.parse_object_name(false)?)
20325-
}
20326-
} else {
20327-
None
20328-
};
20314+
let sequence_options = self.parse_sequence_options(true)?;
2032920315
Ok(Statement::CreateSequence {
2033020316
temporary,
2033120317
if_not_exists,
2033220318
name,
20333-
data_type,
2033420319
sequence_options,
20335-
owned_by,
2033620320
})
2033720321
}
2033820322

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

20362-
//[ START [ WITH ] start ]
20363-
if self.parse_keywords(&[Keyword::START]) {
20364-
if self.parse_keywords(&[Keyword::WITH]) {
20365-
sequence_options.push(SequenceOptions::StartWith(self.parse_number()?, true));
20366-
} else {
20367-
sequence_options.push(SequenceOptions::StartWith(self.parse_number()?, false));
20387+
if sequence_options
20388+
.iter()
20389+
.any(|seen| discriminant(seen) == discriminant(&option))
20390+
{
20391+
return Err(ParserError::ParserError(format!(
20392+
"{name} specified more than once"
20393+
)));
2036820394
}
20369-
}
20370-
//[ CACHE cache ]
20371-
if self.parse_keywords(&[Keyword::CACHE]) {
20372-
sequence_options.push(SequenceOptions::Cache(self.parse_number()?));
20373-
}
20374-
// [ [ NO ] CYCLE ]
20375-
if self.parse_keywords(&[Keyword::NO, Keyword::CYCLE]) {
20376-
sequence_options.push(SequenceOptions::Cycle(true));
20377-
} else if self.parse_keywords(&[Keyword::CYCLE]) {
20378-
sequence_options.push(SequenceOptions::Cycle(false));
20395+
sequence_options.push(option);
2037920396
}
2038020397

2038120398
Ok(sequence_options)

‎tests/sqlparser_postgres.rs‎

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10022,3 +10022,81 @@ fn parse_pg_roots_render_apart_from_operand() {
1002210022
pg().verified_stmt("SELECT ||/ -2");
1002310023
pg().verified_stmt("SELECT |/ ||/ 2");
1002410024
}
10025+
10026+
#[test]
10027+
fn parse_create_sequence_options_in_any_order() {
10028+
// PostgreSQL treats the sequence options as an unordered list, so every
10029+
// permutation below is accepted and round-trips in the order written.
10030+
pg().verified_stmt("CREATE SEQUENCE sa START WITH 1 INCREMENT BY 1");
10031+
pg().verified_stmt("CREATE SEQUENCE sb INCREMENT BY 1 START WITH 1");
10032+
pg().verified_stmt("CREATE SEQUENCE sc CACHE 1 START WITH 1");
10033+
pg().verified_stmt("CREATE SEQUENCE sd CYCLE INCREMENT BY 2");
10034+
pg().verified_stmt("CREATE SEQUENCE se NO MAXVALUE NO MINVALUE NO CYCLE INCREMENT 1");
10035+
pg().verified_stmt(
10036+
"CREATE SEQUENCE sf AS INTEGER START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1",
10037+
);
10038+
// `AS` and `OWNED BY` are options too, so they interleave with the rest.
10039+
pg().verified_stmt("CREATE SEQUENCE sg START WITH 1 AS BIGINT");
10040+
pg().verified_stmt("CREATE SEQUENCE sh OWNED BY t.c START WITH 5");
10041+
pg().verified_stmt("CREATE SEQUENCE si OWNED BY NONE INCREMENT BY 2");
10042+
pg().verified_stmt("CREATE SEQUENCE sj CACHE 1 AS BIGINT OWNED BY a.b START WITH 3");
10043+
10044+
// This is what `pg_dump -s` emits for a table with a SERIAL column.
10045+
pg().one_statement_parses_to(
10046+
r#"CREATE SEQUENCE public.accounts_id_seq
10047+
AS integer
10048+
START WITH 1
10049+
INCREMENT BY 1
10050+
NO MINVALUE
10051+
NO MAXVALUE
10052+
CACHE 1"#,
10053+
"CREATE SEQUENCE public.accounts_id_seq AS INTEGER START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1",
10054+
);
10055+
10056+
// The parsed options keep the order they were written in, and `OWNED BY NONE`
10057+
// is distinct from being owned by something called `NONE`.
10058+
let Statement::CreateSequence {
10059+
sequence_options, ..
10060+
} = pg().verified_stmt("CREATE SEQUENCE sk CACHE 1 OWNED BY NONE AS BIGINT INCREMENT BY 3")
10061+
else {
10062+
panic!("expected a CREATE SEQUENCE statement");
10063+
};
10064+
assert!(matches!(
10065+
sequence_options.as_slice(),
10066+
[
10067+
SequenceOptions::Cache(_),
10068+
SequenceOptions::OwnedBy(None),
10069+
SequenceOptions::DataType(DataType::BigInt(None)),
10070+
SequenceOptions::IncrementBy(_, true),
10071+
]
10072+
));
10073+
10074+
// Each option may still appear only once.
10075+
for (sql, expected) in [
10076+
(
10077+
"CREATE SEQUENCE s START WITH 1 START 2",
10078+
"START specified more than once",
10079+
),
10080+
(
10081+
"CREATE SEQUENCE s MINVALUE 1 NO MINVALUE",
10082+
"MINVALUE | NO MINVALUE specified more than once",
10083+
),
10084+
(
10085+
"CREATE SEQUENCE s CYCLE NO CYCLE",
10086+
"CYCLE | NO CYCLE specified more than once",
10087+
),
10088+
(
10089+
"CREATE SEQUENCE s AS INT AS BIGINT",
10090+
"AS specified more than once",
10091+
),
10092+
(
10093+
"CREATE SEQUENCE s OWNED BY a.b OWNED BY NONE",
10094+
"OWNED BY specified more than once",
10095+
),
10096+
] {
10097+
assert_eq!(
10098+
pg().parse_sql_statements(sql).unwrap_err(),
10099+
ParserError::ParserError(expected.to_string()),
10100+
);
10101+
}
10102+
}

0 commit comments

Comments
 (0)