mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-10-09 13:40:22 +00:00
Snowflake: support for extended column options in CREATE TABLE
(#1454)
This commit is contained in:
parent
1dd7d26fbb
commit
3421e1e4d4
8 changed files with 723 additions and 45 deletions
244
src/ast/ddl.rs
244
src/ast/ddl.rs
|
@ -31,7 +31,7 @@ use sqlparser_derive::{Visit, VisitMut};
|
||||||
use crate::ast::value::escape_single_quote_string;
|
use crate::ast::value::escape_single_quote_string;
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
display_comma_separated, display_separated, DataType, Expr, Ident, MySQLColumnPosition,
|
display_comma_separated, display_separated, DataType, Expr, Ident, MySQLColumnPosition,
|
||||||
ObjectName, OrderByExpr, ProjectionSelect, SequenceOptions, SqlOption, Value,
|
ObjectName, OrderByExpr, ProjectionSelect, SequenceOptions, SqlOption, Tag, Value,
|
||||||
};
|
};
|
||||||
use crate::keywords::Keyword;
|
use crate::keywords::Keyword;
|
||||||
use crate::tokenizer::Token;
|
use crate::tokenizer::Token;
|
||||||
|
@ -1096,17 +1096,221 @@ impl fmt::Display for ColumnOptionDef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Identity is a column option for defining an identity or autoincrement column in a `CREATE 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))]
|
||||||
|
pub enum IdentityPropertyKind {
|
||||||
|
/// An identity property declared via the `AUTOINCREMENT` key word
|
||||||
|
/// Example:
|
||||||
|
/// ```sql
|
||||||
|
/// AUTOINCREMENT(100, 1) NOORDER
|
||||||
|
/// AUTOINCREMENT START 100 INCREMENT 1 ORDER
|
||||||
|
/// ```
|
||||||
|
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
|
||||||
|
Autoincrement(IdentityProperty),
|
||||||
|
/// An identity property declared via the `IDENTITY` key word
|
||||||
|
/// Example, [MS SQL Server] or [Snowflake]:
|
||||||
|
/// ```sql
|
||||||
|
/// IDENTITY(100, 1)
|
||||||
|
/// ```
|
||||||
|
/// [Snowflake]
|
||||||
|
/// ```sql
|
||||||
|
/// IDENTITY(100, 1) ORDER
|
||||||
|
/// IDENTITY START 100 INCREMENT 1 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
|
||||||
|
Identity(IdentityProperty),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for IdentityPropertyKind {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let (command, property) = match self {
|
||||||
|
IdentityPropertyKind::Identity(property) => ("IDENTITY", property),
|
||||||
|
IdentityPropertyKind::Autoincrement(property) => ("AUTOINCREMENT", property),
|
||||||
|
};
|
||||||
|
write!(f, "{command}")?;
|
||||||
|
if let Some(parameters) = &property.parameters {
|
||||||
|
write!(f, "{parameters}")?;
|
||||||
|
}
|
||||||
|
if let Some(order) = &property.order {
|
||||||
|
write!(f, "{order}")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
pub struct IdentityProperty {
|
pub struct IdentityProperty {
|
||||||
|
pub parameters: Option<IdentityPropertyFormatKind>,
|
||||||
|
pub order: Option<IdentityPropertyOrder>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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))]
|
||||||
|
pub enum IdentityPropertyFormatKind {
|
||||||
|
/// A parameters of identity column declared like parameters of function call
|
||||||
|
/// Example:
|
||||||
|
/// ```sql
|
||||||
|
/// (100, 1)
|
||||||
|
/// ```
|
||||||
|
/// [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
|
||||||
|
FunctionCall(IdentityParameters),
|
||||||
|
/// A parameters of identity column declared with keywords `START` and `INCREMENT`
|
||||||
|
/// Example:
|
||||||
|
/// ```sql
|
||||||
|
/// START 100 INCREMENT 1
|
||||||
|
/// ```
|
||||||
|
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
|
||||||
|
StartAndIncrement(IdentityParameters),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for IdentityPropertyFormatKind {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
IdentityPropertyFormatKind::FunctionCall(parameters) => {
|
||||||
|
write!(f, "({}, {})", parameters.seed, parameters.increment)
|
||||||
|
}
|
||||||
|
IdentityPropertyFormatKind::StartAndIncrement(parameters) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
" START {} INCREMENT {}",
|
||||||
|
parameters.seed, parameters.increment
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
pub struct IdentityParameters {
|
||||||
pub seed: Expr,
|
pub seed: Expr,
|
||||||
pub increment: Expr,
|
pub increment: Expr,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for IdentityProperty {
|
/// The identity column option specifies how values are generated for the auto-incremented column, either in increasing or decreasing order.
|
||||||
|
/// Syntax
|
||||||
|
/// ```sql
|
||||||
|
/// ORDER | NOORDER
|
||||||
|
/// ```
|
||||||
|
/// [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 enum IdentityPropertyOrder {
|
||||||
|
Order,
|
||||||
|
NoOrder,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for IdentityPropertyOrder {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}, {}", self.seed, self.increment)
|
match self {
|
||||||
|
IdentityPropertyOrder::Order => write!(f, " ORDER"),
|
||||||
|
IdentityPropertyOrder::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))]
|
||||||
|
pub enum ColumnPolicy {
|
||||||
|
MaskingPolicy(ColumnPolicyProperty),
|
||||||
|
ProjectionPolicy(ColumnPolicyProperty),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ColumnPolicy {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let (command, property) = match self {
|
||||||
|
ColumnPolicy::MaskingPolicy(property) => ("MASKING POLICY", property),
|
||||||
|
ColumnPolicy::ProjectionPolicy(property) => ("PROJECTION POLICY", property),
|
||||||
|
};
|
||||||
|
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))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
pub struct ColumnPolicyProperty {
|
||||||
|
/// This flag indicates that the column policy option is declared using the `WITH` prefix.
|
||||||
|
/// Example
|
||||||
|
/// ```sql
|
||||||
|
/// WITH PROJECTION POLICY sample_policy
|
||||||
|
/// ```
|
||||||
|
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
|
||||||
|
pub with: bool,
|
||||||
|
pub policy_name: Ident,
|
||||||
|
pub using_columns: Option<Vec<Ident>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
/// This flag indicates that the tags option is declared using the `WITH` prefix.
|
||||||
|
/// Example:
|
||||||
|
/// ```sql
|
||||||
|
/// WITH TAG (A = 'Tag A')
|
||||||
|
/// ```
|
||||||
|
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
|
||||||
|
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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1180,16 +1384,32 @@ pub enum ColumnOption {
|
||||||
/// [1]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#view_column_option_list
|
/// [1]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#view_column_option_list
|
||||||
/// [2]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#column_option_list
|
/// [2]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#column_option_list
|
||||||
Options(Vec<SqlOption>),
|
Options(Vec<SqlOption>),
|
||||||
/// MS SQL Server specific: Creates an identity column in a table.
|
/// Creates an identity or an autoincrement column in a table.
|
||||||
/// Syntax
|
/// Syntax
|
||||||
/// ```sql
|
/// ```sql
|
||||||
/// IDENTITY [ (seed , increment) ]
|
/// { 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
|
/// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property
|
||||||
Identity(Option<IdentityProperty>),
|
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
|
||||||
|
Identity(IdentityPropertyKind),
|
||||||
/// SQLite specific: ON CONFLICT option on column definition
|
/// SQLite specific: ON CONFLICT option on column definition
|
||||||
/// <https://www.sqlite.org/lang_conflict.html>
|
/// <https://www.sqlite.org/lang_conflict.html>
|
||||||
OnConflict(Keyword),
|
OnConflict(Keyword),
|
||||||
|
/// Snowflake specific: an option of specifying security masking or projection policy to set on 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
|
||||||
|
Policy(ColumnPolicy),
|
||||||
|
/// Snowflake specific: Specifies the tag name and the tag string value.
|
||||||
|
/// Syntax:
|
||||||
|
/// ```sql
|
||||||
|
/// [ WITH ] TAG ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] )
|
||||||
|
/// ```
|
||||||
|
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
|
||||||
|
Tags(TagsColumnOption),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ColumnOption {
|
impl fmt::Display for ColumnOption {
|
||||||
|
@ -1292,16 +1512,18 @@ impl fmt::Display for ColumnOption {
|
||||||
write!(f, "OPTIONS({})", display_comma_separated(options))
|
write!(f, "OPTIONS({})", display_comma_separated(options))
|
||||||
}
|
}
|
||||||
Identity(parameters) => {
|
Identity(parameters) => {
|
||||||
write!(f, "IDENTITY")?;
|
write!(f, "{parameters}")
|
||||||
if let Some(parameters) = parameters {
|
|
||||||
write!(f, "({parameters})")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
OnConflict(keyword) => {
|
OnConflict(keyword) => {
|
||||||
write!(f, "ON CONFLICT {:?}", keyword)?;
|
write!(f, "ON CONFLICT {:?}", keyword)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Policy(parameters) => {
|
||||||
|
write!(f, "{parameters}")
|
||||||
|
}
|
||||||
|
Tags(tags) => {
|
||||||
|
write!(f, "{tags}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,11 +40,12 @@ pub use self::data_type::{
|
||||||
pub use self::dcl::{AlterRoleOperation, ResetConfig, RoleOption, SetConfigValue, Use};
|
pub use self::dcl::{AlterRoleOperation, ResetConfig, RoleOption, SetConfigValue, Use};
|
||||||
pub use self::ddl::{
|
pub use self::ddl::{
|
||||||
AlterColumnOperation, AlterIndexOperation, AlterPolicyOperation, AlterTableOperation,
|
AlterColumnOperation, AlterIndexOperation, AlterPolicyOperation, AlterTableOperation,
|
||||||
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ConstraintCharacteristics, Deduplicate,
|
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnPolicy, ColumnPolicyProperty,
|
||||||
DeferrableInitial, GeneratedAs, GeneratedExpressionMode, IdentityProperty, IndexOption,
|
ConstraintCharacteristics, Deduplicate, DeferrableInitial, GeneratedAs,
|
||||||
IndexType, KeyOrIndexDisplay, Owner, Partition, ProcedureParam, ReferentialAction,
|
GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
|
||||||
TableConstraint, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation,
|
IdentityPropertyKind, IdentityPropertyOrder, IndexOption, IndexType, KeyOrIndexDisplay, Owner,
|
||||||
ViewColumnDef,
|
Partition, ProcedureParam, ReferentialAction, TableConstraint, TagsColumnOption,
|
||||||
|
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef,
|
||||||
};
|
};
|
||||||
pub use self::dml::{CreateIndex, CreateTable, Delete, Insert};
|
pub use self::dml::{CreateIndex, CreateTable, Delete, Insert};
|
||||||
pub use self::operator::{BinaryOperator, UnaryOperator};
|
pub use self::operator::{BinaryOperator, UnaryOperator};
|
||||||
|
|
|
@ -49,7 +49,7 @@ pub use self::postgresql::PostgreSqlDialect;
|
||||||
pub use self::redshift::RedshiftSqlDialect;
|
pub use self::redshift::RedshiftSqlDialect;
|
||||||
pub use self::snowflake::SnowflakeDialect;
|
pub use self::snowflake::SnowflakeDialect;
|
||||||
pub use self::sqlite::SQLiteDialect;
|
pub use self::sqlite::SQLiteDialect;
|
||||||
use crate::ast::{Expr, Statement};
|
use crate::ast::{ColumnOption, Expr, Statement};
|
||||||
pub use crate::keywords;
|
pub use crate::keywords;
|
||||||
use crate::keywords::Keyword;
|
use crate::keywords::Keyword;
|
||||||
use crate::parser::{Parser, ParserError};
|
use crate::parser::{Parser, ParserError};
|
||||||
|
@ -478,6 +478,19 @@ pub trait Dialect: Debug + Any {
|
||||||
None
|
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.
|
/// Decide the lexical Precedence of operators.
|
||||||
///
|
///
|
||||||
/// Uses (APPROXIMATELY) <https://www.postgresql.org/docs/7.0/operators.htm#AEN2026> as a reference
|
/// Uses (APPROXIMATELY) <https://www.postgresql.org/docs/7.0/operators.htm#AEN2026> as a reference
|
||||||
|
|
|
@ -22,7 +22,11 @@ use crate::ast::helpers::stmt_data_loading::{
|
||||||
DataLoadingOption, DataLoadingOptionType, DataLoadingOptions, StageLoadSelectItem,
|
DataLoadingOption, DataLoadingOptionType, DataLoadingOptions, StageLoadSelectItem,
|
||||||
StageParamsObject,
|
StageParamsObject,
|
||||||
};
|
};
|
||||||
use crate::ast::{Ident, ObjectName, RowAccessPolicy, Statement, Tag, WrappedCollection};
|
use crate::ast::{
|
||||||
|
ColumnOption, ColumnPolicy, ColumnPolicyProperty, Ident, IdentityParameters, IdentityProperty,
|
||||||
|
IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, ObjectName,
|
||||||
|
RowAccessPolicy, Statement, TagsColumnOption, WrappedCollection,
|
||||||
|
};
|
||||||
use crate::dialect::{Dialect, Precedence};
|
use crate::dialect::{Dialect, Precedence};
|
||||||
use crate::keywords::Keyword;
|
use crate::keywords::Keyword;
|
||||||
use crate::parser::{Parser, ParserError};
|
use crate::parser::{Parser, ParserError};
|
||||||
|
@ -149,6 +153,36 @@ impl Dialect for SnowflakeDialect {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_column_option(
|
||||||
|
&self,
|
||||||
|
parser: &mut Parser,
|
||||||
|
) -> Option<Result<Option<ColumnOption>, ParserError>> {
|
||||||
|
parser.maybe_parse(|parser| {
|
||||||
|
let with = parser.parse_keyword(Keyword::WITH);
|
||||||
|
|
||||||
|
if parser.parse_keyword(Keyword::IDENTITY) {
|
||||||
|
Ok(parse_identity_property(parser)
|
||||||
|
.map(|p| Some(ColumnOption::Identity(IdentityPropertyKind::Identity(p)))))
|
||||||
|
} else if parser.parse_keyword(Keyword::AUTOINCREMENT) {
|
||||||
|
Ok(parse_identity_property(parser).map(|p| {
|
||||||
|
Some(ColumnOption::Identity(IdentityPropertyKind::Autoincrement(
|
||||||
|
p,
|
||||||
|
)))
|
||||||
|
}))
|
||||||
|
} else if parser.parse_keywords(&[Keyword::MASKING, Keyword::POLICY]) {
|
||||||
|
Ok(parse_column_policy_property(parser, with)
|
||||||
|
.map(|p| Some(ColumnOption::Policy(ColumnPolicy::MaskingPolicy(p)))))
|
||||||
|
} else if parser.parse_keywords(&[Keyword::PROJECTION, Keyword::POLICY]) {
|
||||||
|
Ok(parse_column_policy_property(parser, with)
|
||||||
|
.map(|p| Some(ColumnOption::Policy(ColumnPolicy::ProjectionPolicy(p)))))
|
||||||
|
} else if parser.parse_keywords(&[Keyword::TAG]) {
|
||||||
|
Ok(parse_column_tags(parser, with).map(|p| Some(ColumnOption::Tags(p))))
|
||||||
|
} else {
|
||||||
|
Err(ParserError::ParserError("not found match".to_string()))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
|
fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
|
||||||
let token = parser.peek_token();
|
let token = parser.peek_token();
|
||||||
// Snowflake supports the `:` cast operator unlike other dialects
|
// Snowflake supports the `:` cast operator unlike other dialects
|
||||||
|
@ -307,16 +341,8 @@ pub fn parse_create_table(
|
||||||
builder.with_row_access_policy(Some(RowAccessPolicy::new(policy, columns)))
|
builder.with_row_access_policy(Some(RowAccessPolicy::new(policy, columns)))
|
||||||
}
|
}
|
||||||
Keyword::TAG => {
|
Keyword::TAG => {
|
||||||
fn parse_tag(parser: &mut Parser) -> Result<Tag, ParserError> {
|
|
||||||
let name = parser.parse_identifier(false)?;
|
|
||||||
parser.expect_token(&Token::Eq)?;
|
|
||||||
let value = parser.parse_literal_string()?;
|
|
||||||
|
|
||||||
Ok(Tag::new(name, value))
|
|
||||||
}
|
|
||||||
|
|
||||||
parser.expect_token(&Token::LParen)?;
|
parser.expect_token(&Token::LParen)?;
|
||||||
let tags = parser.parse_comma_separated(parse_tag)?;
|
let tags = parser.parse_comma_separated(Parser::parse_tag)?;
|
||||||
parser.expect_token(&Token::RParen)?;
|
parser.expect_token(&Token::RParen)?;
|
||||||
builder = builder.with_tags(Some(tags));
|
builder = builder.with_tags(Some(tags));
|
||||||
}
|
}
|
||||||
|
@ -776,3 +802,79 @@ fn parse_parentheses_options(parser: &mut Parser) -> Result<Vec<DataLoadingOptio
|
||||||
}
|
}
|
||||||
Ok(options)
|
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(IdentityPropertyFormatKind::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(IdentityPropertyFormatKind::StartAndIncrement(
|
||||||
|
IdentityParameters { seed, increment },
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let order = match parser.parse_one_of_keywords(&[Keyword::ORDER, Keyword::NOORDER]) {
|
||||||
|
Some(Keyword::ORDER) => Some(IdentityPropertyOrder::Order),
|
||||||
|
Some(Keyword::NOORDER) => Some(IdentityPropertyOrder::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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parsing tags list of column
|
||||||
|
/// Syntax:
|
||||||
|
/// ```sql
|
||||||
|
/// ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] )
|
||||||
|
/// ```
|
||||||
|
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
|
||||||
|
fn parse_column_tags(parser: &mut Parser, with: bool) -> Result<TagsColumnOption, ParserError> {
|
||||||
|
parser.expect_token(&Token::LParen)?;
|
||||||
|
let tags = parser.parse_comma_separated(Parser::parse_tag)?;
|
||||||
|
parser.expect_token(&Token::RParen)?;
|
||||||
|
|
||||||
|
Ok(TagsColumnOption { with, tags })
|
||||||
|
}
|
||||||
|
|
|
@ -453,6 +453,7 @@ define_keywords!(
|
||||||
MACRO,
|
MACRO,
|
||||||
MANAGEDLOCATION,
|
MANAGEDLOCATION,
|
||||||
MAP,
|
MAP,
|
||||||
|
MASKING,
|
||||||
MATCH,
|
MATCH,
|
||||||
MATCHED,
|
MATCHED,
|
||||||
MATCHES,
|
MATCHES,
|
||||||
|
@ -504,6 +505,7 @@ define_keywords!(
|
||||||
NOINHERIT,
|
NOINHERIT,
|
||||||
NOLOGIN,
|
NOLOGIN,
|
||||||
NONE,
|
NONE,
|
||||||
|
NOORDER,
|
||||||
NOREPLICATION,
|
NOREPLICATION,
|
||||||
NORMALIZE,
|
NORMALIZE,
|
||||||
NOSCAN,
|
NOSCAN,
|
||||||
|
|
|
@ -6065,7 +6065,7 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
} else if let Some(option) = self.parse_optional_column_option()? {
|
} else if let Some(option) = self.parse_optional_column_option()? {
|
||||||
options.push(ColumnOptionDef { name: None, option });
|
options.push(ColumnOptionDef { name: None, option });
|
||||||
} else if dialect_of!(self is MySqlDialect | GenericDialect)
|
} else if dialect_of!(self is MySqlDialect | SnowflakeDialect | GenericDialect)
|
||||||
&& self.parse_keyword(Keyword::COLLATE)
|
&& self.parse_keyword(Keyword::COLLATE)
|
||||||
{
|
{
|
||||||
collation = Some(self.parse_object_name(false)?);
|
collation = Some(self.parse_object_name(false)?);
|
||||||
|
@ -6105,6 +6105,10 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_optional_column_option(&mut self) -> Result<Option<ColumnOption>, ParserError> {
|
pub fn parse_optional_column_option(&mut self) -> Result<Option<ColumnOption>, ParserError> {
|
||||||
|
if let Some(option) = self.dialect.parse_column_option(self) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
|
||||||
if self.parse_keywords(&[Keyword::CHARACTER, Keyword::SET]) {
|
if self.parse_keywords(&[Keyword::CHARACTER, Keyword::SET]) {
|
||||||
Ok(Some(ColumnOption::CharacterSet(
|
Ok(Some(ColumnOption::CharacterSet(
|
||||||
self.parse_object_name(false)?,
|
self.parse_object_name(false)?,
|
||||||
|
@ -6232,17 +6236,24 @@ impl<'a> Parser<'a> {
|
||||||
} else if self.parse_keyword(Keyword::IDENTITY)
|
} else if self.parse_keyword(Keyword::IDENTITY)
|
||||||
&& dialect_of!(self is MsSqlDialect | GenericDialect)
|
&& dialect_of!(self is MsSqlDialect | GenericDialect)
|
||||||
{
|
{
|
||||||
let property = if self.consume_token(&Token::LParen) {
|
let parameters = if self.consume_token(&Token::LParen) {
|
||||||
let seed = self.parse_number()?;
|
let seed = self.parse_number()?;
|
||||||
self.expect_token(&Token::Comma)?;
|
self.expect_token(&Token::Comma)?;
|
||||||
let increment = self.parse_number()?;
|
let increment = self.parse_number()?;
|
||||||
self.expect_token(&Token::RParen)?;
|
self.expect_token(&Token::RParen)?;
|
||||||
|
|
||||||
Some(IdentityProperty { seed, increment })
|
Some(IdentityPropertyFormatKind::FunctionCall(
|
||||||
|
IdentityParameters { seed, increment },
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
Ok(Some(ColumnOption::Identity(property)))
|
Ok(Some(ColumnOption::Identity(
|
||||||
|
IdentityPropertyKind::Identity(IdentityProperty {
|
||||||
|
parameters,
|
||||||
|
order: None,
|
||||||
|
}),
|
||||||
|
)))
|
||||||
} else if dialect_of!(self is SQLiteDialect | GenericDialect)
|
} else if dialect_of!(self is SQLiteDialect | GenericDialect)
|
||||||
&& self.parse_keywords(&[Keyword::ON, Keyword::CONFLICT])
|
&& self.parse_keywords(&[Keyword::ON, Keyword::CONFLICT])
|
||||||
{
|
{
|
||||||
|
@ -6260,6 +6271,15 @@ impl<'a> Parser<'a> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_tag(&mut self) -> Result<Tag, ParserError> {
|
||||||
|
let name = self.parse_identifier(false)?;
|
||||||
|
self.expect_token(&Token::Eq)?;
|
||||||
|
let value = self.parse_literal_string()?;
|
||||||
|
|
||||||
|
Ok(Tag::new(name, value))
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_optional_column_option_generated(
|
fn parse_optional_column_option_generated(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Result<Option<ColumnOption>, ParserError> {
|
) -> Result<Option<ColumnOption>, ParserError> {
|
||||||
|
|
|
@ -921,7 +921,12 @@ fn parse_create_table_with_identity_column() {
|
||||||
vec![
|
vec![
|
||||||
ColumnOptionDef {
|
ColumnOptionDef {
|
||||||
name: None,
|
name: None,
|
||||||
option: ColumnOption::Identity(None),
|
option: ColumnOption::Identity(IdentityPropertyKind::Identity(
|
||||||
|
IdentityProperty {
|
||||||
|
parameters: None,
|
||||||
|
order: None,
|
||||||
|
},
|
||||||
|
)),
|
||||||
},
|
},
|
||||||
ColumnOptionDef {
|
ColumnOptionDef {
|
||||||
name: None,
|
name: None,
|
||||||
|
@ -934,19 +939,17 @@ fn parse_create_table_with_identity_column() {
|
||||||
vec![
|
vec![
|
||||||
ColumnOptionDef {
|
ColumnOptionDef {
|
||||||
name: None,
|
name: None,
|
||||||
#[cfg(not(feature = "bigdecimal"))]
|
option: ColumnOption::Identity(IdentityPropertyKind::Identity(
|
||||||
option: ColumnOption::Identity(Some(IdentityProperty {
|
IdentityProperty {
|
||||||
seed: Expr::Value(Value::Number("1".to_string(), false)),
|
parameters: Some(IdentityPropertyFormatKind::FunctionCall(
|
||||||
increment: Expr::Value(Value::Number("1".to_string(), false)),
|
IdentityParameters {
|
||||||
})),
|
seed: Expr::Value(number("1")),
|
||||||
#[cfg(feature = "bigdecimal")]
|
increment: Expr::Value(number("1")),
|
||||||
option: ColumnOption::Identity(Some(IdentityProperty {
|
},
|
||||||
seed: Expr::Value(Value::Number(bigdecimal::BigDecimal::from(1), false)),
|
)),
|
||||||
increment: Expr::Value(Value::Number(
|
order: None,
|
||||||
bigdecimal::BigDecimal::from(1),
|
},
|
||||||
false,
|
)),
|
||||||
)),
|
|
||||||
})),
|
|
||||||
},
|
},
|
||||||
ColumnOptionDef {
|
ColumnOptionDef {
|
||||||
name: None,
|
name: None,
|
||||||
|
|
|
@ -525,6 +525,321 @@ fn test_snowflake_single_line_tokenize() {
|
||||||
assert_eq!(expected, tokens);
|
assert_eq!(expected, tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snowflake_create_table_with_autoincrement_columns() {
|
||||||
|
let sql = concat!(
|
||||||
|
"CREATE TABLE my_table (",
|
||||||
|
"a INT AUTOINCREMENT ORDER, ",
|
||||||
|
"b INT AUTOINCREMENT(100, 1) NOORDER, ",
|
||||||
|
"c INT IDENTITY, ",
|
||||||
|
"d INT IDENTITY START 100 INCREMENT 1 ORDER",
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
// it is a snowflake specific options (AUTOINCREMENT/IDENTITY)
|
||||||
|
match snowflake().verified_stmt(sql) {
|
||||||
|
Statement::CreateTable(CreateTable { columns, .. }) => {
|
||||||
|
assert_eq!(
|
||||||
|
columns,
|
||||||
|
vec![
|
||||||
|
ColumnDef {
|
||||||
|
name: "a".into(),
|
||||||
|
data_type: DataType::Int(None),
|
||||||
|
collation: None,
|
||||||
|
options: vec![ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Identity(IdentityPropertyKind::Autoincrement(
|
||||||
|
IdentityProperty {
|
||||||
|
parameters: None,
|
||||||
|
order: Some(IdentityPropertyOrder::Order),
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
ColumnDef {
|
||||||
|
name: "b".into(),
|
||||||
|
data_type: DataType::Int(None),
|
||||||
|
collation: None,
|
||||||
|
options: vec![ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Identity(IdentityPropertyKind::Autoincrement(
|
||||||
|
IdentityProperty {
|
||||||
|
parameters: Some(IdentityPropertyFormatKind::FunctionCall(
|
||||||
|
IdentityParameters {
|
||||||
|
seed: Expr::Value(number("100")),
|
||||||
|
increment: Expr::Value(number("1")),
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
order: Some(IdentityPropertyOrder::NoOrder),
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
ColumnDef {
|
||||||
|
name: "c".into(),
|
||||||
|
data_type: DataType::Int(None),
|
||||||
|
collation: None,
|
||||||
|
options: vec![ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Identity(IdentityPropertyKind::Identity(
|
||||||
|
IdentityProperty {
|
||||||
|
parameters: None,
|
||||||
|
order: None,
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
ColumnDef {
|
||||||
|
name: "d".into(),
|
||||||
|
data_type: DataType::Int(None),
|
||||||
|
collation: None,
|
||||||
|
options: vec![ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Identity(IdentityPropertyKind::Identity(
|
||||||
|
IdentityProperty {
|
||||||
|
parameters: Some(
|
||||||
|
IdentityPropertyFormatKind::StartAndIncrement(
|
||||||
|
IdentityParameters {
|
||||||
|
seed: Expr::Value(number("100")),
|
||||||
|
increment: Expr::Value(number("1")),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
order: Some(IdentityPropertyOrder::Order),
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snowflake_create_table_with_collated_column() {
|
||||||
|
match snowflake_and_generic().verified_stmt("CREATE TABLE my_table (a TEXT COLLATE 'de_DE')") {
|
||||||
|
Statement::CreateTable(CreateTable { columns, .. }) => {
|
||||||
|
assert_eq!(
|
||||||
|
columns,
|
||||||
|
vec![ColumnDef {
|
||||||
|
name: "a".into(),
|
||||||
|
data_type: DataType::Text,
|
||||||
|
collation: Some(ObjectName(vec![Ident::with_quote('\'', "de_DE")])),
|
||||||
|
options: vec![]
|
||||||
|
},]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snowflake_create_table_with_columns_masking_policy() {
|
||||||
|
for (sql, with, using_columns) in [
|
||||||
|
(
|
||||||
|
"CREATE TABLE my_table (a INT WITH MASKING POLICY p)",
|
||||||
|
true,
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"CREATE TABLE my_table (a INT MASKING POLICY p)",
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"CREATE TABLE my_table (a INT WITH MASKING POLICY p USING (a, b))",
|
||||||
|
true,
|
||||||
|
Some(vec!["a".into(), "b".into()]),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"CREATE TABLE my_table (a INT MASKING POLICY p USING (a, b))",
|
||||||
|
false,
|
||||||
|
Some(vec!["a".into(), "b".into()]),
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
match snowflake().verified_stmt(sql) {
|
||||||
|
Statement::CreateTable(CreateTable { columns, .. }) => {
|
||||||
|
assert_eq!(
|
||||||
|
columns,
|
||||||
|
vec![ColumnDef {
|
||||||
|
name: "a".into(),
|
||||||
|
data_type: DataType::Int(None),
|
||||||
|
collation: None,
|
||||||
|
options: vec![ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Policy(ColumnPolicy::MaskingPolicy(
|
||||||
|
ColumnPolicyProperty {
|
||||||
|
with,
|
||||||
|
policy_name: "p".into(),
|
||||||
|
using_columns,
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}],
|
||||||
|
},]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snowflake_create_table_with_columns_projection_policy() {
|
||||||
|
for (sql, with) in [
|
||||||
|
(
|
||||||
|
"CREATE TABLE my_table (a INT WITH PROJECTION POLICY p)",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
("CREATE TABLE my_table (a INT PROJECTION POLICY p)", false),
|
||||||
|
] {
|
||||||
|
match snowflake().verified_stmt(sql) {
|
||||||
|
Statement::CreateTable(CreateTable { columns, .. }) => {
|
||||||
|
assert_eq!(
|
||||||
|
columns,
|
||||||
|
vec![ColumnDef {
|
||||||
|
name: "a".into(),
|
||||||
|
data_type: DataType::Int(None),
|
||||||
|
collation: None,
|
||||||
|
options: vec![ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Policy(ColumnPolicy::ProjectionPolicy(
|
||||||
|
ColumnPolicyProperty {
|
||||||
|
with,
|
||||||
|
policy_name: "p".into(),
|
||||||
|
using_columns: None,
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}],
|
||||||
|
},]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snowflake_create_table_with_columns_tags() {
|
||||||
|
for (sql, with) in [
|
||||||
|
(
|
||||||
|
"CREATE TABLE my_table (a INT WITH TAG (A='TAG A', B='TAG B'))",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"CREATE TABLE my_table (a INT TAG (A='TAG A', B='TAG B'))",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
match snowflake().verified_stmt(sql) {
|
||||||
|
Statement::CreateTable(CreateTable { columns, .. }) => {
|
||||||
|
assert_eq!(
|
||||||
|
columns,
|
||||||
|
vec![ColumnDef {
|
||||||
|
name: "a".into(),
|
||||||
|
data_type: DataType::Int(None),
|
||||||
|
collation: None,
|
||||||
|
options: vec![ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Tags(TagsColumnOption {
|
||||||
|
with,
|
||||||
|
tags: vec![
|
||||||
|
Tag::new("A".into(), "TAG A".into()),
|
||||||
|
Tag::new("B".into(), "TAG B".into()),
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
}],
|
||||||
|
},]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snowflake_create_table_with_several_column_options() {
|
||||||
|
let sql = concat!(
|
||||||
|
"CREATE TABLE my_table (",
|
||||||
|
"a INT IDENTITY WITH MASKING POLICY p1 USING (a, b) WITH TAG (A='TAG A', B='TAG B'), ",
|
||||||
|
"b TEXT COLLATE 'de_DE' PROJECTION POLICY p2 TAG (C='TAG C', D='TAG D')",
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
match snowflake().verified_stmt(sql) {
|
||||||
|
Statement::CreateTable(CreateTable { columns, .. }) => {
|
||||||
|
assert_eq!(
|
||||||
|
columns,
|
||||||
|
vec![
|
||||||
|
ColumnDef {
|
||||||
|
name: "a".into(),
|
||||||
|
data_type: DataType::Int(None),
|
||||||
|
collation: None,
|
||||||
|
options: vec![
|
||||||
|
ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Identity(IdentityPropertyKind::Identity(
|
||||||
|
IdentityProperty {
|
||||||
|
parameters: None,
|
||||||
|
order: None
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Policy(ColumnPolicy::MaskingPolicy(
|
||||||
|
ColumnPolicyProperty {
|
||||||
|
with: true,
|
||||||
|
policy_name: "p1".into(),
|
||||||
|
using_columns: Some(vec!["a".into(), "b".into()]),
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Tags(TagsColumnOption {
|
||||||
|
with: true,
|
||||||
|
tags: vec![
|
||||||
|
Tag::new("A".into(), "TAG A".into()),
|
||||||
|
Tag::new("B".into(), "TAG B".into()),
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ColumnDef {
|
||||||
|
name: "b".into(),
|
||||||
|
data_type: DataType::Text,
|
||||||
|
collation: Some(ObjectName(vec![Ident::with_quote('\'', "de_DE")])),
|
||||||
|
options: vec![
|
||||||
|
ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Policy(ColumnPolicy::ProjectionPolicy(
|
||||||
|
ColumnPolicyProperty {
|
||||||
|
with: false,
|
||||||
|
policy_name: "p2".into(),
|
||||||
|
using_columns: None,
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
ColumnOptionDef {
|
||||||
|
name: None,
|
||||||
|
option: ColumnOption::Tags(TagsColumnOption {
|
||||||
|
with: false,
|
||||||
|
tags: vec![
|
||||||
|
Tag::new("C".into(), "TAG C".into()),
|
||||||
|
Tag::new("D".into(), "TAG D".into()),
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_sf_create_or_replace_view_with_comment_missing_equal() {
|
fn parse_sf_create_or_replace_view_with_comment_missing_equal() {
|
||||||
assert!(snowflake_and_generic()
|
assert!(snowflake_and_generic()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue