Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
7phs committed Oct 9, 2024
1 parent 2f076ed commit e5ac170
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 266 deletions.
68 changes: 62 additions & 6 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,13 @@ impl fmt::Display for ColumnOptionDef {
}
}

/// Identity is a column option for defining an identity or autoincrement column in a creating table statement.
/// Syntax
/// ```sql
/// { IDENTITY | AUTOINCREMENT } [ (seed , increment) | START num INCREMENT num ] [ ORDER | NOORDER ]
/// ```
/// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
Expand All @@ -1114,6 +1121,20 @@ pub struct IdentityProperty {
pub order: Option<IdentityOrder>,
}

/// A format of parameters of identity column.
///
/// It is [Snowflake] specific.
/// Syntax
/// ```sql
/// (seed , increment) | START num INCREMENT num
/// ```
/// [MS SQL Server] uses one way of representing these parameters.
/// Syntax
/// ```sql
/// (seed , increment)
/// ```
/// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
Expand All @@ -1135,7 +1156,7 @@ pub struct IdentityParameters {
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum IdentityOrder {
Order,
Noorder,
NoOrder,
}

impl fmt::Display for Identity {
Expand Down Expand Up @@ -1176,11 +1197,18 @@ impl fmt::Display for IdentityOrder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
IdentityOrder::Order => write!(f, " ORDER"),
IdentityOrder::Noorder => write!(f, " NOORDER"),
IdentityOrder::NoOrder => write!(f, " NOORDER"),
}
}
}

/// Column policy that identify a security policy of access to a column.
/// Syntax
/// ```sql
/// [ WITH ] MASKING POLICY <policy_name> [ USING ( <col_name> , <cond_col1> , ... ) ]
/// [ WITH ] PROJECTION POLICY <policy_name>
/// ```
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
Expand All @@ -1193,6 +1221,7 @@ pub enum ColumnPolicy {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ColumnPolicyProperty {
pub with: bool,
pub policy_name: Ident,
pub using_columns: Option<Vec<Ident>>,
}
Expand All @@ -1203,10 +1232,37 @@ impl fmt::Display for ColumnPolicy {
ColumnPolicy::MaskingPolicy(property) => ("MASKING POLICY", property),
ColumnPolicy::ProjectionPolicy(property) => ("PROJECTION POLICY", property),
};
write!(f, "WITH {command} {}", property.policy_name)?;
if property.with {
write!(f, "WITH ")?;
}
write!(f, "{command} {}", property.policy_name)?;
if let Some(using_columns) = &property.using_columns {
write!(f, "USING ({})", display_comma_separated(using_columns))?;
write!(f, " USING ({})", display_comma_separated(using_columns))?;
}
Ok(())
}
}

/// Tags option of column
/// Syntax
/// ```sql
/// [ WITH ] TAG ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] )
/// ```
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct TagsColumnOption {
pub with: bool,
pub tags: Vec<Tag>,
}

impl fmt::Display for TagsColumnOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.with {
write!(f, "WITH ")?;
}
write!(f, "TAG ({})", display_comma_separated(&self.tags))?;
Ok(())
}
}
Expand Down Expand Up @@ -1303,7 +1359,7 @@ pub enum ColumnOption {
/// [ WITH ] TAG ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] )
/// ```
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
Tags(Vec<Tag>),
Tags(TagsColumnOption),
}

