Skip to content

Commit 035561e

Browse files
Accept CREATE SEQUENCE options in any order
1 parent 1195239 commit 035561e

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
@@ -4762,12 +4762,8 @@ pub enum Statement {
47624762
if_not_exists: bool,
47634763
/// Sequence name.
47644764
name: ObjectName,
4765-
/// Optional data type for the sequence.
4766-
data_type: Option<DataType>,
4767-
/// Sequence options (INCREMENT, MINVALUE, etc.).
4765+
/// Sequence options, in the order they were written.
47684766
sequence_options: Vec<SequenceOptions>,
4769-
/// Optional `OWNED BY` target.
4770-
owned_by: Option<ObjectName>,
47714767
},
47724768
/// A `CREATE DOMAIN` statement.
47734769
CreateDomain(CreateDomain),
@@ -6211,32 +6207,19 @@ impl fmt::Display for Statement {
62116207
temporary,
62126208
if_not_exists,
62136209
name,
6214-
data_type,
62156210
sequence_options,
6216-
owned_by,
62176211
} => {
6218-
let as_type: String = if let Some(dt) = data_type.as_ref() {
6219-
//Cannot use format!(" AS {}", dt), due to format! is not available in --target thumbv6m-none-eabi
6220-
// " AS ".to_owned() + &dt.to_string()
6221-
[" AS ", &dt.to_string()].concat()
6222-
} else {
6223-
"".to_string()
6224-
};
62256212
write!(
62266213
f,
6227-
"CREATE {temporary}SEQUENCE {if_not_exists}{name}{as_type}",
6214+
"CREATE {temporary}SEQUENCE {if_not_exists}{name}",
62286215
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
62296216
temporary = if *temporary { "TEMPORARY " } else { "" },
62306217
name = name,
6231-
as_type = as_type
62326218
)?;
62336219
for sequence_option in sequence_options {
62346220
write!(f, "{sequence_option}")?;
62356221
}
6236-
if let Some(ob) = owned_by.as_ref() {
6237-
write!(f, " OWNED BY {ob}")?;
6238-
}
6239-
write!(f, "")
6222+
Ok(())
62406223
}
62416224
Statement::CreateStage {
62426225
or_replace,
@@ -6522,14 +6505,21 @@ impl fmt::Display for Statement {
65226505

65236506
/// Can use to describe options in create sequence or table column type identity
65246507
/// ```sql
6525-
/// [ INCREMENT [ BY ] increment ]
6508+
/// [ AS data_type ] [ INCREMENT [ BY ] increment ]
65266509
/// [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
65276510
/// [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ]
6511+
/// [ OWNED BY { table_name.column_name | NONE } ]
65286512
/// ```
6513+
///
6514+
/// The options form an unordered list, so they are stored in the order written.
6515+
/// `AS` and `OWNED BY` are only accepted by `CREATE SEQUENCE`, not by an
6516+
/// identity column.
65296517
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
65306518
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
65316519
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
65326520
pub enum SequenceOptions {
6521+
/// `AS <data_type>` option.
6522+
DataType(DataType),
65336523
/// `INCREMENT [BY] <expr>` option; second value indicates presence of `BY` keyword.
65346524
IncrementBy(Expr, bool),
65356525
/// `MINVALUE <expr>` or `NO MINVALUE`.
@@ -6542,11 +6532,16 @@ pub enum SequenceOptions {
65426532
Cache(Expr),
65436533
/// `CYCLE` or `NO CYCLE` option.
65446534
Cycle(bool),
6535+
/// `OWNED BY <object_name>`, or `OWNED BY NONE` when no target is given.
6536+
OwnedBy(Option<ObjectName>),
65456537
}
65466538

65476539
impl fmt::Display for SequenceOptions {
65486540
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65496541
match self {
6542+
SequenceOptions::DataType(data_type) => {
6543+
write!(f, " AS {data_type}")
6544+
}
65506545
SequenceOptions::IncrementBy(increment, by) => {
65516546
write!(
65526547
f,
@@ -6581,6 +6576,12 @@ impl fmt::Display for SequenceOptions {
65816576
SequenceOptions::Cycle(no) => {
65826577
write!(f, " {}CYCLE", if *no { "NO " } else { "" })
65836578
}
6579+
SequenceOptions::OwnedBy(Some(object_name)) => {
6580+
write!(f, " OWNED BY {object_name}")
6581+
}
6582+
SequenceOptions::OwnedBy(None) => {
6583+
write!(f, " OWNED BY NONE")
6584+
}
65846585
}
65856586
}
65866587
}

‎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

@@ -20338,72 +20339,88 @@ impl<'a> Parser<'a> {
2033820339
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
2033920340
//name
2034020341
let name = self.parse_object_name(false)?;
20341-
//[ AS data_type ]
20342-
let mut data_type: Option<DataType> = None;
20343-
if self.parse_keywords(&[Keyword::AS]) {
20344-
data_type = Some(self.parse_data_type()?)
20345-
}
20346-
let sequence_options = self.parse_create_sequence_options()?;
20347-
// [ OWNED BY { table_name.column_name | NONE } ]
20348-
let owned_by = if self.parse_keywords(&[Keyword::OWNED, Keyword::BY]) {
20349-
if self.parse_keywords(&[Keyword::NONE]) {
20350-
Some(ObjectName::from(vec![Ident::new("NONE")]))
20351-
} else {
20352-
Some(self.parse_object_name(false)?)
20353-
}
20354-
} else {
20355-
None
20356-
};
20342+
let sequence_options = self.parse_sequence_options(true)?;
2035720343
Ok(Statement::CreateSequence {
2035820344
temporary,
2035920345
if_not_exists,
2036020346
name,
20361-
data_type,
2036220347
sequence_options,
20363-
owned_by,
2036420348
})
2036520349
}
2036620350

20367-
fn parse_create_sequence_options(&mut self) -> Result<Vec<SequenceOptions>, ParserError> {
20368-
let mut sequence_options = vec![];
20369-
//[ INCREMENT [ BY ] increment ]
20370-
if self.parse_keywords(&[Keyword::INCREMENT]) {
20371-
if self.parse_keywords(&[Keyword::BY]) {
20372-
sequence_options.push(SequenceOptions::IncrementBy(self.parse_number()?, true));
20351+
/// Parse the sequence options shared by `CREATE SEQUENCE` and identity
20352+
/// columns. The options form an unordered list, each allowed at most once.
20353+
///
20354+
/// `AS <data_type>` and `OWNED BY` are options of `CREATE SEQUENCE` only, so
20355+
/// `allow_type_and_owner` gates them off for an identity column.
20356+
fn parse_sequence_options(
20357+
&mut self,
20358+
allow_type_and_owner: bool,
20359+
) -> Result<Vec<SequenceOptions>, ParserError> {
20360+
let mut sequence_options: Vec<SequenceOptions> = vec![];
20361+
loop {
20362+
let (option, name) = if self.parse_keyword(Keyword::INCREMENT) {
20363+
//[ INCREMENT [ BY ] increment ]
20364+
let by = self.parse_keyword(Keyword::BY);
20365+
(
20366+
SequenceOptions::IncrementBy(self.parse_number()?, by),
20367+
"INCREMENT",
20368+
)
20369+
} else if self.parse_keyword(Keyword::MINVALUE) {
20370+
//[ MINVALUE minvalue | NO MINVALUE ]
20371+
(
20372+
SequenceOptions::MinValue(Some(self.parse_number()?)),
20373+
"MINVALUE | NO MINVALUE",
20374+
)
20375+
} else if self.parse_keywords(&[Keyword::NO, Keyword::MINVALUE]) {
20376+
(SequenceOptions::MinValue(None), "MINVALUE | NO MINVALUE")
20377+
} else if self.parse_keyword(Keyword::MAXVALUE) {
20378+
//[ MAXVALUE maxvalue | NO MAXVALUE ]
20379+
(
20380+
SequenceOptions::MaxValue(Some(self.parse_number()?)),
20381+
"MAXVALUE | NO MAXVALUE",
20382+
)
20383+
} else if self.parse_keywords(&[Keyword::NO, Keyword::MAXVALUE]) {
20384+
(SequenceOptions::MaxValue(None), "MAXVALUE | NO MAXVALUE")
20385+
} else if self.parse_keyword(Keyword::START) {
20386+
//[ START [ WITH ] start ]
20387+
let with = self.parse_keyword(Keyword::WITH);
20388+
(
20389+
SequenceOptions::StartWith(self.parse_number()?, with),
20390+
"START",
20391+
)
20392+
} else if self.parse_keyword(Keyword::CACHE) {
20393+
//[ CACHE cache ]
20394+
(SequenceOptions::Cache(self.parse_number()?), "CACHE")
20395+
} else if self.parse_keywords(&[Keyword::NO, Keyword::CYCLE]) {
20396+
// [ [ NO ] CYCLE ]
20397+
(SequenceOptions::Cycle(true), "CYCLE | NO CYCLE")
20398+
} else if self.parse_keyword(Keyword::CYCLE) {
20399+
(SequenceOptions::Cycle(false), "CYCLE | NO CYCLE")
20400+
} else if allow_type_and_owner && self.parse_keyword(Keyword::AS) {
20401+
//[ AS data_type ]
20402+
(SequenceOptions::DataType(self.parse_data_type()?), "AS")
20403+
} else if allow_type_and_owner && self.parse_keywords(&[Keyword::OWNED, Keyword::BY]) {
20404+
// [ OWNED BY { table_name.column_name | NONE } ]
20405+
let owner = if self.parse_keyword(Keyword::NONE) {
20406+
None
20407+
} else {
20408+
Some(self.parse_object_name(false)?)
20409+
};
20410+
(SequenceOptions::OwnedBy(owner), "OWNED BY")
2037320411
} else {
20374-
sequence_options.push(SequenceOptions::IncrementBy(self.parse_number()?, false));
20375-
}
20376-
}
20377-
//[ MINVALUE minvalue | NO MINVALUE ]
20378-
if self.parse_keyword(Keyword::MINVALUE) {
20379-
sequence_options.push(SequenceOptions::MinValue(Some(self.parse_number()?)));
20380-
} else if self.parse_keywords(&[Keyword::NO, Keyword::MINVALUE]) {
20381-
sequence_options.push(SequenceOptions::MinValue(None));
20382-
}
20383-
//[ MAXVALUE maxvalue | NO MAXVALUE ]
20384-
if self.parse_keywords(&[Keyword::MAXVALUE]) {
20385-
sequence_options.push(SequenceOptions::MaxValue(Some(self.parse_number()?)));
20386-
} else if self.parse_keywords(&[Keyword::NO, Keyword::MAXVALUE]) {
20387-
sequence_options.push(SequenceOptions::MaxValue(None));
20388-
}
20412+
break;
20413+
};
2038920414

20390-
//[ START [ WITH ] start ]
20391-
if self.parse_keywords(&[Keyword::START]) {
20392-
if self.parse_keywords(&[Keyword::WITH]) {
20393-
sequence_options.push(SequenceOptions::StartWith(self.parse_number()?, true));
20394-
} else {
20395-
sequence_options.push(SequenceOptions::StartWith(self.parse_number()?, false));
20415+
if sequence_options
20416+
.iter()
20417+
.any(|seen| discriminant(seen) == discriminant(&option))
20418+
{
20419+
return Err(ParserError::ParserError(format!(
20420+
"{name} specified more than once"
20421+
)));
2039620422
}
20397-
}
20398-
//[ CACHE cache ]
20399-
if self.parse_keywords(&[Keyword::CACHE]) {
20400-
sequence_options.push(SequenceOptions::Cache(self.parse_number()?));
20401-
}
20402-
// [ [ NO ] CYCLE ]
20403-
if self.parse_keywords(&[Keyword::NO, Keyword::CYCLE]) {
20404-
sequence_options.push(SequenceOptions::Cycle(true));
20405-
} else if self.parse_keywords(&[Keyword::CYCLE]) {
20406-
sequence_options.push(SequenceOptions::Cycle(false));
20423+
sequence_options.push(option);
2040720424
}
2040820425

2040920426
Ok(sequence_options)

‎tests/sqlparser_postgres.rs‎

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9953,3 +9953,81 @@ fn parse_insert_by_name_keywords_as_table_and_alias() {
99539953
statement => panic!("Expected INSERT statement, got: {statement:?}"),
99549954
}
99559955
}
9956+
9957+
#[test]
9958+
fn parse_create_sequence_options_in_any_order() {
9959+
// PostgreSQL treats the sequence options as an unordered list, so every
9960+
// permutation below is accepted and round-trips in the order written.
9961+
pg().verified_stmt("CREATE SEQUENCE sa START WITH 1 INCREMENT BY 1");
9962+
pg().verified_stmt("CREATE SEQUENCE sb INCREMENT BY 1 START WITH 1");
9963+
pg().verified_stmt("CREATE SEQUENCE sc CACHE 1 START WITH 1");
9964+
pg().verified_stmt("CREATE SEQUENCE sd CYCLE INCREMENT BY 2");
9965+
pg().verified_stmt("CREATE SEQUENCE se NO MAXVALUE NO MINVALUE NO CYCLE INCREMENT 1");
9966+
pg().verified_stmt(
9967+
"CREATE SEQUENCE sf AS INTEGER START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1",
9968+
);
9969+
// `AS` and `OWNED BY` are options too, so they interleave with the rest.
9970+
pg().verified_stmt("CREATE SEQUENCE sg START WITH 1 AS BIGINT");
9971+
pg().verified_stmt("CREATE SEQUENCE sh OWNED BY t.c START WITH 5");
9972+
pg().verified_stmt("CREATE SEQUENCE si OWNED BY NONE INCREMENT BY 2");
9973+
pg().verified_stmt("CREATE SEQUENCE sj CACHE 1 AS BIGINT OWNED BY a.b START WITH 3");
9974+
9975+
// This is what `pg_dump -s` emits for a table with a SERIAL column.
9976+
pg().one_statement_parses_to(
9977+
r#"CREATE SEQUENCE public.accounts_id_seq
9978+
AS integer
9979+
START WITH 1
9980+
INCREMENT BY 1
9981+
NO MINVALUE
9982+
NO MAXVALUE
9983+
CACHE 1"#,
9984+
"CREATE SEQUENCE public.accounts_id_seq AS INTEGER START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1",
9985+
);
9986+
9987+
// The parsed options keep the order they were written in, and `OWNED BY NONE`
9988+
// is distinct from being owned by something called `NONE`.
9989+
let Statement::CreateSequence {
9990+
sequence_options, ..
9991+
} = pg().verified_stmt("CREATE SEQUENCE sk CACHE 1 OWNED BY NONE AS BIGINT INCREMENT BY 3")
9992+
else {
9993+
panic!("expected a CREATE SEQUENCE statement");
9994+
};
9995+
assert!(matches!(
9996+
sequence_options.as_slice(),
9997+
[
9998+
SequenceOptions::Cache(_),
9999+
SequenceOptions::OwnedBy(None),
10000+
SequenceOptions::DataType(DataType::BigInt(None)),
10001+
SequenceOptions::IncrementBy(_, true),
10002+
]
10003+
));
10004+
10005+
// Each option may still appear only once.
10006+
for (sql, expected) in [
10007+
(
10008+
"CREATE SEQUENCE s START WITH 1 START 2",
10009+
"START specified more than once",
10010+
),
10011+
(
10012+
"CREATE SEQUENCE s MINVALUE 1 NO MINVALUE",
10013+
"MINVALUE | NO MINVALUE specified more than once",
10014+
),
10015+
(
10016+
"CREATE SEQUENCE s CYCLE NO CYCLE",
10017+
"CYCLE | NO CYCLE specified more than once",
10018+
),
10019+
(
10020+
"CREATE SEQUENCE s AS INT AS BIGINT",
10021+
"AS specified more than once",
10022+
),
10023+
(
10024+
"CREATE SEQUENCE s OWNED BY a.b OWNED BY NONE",
10025+
"OWNED BY specified more than once",
10026+
),
10027+
] {
10028+
assert_eq!(
10029+
pg().parse_sql_statements(sql).unwrap_err(),
10030+
ParserError::ParserError(expected.to_string()),
10031+
);
10032+
}
10033+
}

0 commit comments

Comments
 (0)