Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 22 additions & 21 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4810,12 +4810,8 @@ pub enum Statement {
if_not_exists: bool,
/// Sequence name.
name: ObjectName,
/// Optional data type for the sequence.
data_type: Option<DataType>,
/// Sequence options (INCREMENT, MINVALUE, etc.).
/// Sequence options, in the order they were written.
sequence_options: Vec<SequenceOptions>,
/// Optional `OWNED BY` target.
owned_by: Option<ObjectName>,
},
/// A `CREATE DOMAIN` statement.
CreateDomain(CreateDomain),
Expand Down Expand Up @@ -6263,32 +6259,19 @@ impl fmt::Display for Statement {
temporary,
if_not_exists,
name,
data_type,
sequence_options,
owned_by,
} => {
let as_type: String = if let Some(dt) = data_type.as_ref() {
//Cannot use format!(" AS {}", dt), due to format! is not available in --target thumbv6m-none-eabi
// " AS ".to_owned() + &dt.to_string()
[" AS ", &dt.to_string()].concat()
} else {
"".to_string()
};
write!(
f,
"CREATE {temporary}SEQUENCE {if_not_exists}{name}{as_type}",
"CREATE {temporary}SEQUENCE {if_not_exists}{name}",
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
temporary = if *temporary { "TEMPORARY " } else { "" },
name = name,
as_type = as_type
)?;
for sequence_option in sequence_options {
write!(f, "{sequence_option}")?;
}
if let Some(ob) = owned_by.as_ref() {
write!(f, " OWNED BY {ob}")?;
}
write!(f, "")
Ok(())
}
Statement::CreateStage {
or_replace,
Expand Down Expand Up @@ -6574,14 +6557,21 @@ impl fmt::Display for Statement {

/// Can use to describe options in create sequence or table column type identity
/// ```sql
/// [ INCREMENT [ BY ] increment ]
/// [ AS data_type ] [ INCREMENT [ BY ] increment ]
/// [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
/// [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ]
/// [ OWNED BY { table_name.column_name | NONE } ]
/// ```
///
/// The options form an unordered list, so they are stored in the order written.
/// `AS` and `OWNED BY` are only accepted by `CREATE SEQUENCE`, not by an
/// identity column.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum SequenceOptions {
/// `AS <data_type>` option.
DataType(DataType),
/// `INCREMENT [BY] <expr>` option; second value indicates presence of `BY` keyword.
IncrementBy(Expr, bool),
/// `MINVALUE <expr>` or `NO MINVALUE`.
Expand All @@ -6594,11 +6584,16 @@ pub enum SequenceOptions {
Cache(Expr),
/// `CYCLE` or `NO CYCLE` option.
Cycle(bool),
/// `OWNED BY <object_name>`, or `OWNED BY NONE` when no target is given.
OwnedBy(Option<ObjectName>),
}

impl fmt::Display for SequenceOptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SequenceOptions::DataType(data_type) => {
write!(f, " AS {data_type}")
}
SequenceOptions::IncrementBy(increment, by) => {
write!(
f,
Expand Down Expand Up @@ -6633,6 +6628,12 @@ impl fmt::Display for SequenceOptions {
SequenceOptions::Cycle(no) => {
write!(f, " {}CYCLE", if *no { "NO " } else { "" })
}
SequenceOptions::OwnedBy(Some(object_name)) => {
write!(f, " OWNED BY {object_name}")
}
SequenceOptions::OwnedBy(None) => {
write!(f, " OWNED BY NONE")
}
}
}
}
Expand Down
138 changes: 80 additions & 58 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use alloc::{
};
use core::{
fmt::{self, Display},
mem::discriminant,
str::FromStr,
};
#[cfg(feature = "std")]
Expand Down Expand Up @@ -9969,7 +9970,7 @@ impl<'a> Parser<'a> {
if self.parse_keywords(&[Keyword::ALWAYS, Keyword::AS, Keyword::IDENTITY]) {
let mut sequence_options = vec![];
if self.expect_token(&Token::LParen).is_ok() {
sequence_options = self.parse_create_sequence_options()?;
sequence_options = self.parse_sequence_options(false)?;
self.expect_token(&Token::RParen)?;
}
Ok(Some(ColumnOption::Generated {
Expand All @@ -9987,7 +9988,7 @@ impl<'a> Parser<'a> {
]) {
let mut sequence_options = vec![];
if self.expect_token(&Token::LParen).is_ok() {
sequence_options = self.parse_create_sequence_options()?;
sequence_options = self.parse_sequence_options(false)?;
self.expect_token(&Token::RParen)?;
}
Ok(Some(ColumnOption::Generated {
Expand Down Expand Up @@ -11145,7 +11146,7 @@ impl<'a> Parser<'a> {

if self.peek_token_ref().token == Token::LParen {
self.expect_token(&Token::LParen)?;
sequence_options = Some(self.parse_create_sequence_options()?);
sequence_options = Some(self.parse_sequence_options(false)?);
self.expect_token(&Token::RParen)?;
}

Expand Down Expand Up @@ -20650,72 +20651,93 @@ impl<'a> Parser<'a> {
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
//name
let name = self.parse_object_name(false)?;
//[ AS data_type ]
let mut data_type: Option<DataType> = None;
if self.parse_keywords(&[Keyword::AS]) {
data_type = Some(self.parse_data_type()?)
}
let sequence_options = self.parse_create_sequence_options()?;
// [ OWNED BY { table_name.column_name | NONE } ]
let owned_by = if self.parse_keywords(&[Keyword::OWNED, Keyword::BY]) {
if self.parse_keywords(&[Keyword::NONE]) {
Some(ObjectName::from(vec![Ident::new("NONE")]))
} else {
Some(self.parse_object_name(false)?)
}
} else {
None
};
let sequence_options = self.parse_sequence_options(true)?;
Ok(Statement::CreateSequence {
temporary,
if_not_exists,
name,
data_type,
sequence_options,
owned_by,
})
}

fn parse_create_sequence_options(&mut self) -> Result<Vec<SequenceOptions>, ParserError> {
let mut sequence_options = vec![];
//[ INCREMENT [ BY ] increment ]
if self.parse_keywords(&[Keyword::INCREMENT]) {
if self.parse_keywords(&[Keyword::BY]) {
sequence_options.push(SequenceOptions::IncrementBy(self.parse_number()?, true));
/// Parse the sequence options shared by `CREATE SEQUENCE` and identity
/// columns. The options form an unordered list, each allowed at most once.
///
/// `AS <data_type>` and `OWNED BY` are options of `CREATE SEQUENCE` only, so
/// `allow_type_and_owner` gates them off for an identity column.
fn parse_sequence_options(
&mut self,
allow_type_and_owner: bool,
) -> Result<Vec<SequenceOptions>, ParserError> {
let mut sequence_options: Vec<SequenceOptions> = vec![];
loop {
let (option, name) = if self.parse_keyword(Keyword::INCREMENT) {
//[ INCREMENT [ BY ] increment ]
let by = self.parse_keyword(Keyword::BY);
(
SequenceOptions::IncrementBy(self.parse_number()?, by),
"INCREMENT",
)
} else if self.parse_keyword(Keyword::MINVALUE) {
//[ MINVALUE minvalue | NO MINVALUE ]
(
SequenceOptions::MinValue(Some(self.parse_number()?)),
"MINVALUE | NO MINVALUE",
)
} else if self.parse_keywords(&[Keyword::NO, Keyword::MINVALUE]) {
(SequenceOptions::MinValue(None), "MINVALUE | NO MINVALUE")
} else if self.parse_keyword(Keyword::MAXVALUE) {
//[ MAXVALUE maxvalue | NO MAXVALUE ]
(
SequenceOptions::MaxValue(Some(self.parse_number()?)),
"MAXVALUE | NO MAXVALUE",
)
} else if self.parse_keywords(&[Keyword::NO, Keyword::MAXVALUE]) {
(SequenceOptions::MaxValue(None), "MAXVALUE | NO MAXVALUE")
} else if self.parse_keyword(Keyword::START) {
//[ START [ WITH ] start ]
let with = self.parse_keyword(Keyword::WITH);
(
SequenceOptions::StartWith(self.parse_number()?, with),
"START",
)
} else if self.parse_keyword(Keyword::CACHE) {
//[ CACHE cache ]
(SequenceOptions::Cache(self.parse_number()?), "CACHE")
} else if self.parse_keywords(&[Keyword::NO, Keyword::CYCLE]) {
// [ [ NO ] CYCLE ]
(SequenceOptions::Cycle(true), "CYCLE | NO CYCLE")
} else if self.parse_keyword(Keyword::CYCLE) {
(SequenceOptions::Cycle(false), "CYCLE | NO CYCLE")
} else if allow_type_and_owner && self.parse_keyword(Keyword::AS) {
//[ AS data_type ]
(SequenceOptions::DataType(self.parse_data_type()?), "AS")
} else if allow_type_and_owner && self.parse_keywords(&[Keyword::OWNED, Keyword::BY]) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostgreSQL's grammar takes any qualified name after OWNED BY and only the single bare word none means no owner.

https://www.postgresql.org/docs/current/sql-createsequence.html#SQL-CREATESEQUENCE-PARAMETERS

Pls fix and add test

pg().verified_stmt("CREATE SEQUENCE s OWNED BY none.c")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added as a red test and fixed

// [ OWNED BY { table_name.column_name | NONE } ]
// Only the single bare word `NONE` means no owner, `none.c` is a column.
let owner = self.parse_object_name(false)?;
let is_none = matches!(
owner.0.as_slice(),
[ObjectNamePart::Identifier(ident)]
if ident.quote_style.is_none() && ident.value.eq_ignore_ascii_case("NONE")
);
(
SequenceOptions::OwnedBy((!is_none).then_some(owner)),
"OWNED BY",
)
} else {
sequence_options.push(SequenceOptions::IncrementBy(self.parse_number()?, false));
}
}
//[ MINVALUE minvalue | NO MINVALUE ]
if self.parse_keyword(Keyword::MINVALUE) {
sequence_options.push(SequenceOptions::MinValue(Some(self.parse_number()?)));
} else if self.parse_keywords(&[Keyword::NO, Keyword::MINVALUE]) {
sequence_options.push(SequenceOptions::MinValue(None));
}
//[ MAXVALUE maxvalue | NO MAXVALUE ]
if self.parse_keywords(&[Keyword::MAXVALUE]) {
sequence_options.push(SequenceOptions::MaxValue(Some(self.parse_number()?)));
} else if self.parse_keywords(&[Keyword::NO, Keyword::MAXVALUE]) {
sequence_options.push(SequenceOptions::MaxValue(None));
}
break;
};

//[ START [ WITH ] start ]
if self.parse_keywords(&[Keyword::START]) {
if self.parse_keywords(&[Keyword::WITH]) {
sequence_options.push(SequenceOptions::StartWith(self.parse_number()?, true));
} else {
sequence_options.push(SequenceOptions::StartWith(self.parse_number()?, false));
if sequence_options
.iter()
.any(|seen| discriminant(seen) == discriminant(&option))
{
return Err(ParserError::ParserError(format!(
"{name} specified more than once"
)));
}
}
//[ CACHE cache ]
if self.parse_keywords(&[Keyword::CACHE]) {
sequence_options.push(SequenceOptions::Cache(self.parse_number()?));
}
// [ [ NO ] CYCLE ]
if self.parse_keywords(&[Keyword::NO, Keyword::CYCLE]) {
sequence_options.push(SequenceOptions::Cycle(true));
} else if self.parse_keywords(&[Keyword::CYCLE]) {
sequence_options.push(SequenceOptions::Cycle(false));
sequence_options.push(option);
}

Ok(sequence_options)
Expand Down
Loading