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
13 changes: 10 additions & 3 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ use crate::ast::{
HiveFormat, HiveIOFormat, HiveRowFormat, HiveSetLocation, Ident, InitializeKind,
MySQLColumnPosition, ObjectName, OnCommit, OneOrManyWithParens, OperateFunctionArg,
OrderByExpr, ProjectionSelect, Query, RefreshModeKind, RowAccessPolicy, SequenceOptions,
Spanned, SqlOption, StorageSerializationPolicy, TableVersion, Tag, TriggerEvent,
TriggerExecBody, TriggerObject, TriggerPeriod, TriggerReferencing, Value, ValueWithSpan,
WrappedCollection,
Spanned, SqlOption, StorageLifecyclePolicy, StorageSerializationPolicy, TableVersion, Tag,
TriggerEvent, TriggerExecBody, TriggerObject, TriggerPeriod, TriggerReferencing, Value,
ValueWithSpan, WrappedCollection,
};
use crate::display_utils::{DisplayCommaSeparated, Indent, NewLine, SpaceOrNewline};
use crate::keywords::Keyword;
Expand Down Expand Up @@ -2999,6 +2999,9 @@ pub struct CreateTable {
/// Snowflake "WITH ROW ACCESS POLICY" clause
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
pub with_row_access_policy: Option<RowAccessPolicy>,
/// Snowflake "WITH STORAGE LIFECYCLE POLICY" clause
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
pub with_storage_lifecycle_policy: Option<StorageLifecyclePolicy>,
/// Snowflake "WITH TAG" clause
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
pub with_tags: Option<Vec<Tag>>,
Expand Down Expand Up @@ -3300,6 +3303,10 @@ impl fmt::Display for CreateTable {
write!(f, " {row_access_policy}",)?;
}

if let Some(storage_lifecycle_policy) = &self.with_storage_lifecycle_policy {
write!(f, " {storage_lifecycle_policy}",)?;
}

if let Some(tag) = &self.with_tags {
write!(f, " WITH TAG ({})", display_comma_separated(tag.as_slice()))?;
}
Expand Down
17 changes: 15 additions & 2 deletions src/ast/helpers/stmt_create_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ use crate::ast::{
ClusteredBy, ColumnDef, CommentDef, CreateTable, CreateTableLikeKind, CreateTableOptions,
DistStyle, Expr, FileFormat, ForValues, HiveDistributionStyle, HiveFormat, Ident,
InitializeKind, ObjectName, OnCommit, OneOrManyWithParens, Query, RefreshModeKind,
RowAccessPolicy, Statement, StorageSerializationPolicy, TableConstraint, TableVersion, Tag,
WrappedCollection,
RowAccessPolicy, Statement, StorageLifecyclePolicy, StorageSerializationPolicy,
TableConstraint, TableVersion, Tag, WrappedCollection,
};

use crate::parser::ParserError;
Expand Down Expand Up @@ -147,6 +147,8 @@ pub struct CreateTableBuilder {
pub with_aggregation_policy: Option<ObjectName>,
/// Optional row access policy applied to the table.
pub with_row_access_policy: Option<RowAccessPolicy>,
/// Optional storage lifecycle policy applied to the table.
pub with_storage_lifecycle_policy: Option<StorageLifecyclePolicy>,
/// Optional tags/labels attached to the table metadata.
pub with_tags: Option<Vec<Tag>>,
/// Optional base location for staged data.
Expand Down Expand Up @@ -222,6 +224,7 @@ impl CreateTableBuilder {
default_ddl_collation: None,
with_aggregation_policy: None,
with_row_access_policy: None,
with_storage_lifecycle_policy: None,
with_tags: None,
base_location: None,
external_volume: None,
Expand Down Expand Up @@ -448,6 +451,14 @@ impl CreateTableBuilder {
self.with_row_access_policy = with_row_access_policy;
self
}
/// Attach a storage lifecycle policy to the table.
pub fn with_storage_lifecycle_policy(
mut self,
with_storage_lifecycle_policy: Option<StorageLifecyclePolicy>,
) -> Self {
self.with_storage_lifecycle_policy = with_storage_lifecycle_policy;
self
}
/// Attach tags/labels to the table metadata.
pub fn with_tags(mut self, with_tags: Option<Vec<Tag>>) -> Self {
self.with_tags = with_tags;
Expand Down Expand Up @@ -565,6 +576,7 @@ impl CreateTableBuilder {
default_ddl_collation: self.default_ddl_collation,
with_aggregation_policy: self.with_aggregation_policy,
with_row_access_policy: self.with_row_access_policy,
with_storage_lifecycle_policy: self.with_storage_lifecycle_policy,
with_tags: self.with_tags,
base_location: self.base_location,
external_volume: self.external_volume,
Expand Down Expand Up @@ -642,6 +654,7 @@ impl From<CreateTable> for CreateTableBuilder {
default_ddl_collation: table.default_ddl_collation,
with_aggregation_policy: table.with_aggregation_policy,
with_row_access_policy: table.with_row_access_policy,
with_storage_lifecycle_policy: table.with_storage_lifecycle_policy,
with_tags: table.with_tags,
base_location: table.base_location,
external_volume: table.external_volume,
Expand Down
24 changes: 24 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10350,6 +10350,30 @@ impl Display for RowAccessPolicy {
}
}

/// Snowflake `[ WITH ] STORAGE LIFECYCLE POLICY <policy_name> ON ( <col_name> [ , ... ] )`
///
/// <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 StorageLifecyclePolicy {
/// The fully-qualified policy object name.
pub policy: ObjectName,
/// Column names the policy applies to.
pub on: Vec<Ident>,
}

impl Display for StorageLifecyclePolicy {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"WITH STORAGE LIFECYCLE POLICY {} ON ({})",
self.policy,
display_comma_separated(self.on.as_slice())
)
}
}

/// Snowflake `WITH TAG ( tag_name = '<tag_value>', ...)`
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
Expand Down
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ impl Spanned for CreateTable {
default_ddl_collation: _, // string, no span
with_aggregation_policy: _, // todo, Snowflake specific
with_row_access_policy: _, // todo, Snowflake specific
with_storage_lifecycle_policy: _, // todo, Snowflake specific
with_tags: _, // todo, Snowflake specific
external_volume: _, // todo, Snowflake specific
base_location: _, // todo, Snowflake specific
Expand Down
16 changes: 15 additions & 1 deletion src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use crate::ast::{
IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, InitializeKind,
Insert, MultiTableInsertIntoClause, MultiTableInsertType, MultiTableInsertValue,
MultiTableInsertValues, MultiTableInsertWhenClause, ObjectName, ObjectNamePart,
RefreshModeKind, RowAccessPolicy, ShowObjects, SqlOption, Statement,
RefreshModeKind, RowAccessPolicy, ShowObjects, SqlOption, Statement, StorageLifecyclePolicy,
StorageSerializationPolicy, TableObject, TagsColumnOption, Value, WrappedCollection,
};
use crate::dialect::{Dialect, Precedence};
Expand Down Expand Up @@ -906,6 +906,7 @@ pub fn parse_create_table(
Keyword::WITH => {
parser.expect_one_of_keywords(&[
Keyword::AGGREGATION,
Keyword::STORAGE,
Keyword::TAG,
Keyword::ROW,
])?;
Expand All @@ -927,6 +928,19 @@ pub fn parse_create_table(
builder =
builder.with_row_access_policy(Some(RowAccessPolicy::new(policy, columns)))
}
Keyword::STORAGE => {
parser.expect_keywords(&[Keyword::LIFECYCLE, Keyword::POLICY])?;
let policy = parser.parse_object_name(false)?;
parser.expect_keyword_is(Keyword::ON)?;
parser.expect_token(&Token::LParen)?;
let columns = parser.parse_comma_separated(|p| p.parse_identifier())?;
parser.expect_token(&Token::RParen)?;

builder = builder.with_storage_lifecycle_policy(Some(StorageLifecyclePolicy {
policy,
on: columns,
}))
}
Keyword::TAG => {
parser.expect_token(&Token::LParen)?;
let tags = parser.parse_comma_separated(Parser::parse_tag)?;
Expand Down
1 change: 1 addition & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ define_keywords!(
LEFT,
LEFTARG,
LEVEL,
LIFECYCLE,
LIKE,
LIKE_REGEX,
LIMIT,
Expand Down
1 change: 1 addition & 0 deletions tests/sqlparser_duckdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,7 @@ fn test_duckdb_union_datatype() {
default_ddl_collation: Default::default(),
with_aggregation_policy: Default::default(),
with_row_access_policy: Default::default(),
with_storage_lifecycle_policy: Default::default(),
with_tags: Default::default(),
base_location: Default::default(),
external_volume: Default::default(),
Expand Down
2 changes: 2 additions & 0 deletions tests/sqlparser_mssql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1993,6 +1993,7 @@ fn parse_create_table_with_valid_options() {
default_ddl_collation: None,
with_aggregation_policy: None,
with_row_access_policy: None,
with_storage_lifecycle_policy: None,
with_tags: None,
base_location: None,
external_volume: None,
Expand Down Expand Up @@ -2163,6 +2164,7 @@ fn parse_create_table_with_identity_column() {
default_ddl_collation: None,
with_aggregation_policy: None,
with_row_access_policy: None,
with_storage_lifecycle_policy: None,
with_tags: None,
base_location: None,
external_volume: None,
Expand Down
1 change: 1 addition & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6461,6 +6461,7 @@ fn parse_trigger_related_functions() {
default_ddl_collation: None,
with_aggregation_policy: None,
with_row_access_policy: None,
with_storage_lifecycle_policy: None,
with_tags: None,
base_location: None,
external_volume: None,
Expand Down
40 changes: 40 additions & 0 deletions tests/sqlparser_snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,46 @@ fn test_snowflake_create_table_with_row_access_policy() {
}
}

#[test]
fn test_snowflake_create_table_with_storage_lifecycle_policy() {
// WITH keyword
match snowflake().verified_stmt(
"CREATE TABLE IF NOT EXISTS my_table (a NUMBER(38, 0), b VARIANT) WITH STORAGE LIFECYCLE POLICY dba.global_settings.my_policy ON (a)",
) {
Statement::CreateTable(CreateTable {
name,
with_storage_lifecycle_policy,
..
}) => {
assert_eq!("my_table", name.to_string());
let policy = with_storage_lifecycle_policy.unwrap();
assert_eq!("dba.global_settings.my_policy", policy.policy.to_string());
assert_eq!(vec![Ident::new("a")], policy.on);
}
_ => unreachable!(),
}

// Without WITH keyword
match snowflake()
.parse_sql_statements(
"CREATE TABLE my_table (a NUMBER(38,0)) STORAGE LIFECYCLE POLICY my_policy ON (a, b)",
)
.unwrap()
.pop()
.unwrap()
{
Statement::CreateTable(CreateTable {
with_storage_lifecycle_policy,
..
}) => {
let policy = with_storage_lifecycle_policy.unwrap();
assert_eq!("my_policy", policy.policy.to_string());
assert_eq!(vec![Ident::new("a"), Ident::new("b")], policy.on);
}
_ => unreachable!(),
}
}

#[test]
fn test_snowflake_create_table_with_tag() {
match snowflake()
Expand Down