Skip to content

Commit 7e2833e

Browse files
Accept CREATE SEQUENCE options in any order
1 parent 2f3b5b8 commit 7e2833e

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")]
@@ -9791,7 +9792,7 @@ impl<'a> Parser<'a> {
97919792
if self.parse_keywords(&[Keyword::ALWAYS, Keyword::AS, Keyword::IDENTITY]) {
97929793
let mut sequence_options = vec![];
97939794
if self.expect_token(&Token::LParen).is_ok() {
9794-
sequence_options = self.parse_create_sequence_options()?;
9795+
sequence_options = self.parse_sequence_options(false)?;
97959796
self.expect_token(&Token::RParen)?;
97969797
}
97979798
Ok(Some(ColumnOption::Generated {
@@ -9809,7 +9810,7 @@ impl<'a> Parser<'a> {
98099810
]) {
98109811
let mut sequence_options = vec![];
98119812
if self.expect_token(&Token::LParen).is_ok() {
9812-
sequence_options = self.parse_create_sequence_options()?;
9813+
sequence_options = self.parse_sequence_options(false)?;
98139814
self.expect_token(&Token::RParen)?;
98149815
}
98159816
Ok(Some(ColumnOption::Generated {
@@ -10945,7 +10946,7 @@ impl<'a> Parser<'a> {
1094510946

1094610947
if self.peek_token_ref().token == Token::LParen {
1094710948
self.expect_token(&Token::LParen)?;
10948-
sequence_options = Some(self.parse_create_sequence_options()?);
10949+
sequence_options = Some(self.parse_sequence_options(false)?);
1094910950
self.expect_token(&Token::RParen)?;
1095010951
}
1095110952

@@ -20256,72 +20257,88 @@ impl<'a> Parser<'a> {
2025620257
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
2025720258
//name
2025820259
let name = self.parse_object_name(false)?;
20259-
//[ AS data_type ]
20260-
let mut data_type: Option<DataType> = None;
20261-
if self.parse_keywords(&[Keyword::AS]) {
20262-
data_type = Some(self.parse_data_type()?)
20263-
}
20264-
let sequence_options = self.parse_create_sequence_options()?;
20265-
// [ OWNED BY { table_name.column_name | NONE } ]
20266-
let owned_by = if self.parse_keywords(&[Keyword::OWNED, Keyword::BY]) {
20267-
if self.parse_keywords(&[Keyword::NONE]) {
20268-
Some(ObjectName::from(vec![Ident::new("NONE")]))
20269-
} else {
20270-
Some(self.parse_object_name(false)?)
20271-
}
20272-
} else {
20273-
None
20274-
};
20260+
let sequence_options = self.parse_sequence_options(true)?;
2027520261
Ok(Statement::CreateSequence {
2027620262
temporary,
2027720263
if_not_exists,
2027820264
name,
20279-
data_type,
2028020265
sequence_options,
20281-
owned_by,
2028220266
})
2028320267
}
2028420268

20285-
fn parse_create_sequence_options(&mut self) -> Result<Vec<SequenceOptions>, ParserError> {
20286-
let mut sequence_options = vec![];
20287-
//[ INCREMENT [ BY ] increment ]
20288-
if self.parse_keywords(&[Keyword::INCREMENT]) {
20289-
if self.parse_keywords(&[Keyword::BY]) {
20290-
sequence_options.push(SequenceOptions::IncrementBy(self.parse_number()?, true));
20269+
/// Parse the sequence options shared by `CREATE SEQUENCE` and identity
20270+
/// columns. The options form an unordered list, each allowed at most once.
20271+
///
20272+
/// `AS <data_type>` and `OWNED BY` are options of `CREATE SEQUENCE` only, so
20273+
/// `allow_type_and_owner` gates them off for an identity column.
20274+
fn parse_sequence_options(
20275+
&mut self,
20276+
allow_type_and_owner: bool,
20277+
) -> Result<Vec<SequenceOptions>, ParserError> {
20278+
let mut sequence_options: Vec<SequenceOptions> = vec![];
20279+
loop {
20280+
let (option, name) = if self.parse_keyword(Keyword::INCREMENT) {
20281+
//[ INCREMENT [ BY ] increment ]
20282+
let by = self.parse_keyword(Keyword::BY);
20283+
(
20284+
SequenceOptions::IncrementBy(self.parse_number()?, by),
20285+
"INCREMENT",
20286+
)
20287+
} else if self.parse_keyword(Keyword::MINVALUE) {
20288+
//[ MINVALUE minvalue | NO MINVALUE ]
20289+
(
20290+
SequenceOptions::MinValue(Some(self.parse_number()?)),
20291+
"MINVALUE | NO MINVALUE",
20292+
)
20293+
} else if self.parse_keywords(&[Keyword::NO, Keyword::MINVALUE]) {
20294+
(SequenceOptions::MinValue(None), "MINVALUE | NO MINVALUE")
20295+
} else if self.parse_keyword(Keyword::MAXVALUE) {
20296+
//[ MAXVALUE maxvalue | NO MAXVALUE ]
20297+
(
20298+
SequenceOptions::MaxValue(Some(self.parse_number()?)),
20299+
"MAXVALUE | NO MAXVALUE",
20300+
)
20301+
} else if self.parse_keywords(&[Keyword::NO, Keyword::MAXVALUE]) {
20302+
(SequenceOptions::MaxValue(None), "MAXVALUE | NO MAXVALUE")
20303+
} else if self.parse_keyword(Keyword::START) {
20304+
//[ START [ WITH ] start ]
20305+
let with = self.parse_keyword(Keyword::WITH);
20306+
(
20307+
SequenceOptions::StartWith(self.parse_number()?, with),
20308+
"START",
20309+
)
20310+
} else if self.parse_keyword(Keyword::CACHE) {
20311+
//[ CACHE cache ]
20312+
(SequenceOptions::Cache(self.parse_number()?), "CACHE")
20313+
} else if self.parse_keywords(&[Keyword::NO, Keyword::CYCLE]) {
20314+
// [ [ NO ] CYCLE ]
20315+
(SequenceOptions::Cycle(true), "CYCLE | NO CYCLE")
20316+
} else if self.parse_keyword(Keyword::CYCLE) {
20317+
(SequenceOptions::Cycle(false), "CYCLE | NO CYCLE")
20318+
} else if allow_type_and_owner && self.parse_keyword(Keyword::AS) {
20319+
//[ AS data_type ]
20320+
(SequenceOptions::DataType(self.parse_data_type()?), "AS")
20321+
} else if allow_type_and_owner && self.parse_keywords(&[Keyword::OWNED, Keyword::BY]) {
20322+
// [ OWNED BY { table_name.column_name | NONE } ]
20323+
let owner = if self.parse_keyword(Keyword::NONE) {
20324+
None
20325+
} else {
20326+
Some(self.parse_object_name(false)?)
20327+
};
20328+
(SequenceOptions::OwnedBy(owner), "OWNED BY")
2029120329
} else {
20292-
sequence_options.push(SequenceOptions::IncrementBy(self.parse_number()?, false));
20293-
}
20294-
}
20295-
//[ MINVALUE minvalue | NO MINVALUE ]
20296-
if self.parse_keyword(Keyword::MINVALUE) {
20297-
sequence_options.push(SequenceOptions::MinValue(Some(self.parse_number()?)));
20298-
} else if self.parse_keywords(&[Keyword::NO, Keyword::MINVALUE]) {
20299-
sequence_options.push(SequenceOptions::MinValue(None));
20300-
}
20301-
//[ MAXVALUE maxvalue | NO MAXVALUE ]
20302-
if self.parse_keywords(&[Keyword::MAXVALUE]) {
20303-
sequence_options.push(SequenceOptions::MaxValue(Some(self.parse_number()?)));
20304-
} else if self.parse_keywords(&[Keyword::NO, Keyword::MAXVALUE]) {
20305-
sequence_options.push(SequenceOptions::MaxValue(None));
20306-
}
20330+
break;
20331+
};
2030720332

20308-
//[ START [ WITH ] start ]
20309-
if self.parse_keywords(&[Keyword::START]) {
20310-
if self.parse_keywords(&[Keyword::WITH]) {
20311-
sequence_options.push(SequenceOptions::StartWith(self.parse_number()?, true));
20312-
} else {
20313-
sequence_options.push(SequenceOptions::StartWith(self.parse_number()?, false));
20333+
if sequence_options
20334+
.iter()
20335+
.any(|seen| discriminant(seen) == discriminant(&option))
20336+
{
20337+
return Err(ParserError::ParserError(format!(
20338+
"{name} specified more than once"
20339+
)));
2031420340
}
20315-
}
20316-
//[ CACHE cache ]
20317-
if self.parse_keywords(&[Keyword::CACHE]) {
20318-
sequence_options.push(SequenceOptions::Cache(self.parse_number()?));
20319-
}
20320-
// [ [ NO ] CYCLE ]
20321-
if self.parse_keywords(&[Keyword::NO, Keyword::CYCLE]) {
20322-
sequence_options.push(SequenceOptions::Cycle(true));
20323-
} else if self.parse_keywords(&[Keyword::CYCLE]) {
20324-
sequence_options.push(SequenceOptions::Cycle(false));
20341+
sequence_options.push(option);
2032520342
}
2032620343

2032720344
Ok(sequence_options)

tests/sqlparser_postgres.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9663,3 +9663,81 @@ fn parse_right_deep_join_chain() {
96639663
// NATURAL JOIN followed by a constrained join must stay left-associative.
96649664
pg().verified_stmt("SELECT * FROM t0 NATURAL JOIN t1 INNER JOIN t2 ON true");
96659665
}
9666+
9667+
#[test]
9668+
fn parse_create_sequence_options_in_any_order() {
9669+
// PostgreSQL treats the sequence options as an unordered list, so every
9670+
// permutation below is accepted and round-trips in the order written.
9671+
pg().verified_stmt("CREATE SEQUENCE sa START WITH 1 INCREMENT BY 1");
9672+
pg().verified_stmt("CREATE SEQUENCE sb INCREMENT BY 1 START WITH 1");
9673+
pg().verified_stmt("CREATE SEQUENCE sc CACHE 1 START WITH 1");
9674+
pg().verified_stmt("CREATE SEQUENCE sd CYCLE INCREMENT BY 2");
9675+
pg().verified_stmt("CREATE SEQUENCE se NO MAXVALUE NO MINVALUE NO CYCLE INCREMENT 1");
9676+
pg().verified_stmt(
9677+
"CREATE SEQUENCE sf AS INTEGER START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1",
9678+
);
9679+
// `AS` and `OWNED BY` are options too, so they interleave with the rest.
9680+
pg().verified_stmt("CREATE SEQUENCE sg START WITH 1 AS BIGINT");
9681+
pg().verified_stmt("CREATE SEQUENCE sh OWNED BY t.c START WITH 5");
9682+
pg().verified_stmt("CREATE SEQUENCE si OWNED BY NONE INCREMENT BY 2");
9683+
pg().verified_stmt("CREATE SEQUENCE sj CACHE 1 AS BIGINT OWNED BY a.b START WITH 3");
9684+
9685+
// This is what `pg_dump -s` emits for a table with a SERIAL column.
9686+
pg().one_statement_parses_to(
9687+
r#"CREATE SEQUENCE public.accounts_id_seq
9688+
AS integer
9689+
START WITH 1
9690+
INCREMENT BY 1
9691+
NO MINVALUE
9692+
NO MAXVALUE
9693+
CACHE 1"#,
9694+
"CREATE SEQUENCE public.accounts_id_seq AS INTEGER START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1",
9695+
);
9696+
9697+
// The parsed options keep the order they were written in, and `OWNED BY NONE`
9698+
// is distinct from being owned by something called `NONE`.
9699+
let Statement::CreateSequence {
9700+
sequence_options, ..
9701+
} = pg().verified_stmt("CREATE SEQUENCE sk CACHE 1 OWNED BY NONE AS BIGINT INCREMENT BY 3")
9702+
else {
9703+
panic!("expected a CREATE SEQUENCE statement");
9704+
};
9705+
assert!(matches!(
9706+
sequence_options.as_slice(),
9707+
[
9708+
SequenceOptions::Cache(_),
9709+
SequenceOptions::OwnedBy(None),
9710+
SequenceOptions::DataType(DataType::BigInt(None)),
9711+
SequenceOptions::IncrementBy(_, true),
9712+
]
9713+
));
9714+
9715+
// Each option may still appear only once.
9716+
for (sql, expected) in [
9717+
(
9718+
"CREATE SEQUENCE s START WITH 1 START 2",
9719+
"START specified more than once",
9720+
),
9721+
(
9722+
"CREATE SEQUENCE s MINVALUE 1 NO MINVALUE",
9723+
"MINVALUE | NO MINVALUE specified more than once",
9724+
),
9725+
(
9726+
"CREATE SEQUENCE s CYCLE NO CYCLE",
9727+
"CYCLE | NO CYCLE specified more than once",
9728+
),
9729+
(
9730+
"CREATE SEQUENCE s AS INT AS BIGINT",
9731+
"AS specified more than once",
9732+
),
9733+
(
9734+
"CREATE SEQUENCE s OWNED BY a.b OWNED BY NONE",
9735+
"OWNED BY specified more than once",
9736+
),
9737+
] {
9738+
assert_eq!(
9739+
pg().parse_sql_statements(sql).unwrap_err(),
9740+
ParserError::ParserError(expected.to_string()),
9741+
);
9742+
}
9743+
}

0 commit comments

Comments
 (0)