impl fmt::Display for ColumnOption {
Expand Down Expand Up @@ -1412,7 +1468,7 @@ impl fmt::Display for ColumnOption {
write!(f, "{parameters}")
}
Tags(tags) => {
write!(f, "WITH TAG ({})", display_comma_separated(tags))
write!(f, "{tags}")
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub use self::ddl::{
ConstraintCharacteristics, Deduplicate, DeferrableInitial, GeneratedAs,
GeneratedExpressionMode, Identity, IdentityFormat, IdentityOrder, IdentityParameters,
IdentityProperty, IndexOption, IndexType, KeyOrIndexDisplay, Owner, Partition, ProcedureParam,
ReferentialAction, TableConstraint, UserDefinedTypeCompositeAttributeDef,
ReferentialAction, TableConstraint, TagsColumnOption, UserDefinedTypeCompositeAttributeDef,
UserDefinedTypeRepresentation, ViewColumnDef,
};
pub use self::dml::{CreateIndex, CreateTable, Delete, Insert};
Expand Down
15 changes: 14 additions & 1 deletion src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub use self::postgresql::PostgreSqlDialect;
pub use self::redshift::RedshiftSqlDialect;
pub use self::snowflake::SnowflakeDialect;
pub use self::sqlite::SQLiteDialect;
use crate::ast::{Expr, Statement};
use crate::ast::{ColumnOption, Expr, Statement};
pub use crate::keywords;
use crate::keywords::Keyword;
use crate::parser::{Parser, ParserError};
Expand Down Expand Up @@ -478,6 +478,19 @@ pub trait Dialect: Debug + Any {
None
}

/// Dialect-specific column option parser override
///
/// This method is called to parse the next column option.
///
/// If `None` is returned, falls back to the default behavior.
fn parse_column_option(
&self,
_parser: &mut Parser,
) -> Option<Result<Option<ColumnOption>, ParserError>> {
// return None to fall back to the default behavior
None
}

/// Decide the lexical Precedence of operators.
///
/// Uses (APPROXIMATELY) <https://www.postgresql.org/docs/7.0/operators.htm#AEN2026> as a reference
Expand Down
105 changes: 104 additions & 1 deletion src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ use crate::ast::helpers::stmt_data_loading::{
DataLoadingOption, DataLoadingOptionType, DataLoadingOptions, StageLoadSelectItem,
StageParamsObject,
};
use crate::ast::{CommentDef, Ident, ObjectName, RowAccessPolicy, Statement, WrappedCollection};
use crate::ast::{
ColumnOption, ColumnPolicy, ColumnPolicyProperty, CommentDef, Ident, Identity, IdentityFormat,
IdentityOrder, IdentityParameters, IdentityProperty, ObjectName, RowAccessPolicy, Statement,
WrappedCollection,
};
use crate::dialect::{Dialect, Precedence};
use crate::keywords::Keyword;
use crate::parser::{Parser, ParserError};
Expand Down Expand Up @@ -149,6 +153,41 @@ impl Dialect for SnowflakeDialect {
None
}

fn parse_column_option(
&self,
parser: &mut Parser,
) -> Option<Result<Option<ColumnOption>, ParserError>> {
let with = parser.parse_keyword(Keyword::WITH);

if parser.parse_keyword(Keyword::IDENTITY) {
Some(
parse_identity_property(parser)
.map(|p| Some(ColumnOption::Identity(Identity::Identity(p)))),
)
} else if parser.parse_keyword(Keyword::AUTOINCREMENT) {
Some(
parse_identity_property(parser)
.map(|p| Some(ColumnOption::Identity(Identity::Autoincrement(p)))),
)
} else if parser.parse_keywords(&[Keyword::MASKING, Keyword::POLICY]) {
Some(
parse_column_policy_property(parser, with)
.map(|p| Some(ColumnOption::Policy(ColumnPolicy::MaskingPolicy(p)))),
)
} else if parser.parse_keywords(&[Keyword::PROJECTION, Keyword::POLICY]) {
Some(
parse_column_policy_property(parser, with)
.map(|p| Some(ColumnOption::Policy(ColumnPolicy::ProjectionPolicy(p)))),
)
} else {
// needs to revert initial state of parser if dialect finds any matching
if with {
parser.prev_token();
}
None
}
}

fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
let token = parser.peek_token();
// Snowflake supports the `:` cast operator unlike other dialects
Expand Down Expand Up @@ -772,3 +811,67 @@ fn parse_parentheses_options(parser: &mut Parser) -> Result<Vec<DataLoadingOptio
}
Ok(options)
}

/// Parsing a property of identity or autoincrement column option
/// Syntax:
/// ```sql
/// [ (seed , increment) | START num INCREMENT num ] [ ORDER | NOORDER ]
/// ```
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
fn parse_identity_property(parser: &mut Parser) -> Result<IdentityProperty, ParserError> {
let parameters = if parser.consume_token(&Token::LParen) {
let seed = parser.parse_number()?;
parser.expect_token(&Token::Comma)?;
let increment = parser.parse_number()?;
parser.expect_token(&Token::RParen)?;

Some(IdentityFormat::FunctionCall(IdentityParameters {
seed,
increment,
}))
} else if parser.parse_keyword(Keyword::START) {
let seed = parser.parse_number()?;
parser.expect_keyword(Keyword::INCREMENT)?;
let increment = parser.parse_number()?;

Some(IdentityFormat::StartAndIncrement(IdentityParameters {
seed,
increment,
}))
} else {
None
};
let order = match parser.parse_one_of_keywords(&[Keyword::ORDER, Keyword::NOORDER]) {
Some(Keyword::ORDER) => Some(IdentityOrder::Order),
Some(Keyword::NOORDER) => Some(IdentityOrder::NoOrder),
_ => None,
};
Ok(IdentityProperty { parameters, order })
}

/// Parsing a policy property of column option
/// Syntax:
/// ```sql
/// <policy_name> [ USING ( <col_name> , <cond_col1> , ... )
/// ```
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
fn parse_column_policy_property(
parser: &mut Parser,
with: bool,
) -> Result<ColumnPolicyProperty, ParserError> {
let policy_name = parser.parse_identifier(false)?;
let using_columns = if parser.parse_keyword(Keyword::USING) {
parser.expect_token(&Token::LParen)?;
let columns = parser.parse_comma_separated(|p| p.parse_identifier(false))?;
parser.expect_token(&Token::RParen)?;
Some(columns)
} else {
None
};

Ok(ColumnPolicyProperty {
with,
policy_name,
using_columns,
})
}
Loading

0 comments on commit e5ac170

Please sign in to comment.