mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-22 23:14:07 +00:00
Add support for Create Iceberg Table statement for Snowflake parser (#1664)
This commit is contained in:
parent
183274e274
commit
c7c0de6551
10 changed files with 296 additions and 5 deletions
|
@ -36,7 +36,8 @@ use super::{
|
||||||
CommentDef, Expr, FileFormat, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat,
|
CommentDef, Expr, FileFormat, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat,
|
||||||
HiveRowFormat, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert,
|
HiveRowFormat, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert,
|
||||||
OneOrManyWithParens, OrderByExpr, Query, RowAccessPolicy, SelectItem, Setting, SqlOption,
|
OneOrManyWithParens, OrderByExpr, Query, RowAccessPolicy, SelectItem, Setting, SqlOption,
|
||||||
SqliteOnConflict, TableEngine, TableObject, TableWithJoins, Tag, WrappedCollection,
|
SqliteOnConflict, StorageSerializationPolicy, TableEngine, TableObject, TableWithJoins, Tag,
|
||||||
|
WrappedCollection,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// CREATE INDEX statement.
|
/// CREATE INDEX statement.
|
||||||
|
@ -117,6 +118,7 @@ pub struct CreateTable {
|
||||||
pub if_not_exists: bool,
|
pub if_not_exists: bool,
|
||||||
pub transient: bool,
|
pub transient: bool,
|
||||||
pub volatile: bool,
|
pub volatile: bool,
|
||||||
|
pub iceberg: bool,
|
||||||
/// Table name
|
/// Table name
|
||||||
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
|
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
|
||||||
pub name: ObjectName,
|
pub name: ObjectName,
|
||||||
|
@ -192,6 +194,21 @@ pub struct CreateTable {
|
||||||
/// Snowflake "WITH TAG" clause
|
/// Snowflake "WITH TAG" clause
|
||||||
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
|
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
|
||||||
pub with_tags: Option<Vec<Tag>>,
|
pub with_tags: Option<Vec<Tag>>,
|
||||||
|
/// Snowflake "EXTERNAL_VOLUME" clause for Iceberg tables
|
||||||
|
/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
|
||||||
|
pub external_volume: Option<String>,
|
||||||
|
/// Snowflake "BASE_LOCATION" clause for Iceberg tables
|
||||||
|
/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
|
||||||
|
pub base_location: Option<String>,
|
||||||
|
/// Snowflake "CATALOG" clause for Iceberg tables
|
||||||
|
/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
|
||||||
|
pub catalog: Option<String>,
|
||||||
|
/// Snowflake "CATALOG_SYNC" clause for Iceberg tables
|
||||||
|
/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
|
||||||
|
pub catalog_sync: Option<String>,
|
||||||
|
/// Snowflake "STORAGE_SERIALIZATION_POLICY" clause for Iceberg tables
|
||||||
|
/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
|
||||||
|
pub storage_serialization_policy: Option<StorageSerializationPolicy>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for CreateTable {
|
impl Display for CreateTable {
|
||||||
|
@ -205,7 +222,7 @@ impl Display for CreateTable {
|
||||||
// `CREATE TABLE t (a INT) AS SELECT a from t2`
|
// `CREATE TABLE t (a INT) AS SELECT a from t2`
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"CREATE {or_replace}{external}{global}{temporary}{transient}{volatile}TABLE {if_not_exists}{name}",
|
"CREATE {or_replace}{external}{global}{temporary}{transient}{volatile}{iceberg}TABLE {if_not_exists}{name}",
|
||||||
or_replace = if self.or_replace { "OR REPLACE " } else { "" },
|
or_replace = if self.or_replace { "OR REPLACE " } else { "" },
|
||||||
external = if self.external { "EXTERNAL " } else { "" },
|
external = if self.external { "EXTERNAL " } else { "" },
|
||||||
global = self.global
|
global = self.global
|
||||||
|
@ -221,6 +238,8 @@ impl Display for CreateTable {
|
||||||
temporary = if self.temporary { "TEMPORARY " } else { "" },
|
temporary = if self.temporary { "TEMPORARY " } else { "" },
|
||||||
transient = if self.transient { "TRANSIENT " } else { "" },
|
transient = if self.transient { "TRANSIENT " } else { "" },
|
||||||
volatile = if self.volatile { "VOLATILE " } else { "" },
|
volatile = if self.volatile { "VOLATILE " } else { "" },
|
||||||
|
// Only for Snowflake
|
||||||
|
iceberg = if self.iceberg { "ICEBERG " } else { "" },
|
||||||
name = self.name,
|
name = self.name,
|
||||||
)?;
|
)?;
|
||||||
if let Some(on_cluster) = &self.on_cluster {
|
if let Some(on_cluster) = &self.on_cluster {
|
||||||
|
@ -382,6 +401,31 @@ impl Display for CreateTable {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(external_volume) = self.external_volume.as_ref() {
|
||||||
|
write!(f, " EXTERNAL_VOLUME = '{external_volume}'")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(catalog) = self.catalog.as_ref() {
|
||||||
|
write!(f, " CATALOG = '{catalog}'")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.iceberg {
|
||||||
|
if let Some(base_location) = self.base_location.as_ref() {
|
||||||
|
write!(f, " BASE_LOCATION = '{base_location}'")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(catalog_sync) = self.catalog_sync.as_ref() {
|
||||||
|
write!(f, " CATALOG_SYNC = '{catalog_sync}'")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(storage_serialization_policy) = self.storage_serialization_policy.as_ref() {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
" STORAGE_SERIALIZATION_POLICY = {storage_serialization_policy}"
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
if self.copy_grants {
|
if self.copy_grants {
|
||||||
write!(f, " COPY GRANTS")?;
|
write!(f, " COPY GRANTS")?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ use super::super::dml::CreateTable;
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
ClusteredBy, ColumnDef, CommentDef, Expr, FileFormat, HiveDistributionStyle, HiveFormat, Ident,
|
ClusteredBy, ColumnDef, CommentDef, Expr, FileFormat, HiveDistributionStyle, HiveFormat, Ident,
|
||||||
ObjectName, OnCommit, OneOrManyWithParens, Query, RowAccessPolicy, SqlOption, Statement,
|
ObjectName, OnCommit, OneOrManyWithParens, Query, RowAccessPolicy, SqlOption, Statement,
|
||||||
TableConstraint, TableEngine, Tag, WrappedCollection,
|
StorageSerializationPolicy, TableConstraint, TableEngine, Tag, WrappedCollection,
|
||||||
};
|
};
|
||||||
use crate::parser::ParserError;
|
use crate::parser::ParserError;
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@ pub struct CreateTableBuilder {
|
||||||
pub if_not_exists: bool,
|
pub if_not_exists: bool,
|
||||||
pub transient: bool,
|
pub transient: bool,
|
||||||
pub volatile: bool,
|
pub volatile: bool,
|
||||||
|
pub iceberg: bool,
|
||||||
pub name: ObjectName,
|
pub name: ObjectName,
|
||||||
pub columns: Vec<ColumnDef>,
|
pub columns: Vec<ColumnDef>,
|
||||||
pub constraints: Vec<TableConstraint>,
|
pub constraints: Vec<TableConstraint>,
|
||||||
|
@ -107,6 +108,11 @@ pub struct CreateTableBuilder {
|
||||||
pub with_aggregation_policy: Option<ObjectName>,
|
pub with_aggregation_policy: Option<ObjectName>,
|
||||||
pub with_row_access_policy: Option<RowAccessPolicy>,
|
pub with_row_access_policy: Option<RowAccessPolicy>,
|
||||||
pub with_tags: Option<Vec<Tag>>,
|
pub with_tags: Option<Vec<Tag>>,
|
||||||
|
pub base_location: Option<String>,
|
||||||
|
pub external_volume: Option<String>,
|
||||||
|
pub catalog: Option<String>,
|
||||||
|
pub catalog_sync: Option<String>,
|
||||||
|
pub storage_serialization_policy: Option<StorageSerializationPolicy>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateTableBuilder {
|
impl CreateTableBuilder {
|
||||||
|
@ -119,6 +125,7 @@ impl CreateTableBuilder {
|
||||||
if_not_exists: false,
|
if_not_exists: false,
|
||||||
transient: false,
|
transient: false,
|
||||||
volatile: false,
|
volatile: false,
|
||||||
|
iceberg: false,
|
||||||
name,
|
name,
|
||||||
columns: vec![],
|
columns: vec![],
|
||||||
constraints: vec![],
|
constraints: vec![],
|
||||||
|
@ -155,6 +162,11 @@ impl CreateTableBuilder {
|
||||||
with_aggregation_policy: None,
|
with_aggregation_policy: None,
|
||||||
with_row_access_policy: None,
|
with_row_access_policy: None,
|
||||||
with_tags: None,
|
with_tags: None,
|
||||||
|
base_location: None,
|
||||||
|
external_volume: None,
|
||||||
|
catalog: None,
|
||||||
|
catalog_sync: None,
|
||||||
|
storage_serialization_policy: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn or_replace(mut self, or_replace: bool) -> Self {
|
pub fn or_replace(mut self, or_replace: bool) -> Self {
|
||||||
|
@ -192,6 +204,11 @@ impl CreateTableBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iceberg(mut self, iceberg: bool) -> Self {
|
||||||
|
self.iceberg = iceberg;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn columns(mut self, columns: Vec<ColumnDef>) -> Self {
|
pub fn columns(mut self, columns: Vec<ColumnDef>) -> Self {
|
||||||
self.columns = columns;
|
self.columns = columns;
|
||||||
self
|
self
|
||||||
|
@ -371,6 +388,34 @@ impl CreateTableBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn base_location(mut self, base_location: Option<String>) -> Self {
|
||||||
|
self.base_location = base_location;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn external_volume(mut self, external_volume: Option<String>) -> Self {
|
||||||
|
self.external_volume = external_volume;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn catalog(mut self, catalog: Option<String>) -> Self {
|
||||||
|
self.catalog = catalog;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn catalog_sync(mut self, catalog_sync: Option<String>) -> Self {
|
||||||
|
self.catalog_sync = catalog_sync;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn storage_serialization_policy(
|
||||||
|
mut self,
|
||||||
|
storage_serialization_policy: Option<StorageSerializationPolicy>,
|
||||||
|
) -> Self {
|
||||||
|
self.storage_serialization_policy = storage_serialization_policy;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Statement {
|
pub fn build(self) -> Statement {
|
||||||
Statement::CreateTable(CreateTable {
|
Statement::CreateTable(CreateTable {
|
||||||
or_replace: self.or_replace,
|
or_replace: self.or_replace,
|
||||||
|
@ -380,6 +425,7 @@ impl CreateTableBuilder {
|
||||||
if_not_exists: self.if_not_exists,
|
if_not_exists: self.if_not_exists,
|
||||||
transient: self.transient,
|
transient: self.transient,
|
||||||
volatile: self.volatile,
|
volatile: self.volatile,
|
||||||
|
iceberg: self.iceberg,
|
||||||
name: self.name,
|
name: self.name,
|
||||||
columns: self.columns,
|
columns: self.columns,
|
||||||
constraints: self.constraints,
|
constraints: self.constraints,
|
||||||
|
@ -416,6 +462,11 @@ impl CreateTableBuilder {
|
||||||
with_aggregation_policy: self.with_aggregation_policy,
|
with_aggregation_policy: self.with_aggregation_policy,
|
||||||
with_row_access_policy: self.with_row_access_policy,
|
with_row_access_policy: self.with_row_access_policy,
|
||||||
with_tags: self.with_tags,
|
with_tags: self.with_tags,
|
||||||
|
base_location: self.base_location,
|
||||||
|
external_volume: self.external_volume,
|
||||||
|
catalog: self.catalog,
|
||||||
|
catalog_sync: self.catalog_sync,
|
||||||
|
storage_serialization_policy: self.storage_serialization_policy,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -435,6 +486,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
transient,
|
transient,
|
||||||
volatile,
|
volatile,
|
||||||
|
iceberg,
|
||||||
name,
|
name,
|
||||||
columns,
|
columns,
|
||||||
constraints,
|
constraints,
|
||||||
|
@ -471,6 +523,11 @@ impl TryFrom<Statement> for CreateTableBuilder {
|
||||||
with_aggregation_policy,
|
with_aggregation_policy,
|
||||||
with_row_access_policy,
|
with_row_access_policy,
|
||||||
with_tags,
|
with_tags,
|
||||||
|
base_location,
|
||||||
|
external_volume,
|
||||||
|
catalog,
|
||||||
|
catalog_sync,
|
||||||
|
storage_serialization_policy,
|
||||||
}) => Ok(Self {
|
}) => Ok(Self {
|
||||||
or_replace,
|
or_replace,
|
||||||
temporary,
|
temporary,
|
||||||
|
@ -505,6 +562,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
|
||||||
clustered_by,
|
clustered_by,
|
||||||
options,
|
options,
|
||||||
strict,
|
strict,
|
||||||
|
iceberg,
|
||||||
copy_grants,
|
copy_grants,
|
||||||
enable_schema_evolution,
|
enable_schema_evolution,
|
||||||
change_tracking,
|
change_tracking,
|
||||||
|
@ -515,6 +573,11 @@ impl TryFrom<Statement> for CreateTableBuilder {
|
||||||
with_row_access_policy,
|
with_row_access_policy,
|
||||||
with_tags,
|
with_tags,
|
||||||
volatile,
|
volatile,
|
||||||
|
base_location,
|
||||||
|
external_volume,
|
||||||
|
catalog,
|
||||||
|
catalog_sync,
|
||||||
|
storage_serialization_policy,
|
||||||
}),
|
}),
|
||||||
_ => Err(ParserError::ParserError(format!(
|
_ => Err(ParserError::ParserError(format!(
|
||||||
"Expected create table statement, but received: {stmt}"
|
"Expected create table statement, but received: {stmt}"
|
||||||
|
|
|
@ -8396,6 +8396,29 @@ impl fmt::Display for SessionParamValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Snowflake StorageSerializationPolicy for Iceberg Tables
|
||||||
|
/// ```sql
|
||||||
|
/// [ STORAGE_SERIALIZATION_POLICY = { COMPATIBLE | OPTIMIZED } ]
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
pub enum StorageSerializationPolicy {
|
||||||
|
Compatible,
|
||||||
|
Optimized,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for StorageSerializationPolicy {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
StorageSerializationPolicy::Compatible => write!(f, "COMPATIBLE"),
|
||||||
|
StorageSerializationPolicy::Optimized => write!(f, "OPTIMIZED"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -532,6 +532,7 @@ impl Spanned for CreateTable {
|
||||||
if_not_exists: _, // bool
|
if_not_exists: _, // bool
|
||||||
transient: _, // bool
|
transient: _, // bool
|
||||||
volatile: _, // bool
|
volatile: _, // bool
|
||||||
|
iceberg: _, // bool, Snowflake specific
|
||||||
name,
|
name,
|
||||||
columns,
|
columns,
|
||||||
constraints,
|
constraints,
|
||||||
|
@ -568,6 +569,11 @@ impl Spanned for CreateTable {
|
||||||
with_aggregation_policy: _, // todo, Snowflake specific
|
with_aggregation_policy: _, // todo, Snowflake specific
|
||||||
with_row_access_policy: _, // todo, Snowflake specific
|
with_row_access_policy: _, // todo, Snowflake specific
|
||||||
with_tags: _, // todo, Snowflake specific
|
with_tags: _, // todo, Snowflake specific
|
||||||
|
external_volume: _, // todo, Snowflake specific
|
||||||
|
base_location: _, // todo, Snowflake specific
|
||||||
|
catalog: _, // todo, Snowflake specific
|
||||||
|
catalog_sync: _, // todo, Snowflake specific
|
||||||
|
storage_serialization_policy: _, // todo, Snowflake specific
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
union_spans(
|
union_spans(
|
||||||
|
|
|
@ -37,6 +37,7 @@ use alloc::string::String;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use alloc::{format, vec};
|
use alloc::{format, vec};
|
||||||
|
use sqlparser::ast::StorageSerializationPolicy;
|
||||||
|
|
||||||
use super::keywords::RESERVED_FOR_IDENTIFIER;
|
use super::keywords::RESERVED_FOR_IDENTIFIER;
|
||||||
|
|
||||||
|
@ -130,16 +131,19 @@ impl Dialect for SnowflakeDialect {
|
||||||
let mut temporary = false;
|
let mut temporary = false;
|
||||||
let mut volatile = false;
|
let mut volatile = false;
|
||||||
let mut transient = false;
|
let mut transient = false;
|
||||||
|
let mut iceberg = false;
|
||||||
|
|
||||||
match parser.parse_one_of_keywords(&[
|
match parser.parse_one_of_keywords(&[
|
||||||
Keyword::TEMP,
|
Keyword::TEMP,
|
||||||
Keyword::TEMPORARY,
|
Keyword::TEMPORARY,
|
||||||
Keyword::VOLATILE,
|
Keyword::VOLATILE,
|
||||||
Keyword::TRANSIENT,
|
Keyword::TRANSIENT,
|
||||||
|
Keyword::ICEBERG,
|
||||||
]) {
|
]) {
|
||||||
Some(Keyword::TEMP | Keyword::TEMPORARY) => temporary = true,
|
Some(Keyword::TEMP | Keyword::TEMPORARY) => temporary = true,
|
||||||
Some(Keyword::VOLATILE) => volatile = true,
|
Some(Keyword::VOLATILE) => volatile = true,
|
||||||
Some(Keyword::TRANSIENT) => transient = true,
|
Some(Keyword::TRANSIENT) => transient = true,
|
||||||
|
Some(Keyword::ICEBERG) => iceberg = true,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +152,7 @@ impl Dialect for SnowflakeDialect {
|
||||||
return Some(parse_create_stage(or_replace, temporary, parser));
|
return Some(parse_create_stage(or_replace, temporary, parser));
|
||||||
} else if parser.parse_keyword(Keyword::TABLE) {
|
} else if parser.parse_keyword(Keyword::TABLE) {
|
||||||
return Some(parse_create_table(
|
return Some(parse_create_table(
|
||||||
or_replace, global, temporary, volatile, transient, parser,
|
or_replace, global, temporary, volatile, transient, iceberg, parser,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
// need to go back with the cursor
|
// need to go back with the cursor
|
||||||
|
@ -325,12 +329,14 @@ fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statem
|
||||||
|
|
||||||
/// Parse snowflake create table statement.
|
/// Parse snowflake create table statement.
|
||||||
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
|
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
|
||||||
|
/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
|
||||||
pub fn parse_create_table(
|
pub fn parse_create_table(
|
||||||
or_replace: bool,
|
or_replace: bool,
|
||||||
global: Option<bool>,
|
global: Option<bool>,
|
||||||
temporary: bool,
|
temporary: bool,
|
||||||
volatile: bool,
|
volatile: bool,
|
||||||
transient: bool,
|
transient: bool,
|
||||||
|
iceberg: bool,
|
||||||
parser: &mut Parser,
|
parser: &mut Parser,
|
||||||
) -> Result<Statement, ParserError> {
|
) -> Result<Statement, ParserError> {
|
||||||
let if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
|
let if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
|
||||||
|
@ -342,6 +348,7 @@ pub fn parse_create_table(
|
||||||
.temporary(temporary)
|
.temporary(temporary)
|
||||||
.transient(transient)
|
.transient(transient)
|
||||||
.volatile(volatile)
|
.volatile(volatile)
|
||||||
|
.iceberg(iceberg)
|
||||||
.global(global)
|
.global(global)
|
||||||
.hive_formats(Some(Default::default()));
|
.hive_formats(Some(Default::default()));
|
||||||
|
|
||||||
|
@ -468,6 +475,28 @@ pub fn parse_create_table(
|
||||||
let on_commit = Some(parser.parse_create_table_on_commit()?);
|
let on_commit = Some(parser.parse_create_table_on_commit()?);
|
||||||
builder = builder.on_commit(on_commit);
|
builder = builder.on_commit(on_commit);
|
||||||
}
|
}
|
||||||
|
Keyword::EXTERNAL_VOLUME => {
|
||||||
|
parser.expect_token(&Token::Eq)?;
|
||||||
|
builder.external_volume = Some(parser.parse_literal_string()?);
|
||||||
|
}
|
||||||
|
Keyword::CATALOG => {
|
||||||
|
parser.expect_token(&Token::Eq)?;
|
||||||
|
builder.catalog = Some(parser.parse_literal_string()?);
|
||||||
|
}
|
||||||
|
Keyword::BASE_LOCATION => {
|
||||||
|
parser.expect_token(&Token::Eq)?;
|
||||||
|
builder.base_location = Some(parser.parse_literal_string()?);
|
||||||
|
}
|
||||||
|
Keyword::CATALOG_SYNC => {
|
||||||
|
parser.expect_token(&Token::Eq)?;
|
||||||
|
builder.catalog_sync = Some(parser.parse_literal_string()?);
|
||||||
|
}
|
||||||
|
Keyword::STORAGE_SERIALIZATION_POLICY => {
|
||||||
|
parser.expect_token(&Token::Eq)?;
|
||||||
|
|
||||||
|
builder.storage_serialization_policy =
|
||||||
|
Some(parse_storage_serialization_policy(parser)?);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return parser.expected("end of statement", next_token);
|
return parser.expected("end of statement", next_token);
|
||||||
}
|
}
|
||||||
|
@ -502,9 +531,29 @@ pub fn parse_create_table(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if iceberg && builder.base_location.is_none() {
|
||||||
|
return Err(ParserError::ParserError(
|
||||||
|
"BASE_LOCATION is required for ICEBERG tables".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(builder.build())
|
Ok(builder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_storage_serialization_policy(
|
||||||
|
parser: &mut Parser,
|
||||||
|
) -> Result<StorageSerializationPolicy, ParserError> {
|
||||||
|
let next_token = parser.next_token();
|
||||||
|
match &next_token.token {
|
||||||
|
Token::Word(w) => match w.keyword {
|
||||||
|
Keyword::COMPATIBLE => Ok(StorageSerializationPolicy::Compatible),
|
||||||
|
Keyword::OPTIMIZED => Ok(StorageSerializationPolicy::Optimized),
|
||||||
|
_ => parser.expected("storage_serialization_policy", next_token),
|
||||||
|
},
|
||||||
|
_ => parser.expected("storage_serialization_policy", next_token),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_create_stage(
|
pub fn parse_create_stage(
|
||||||
or_replace: bool,
|
or_replace: bool,
|
||||||
temporary: bool,
|
temporary: bool,
|
||||||
|
|
|
@ -121,6 +121,7 @@ define_keywords!(
|
||||||
AVRO,
|
AVRO,
|
||||||
BACKWARD,
|
BACKWARD,
|
||||||
BASE64,
|
BASE64,
|
||||||
|
BASE_LOCATION,
|
||||||
BEFORE,
|
BEFORE,
|
||||||
BEGIN,
|
BEGIN,
|
||||||
BEGIN_FRAME,
|
BEGIN_FRAME,
|
||||||
|
@ -158,6 +159,7 @@ define_keywords!(
|
||||||
CASES,
|
CASES,
|
||||||
CAST,
|
CAST,
|
||||||
CATALOG,
|
CATALOG,
|
||||||
|
CATALOG_SYNC,
|
||||||
CATCH,
|
CATCH,
|
||||||
CEIL,
|
CEIL,
|
||||||
CEILING,
|
CEILING,
|
||||||
|
@ -191,6 +193,7 @@ define_keywords!(
|
||||||
COMMENT,
|
COMMENT,
|
||||||
COMMIT,
|
COMMIT,
|
||||||
COMMITTED,
|
COMMITTED,
|
||||||
|
COMPATIBLE,
|
||||||
COMPRESSION,
|
COMPRESSION,
|
||||||
COMPUTE,
|
COMPUTE,
|
||||||
CONCURRENTLY,
|
CONCURRENTLY,
|
||||||
|
@ -329,6 +332,7 @@ define_keywords!(
|
||||||
EXTENDED,
|
EXTENDED,
|
||||||
EXTENSION,
|
EXTENSION,
|
||||||
EXTERNAL,
|
EXTERNAL,
|
||||||
|
EXTERNAL_VOLUME,
|
||||||
EXTRACT,
|
EXTRACT,
|
||||||
FAIL,
|
FAIL,
|
||||||
FAILOVER,
|
FAILOVER,
|
||||||
|
@ -397,6 +401,7 @@ define_keywords!(
|
||||||
HOSTS,
|
HOSTS,
|
||||||
HOUR,
|
HOUR,
|
||||||
HOURS,
|
HOURS,
|
||||||
|
ICEBERG,
|
||||||
ID,
|
ID,
|
||||||
IDENTITY,
|
IDENTITY,
|
||||||
IDENTITY_INSERT,
|
IDENTITY_INSERT,
|
||||||
|
@ -599,6 +604,7 @@ define_keywords!(
|
||||||
OPERATOR,
|
OPERATOR,
|
||||||
OPTIMIZATION,
|
OPTIMIZATION,
|
||||||
OPTIMIZE,
|
OPTIMIZE,
|
||||||
|
OPTIMIZED,
|
||||||
OPTIMIZER_COSTS,
|
OPTIMIZER_COSTS,
|
||||||
OPTION,
|
OPTION,
|
||||||
OPTIONS,
|
OPTIONS,
|
||||||
|
@ -806,6 +812,7 @@ define_keywords!(
|
||||||
STDOUT,
|
STDOUT,
|
||||||
STEP,
|
STEP,
|
||||||
STORAGE_INTEGRATION,
|
STORAGE_INTEGRATION,
|
||||||
|
STORAGE_SERIALIZATION_POLICY,
|
||||||
STORED,
|
STORED,
|
||||||
STRICT,
|
STRICT,
|
||||||
STRING,
|
STRING,
|
||||||
|
|
|
@ -660,6 +660,7 @@ fn test_duckdb_union_datatype() {
|
||||||
if_not_exists: Default::default(),
|
if_not_exists: Default::default(),
|
||||||
transient: Default::default(),
|
transient: Default::default(),
|
||||||
volatile: Default::default(),
|
volatile: Default::default(),
|
||||||
|
iceberg: Default::default(),
|
||||||
name: ObjectName(vec!["tbl1".into()]),
|
name: ObjectName(vec!["tbl1".into()]),
|
||||||
columns: vec![
|
columns: vec![
|
||||||
ColumnDef {
|
ColumnDef {
|
||||||
|
@ -737,7 +738,12 @@ fn test_duckdb_union_datatype() {
|
||||||
default_ddl_collation: Default::default(),
|
default_ddl_collation: Default::default(),
|
||||||
with_aggregation_policy: Default::default(),
|
with_aggregation_policy: Default::default(),
|
||||||
with_row_access_policy: Default::default(),
|
with_row_access_policy: Default::default(),
|
||||||
with_tags: Default::default()
|
with_tags: Default::default(),
|
||||||
|
base_location: Default::default(),
|
||||||
|
external_volume: Default::default(),
|
||||||
|
catalog: Default::default(),
|
||||||
|
catalog_sync: Default::default(),
|
||||||
|
storage_serialization_policy: Default::default(),
|
||||||
}),
|
}),
|
||||||
stmt
|
stmt
|
||||||
);
|
);
|
||||||
|
|
|
@ -1539,6 +1539,7 @@ fn parse_create_table_with_valid_options() {
|
||||||
clustered_by: None,
|
clustered_by: None,
|
||||||
options: None,
|
options: None,
|
||||||
strict: false,
|
strict: false,
|
||||||
|
iceberg: false,
|
||||||
copy_grants: false,
|
copy_grants: false,
|
||||||
enable_schema_evolution: None,
|
enable_schema_evolution: None,
|
||||||
change_tracking: None,
|
change_tracking: None,
|
||||||
|
@ -1548,6 +1549,11 @@ fn parse_create_table_with_valid_options() {
|
||||||
with_aggregation_policy: None,
|
with_aggregation_policy: None,
|
||||||
with_row_access_policy: None,
|
with_row_access_policy: None,
|
||||||
with_tags: None,
|
with_tags: None,
|
||||||
|
base_location: None,
|
||||||
|
external_volume: None,
|
||||||
|
catalog: None,
|
||||||
|
catalog_sync: None,
|
||||||
|
storage_serialization_policy: None,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1641,6 +1647,7 @@ fn parse_create_table_with_identity_column() {
|
||||||
if_not_exists: false,
|
if_not_exists: false,
|
||||||
transient: false,
|
transient: false,
|
||||||
volatile: false,
|
volatile: false,
|
||||||
|
iceberg: false,
|
||||||
name: ObjectName(vec![Ident {
|
name: ObjectName(vec![Ident {
|
||||||
value: "mytable".to_string(),
|
value: "mytable".to_string(),
|
||||||
quote_style: None,
|
quote_style: None,
|
||||||
|
@ -1695,6 +1702,11 @@ fn parse_create_table_with_identity_column() {
|
||||||
with_aggregation_policy: None,
|
with_aggregation_policy: None,
|
||||||
with_row_access_policy: None,
|
with_row_access_policy: None,
|
||||||
with_tags: None,
|
with_tags: None,
|
||||||
|
base_location: None,
|
||||||
|
external_volume: None,
|
||||||
|
catalog: None,
|
||||||
|
catalog_sync: None,
|
||||||
|
storage_serialization_policy: None,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5043,6 +5043,7 @@ fn parse_trigger_related_functions() {
|
||||||
if_not_exists: false,
|
if_not_exists: false,
|
||||||
transient: false,
|
transient: false,
|
||||||
volatile: false,
|
volatile: false,
|
||||||
|
iceberg: false,
|
||||||
name: ObjectName(vec![Ident::new("emp")]),
|
name: ObjectName(vec![Ident::new("emp")]),
|
||||||
columns: vec![
|
columns: vec![
|
||||||
ColumnDef {
|
ColumnDef {
|
||||||
|
@ -5109,6 +5110,11 @@ fn parse_trigger_related_functions() {
|
||||||
with_aggregation_policy: None,
|
with_aggregation_policy: None,
|
||||||
with_row_access_policy: None,
|
with_row_access_policy: None,
|
||||||
with_tags: None,
|
with_tags: None,
|
||||||
|
base_location: None,
|
||||||
|
external_volume: None,
|
||||||
|
catalog: None,
|
||||||
|
catalog_sync: None,
|
||||||
|
storage_serialization_policy: None,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -849,6 +849,81 @@ fn test_snowflake_create_table_with_several_column_options() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snowflake_create_iceberg_table_all_options() {
|
||||||
|
match snowflake().verified_stmt("CREATE ICEBERG TABLE my_table (a INT, b INT) \
|
||||||
|
CLUSTER BY (a, b) EXTERNAL_VOLUME = 'volume' CATALOG = 'SNOWFLAKE' BASE_LOCATION = 'relative/path' CATALOG_SYNC = 'OPEN_CATALOG' \
|
||||||
|
STORAGE_SERIALIZATION_POLICY = COMPATIBLE COPY GRANTS CHANGE_TRACKING=TRUE DATA_RETENTION_TIME_IN_DAYS=5 MAX_DATA_EXTENSION_TIME_IN_DAYS=10 \
|
||||||
|
WITH AGGREGATION POLICY policy_name WITH ROW ACCESS POLICY policy_name ON (a) WITH TAG (A='TAG A', B='TAG B')") {
|
||||||
|
Statement::CreateTable(CreateTable {
|
||||||
|
name, cluster_by, base_location,
|
||||||
|
external_volume, catalog, catalog_sync,
|
||||||
|
storage_serialization_policy, change_tracking,
|
||||||
|
copy_grants, data_retention_time_in_days,
|
||||||
|
max_data_extension_time_in_days, with_aggregation_policy,
|
||||||
|
with_row_access_policy, with_tags, ..
|
||||||
|
}) => {
|
||||||
|
assert_eq!("my_table", name.to_string());
|
||||||
|
assert_eq!(
|
||||||
|
Some(WrappedCollection::Parentheses(vec![
|
||||||
|
Ident::new("a"),
|
||||||
|
Ident::new("b"),
|
||||||
|
])),
|
||||||
|
cluster_by
|
||||||
|
);
|
||||||
|
assert_eq!("relative/path", base_location.unwrap());
|
||||||
|
assert_eq!("volume", external_volume.unwrap());
|
||||||
|
assert_eq!("SNOWFLAKE", catalog.unwrap());
|
||||||
|
assert_eq!("OPEN_CATALOG", catalog_sync.unwrap());
|
||||||
|
assert_eq!(StorageSerializationPolicy::Compatible, storage_serialization_policy.unwrap());
|
||||||
|
assert!(change_tracking.unwrap());
|
||||||
|
assert!(copy_grants);
|
||||||
|
assert_eq!(Some(5), data_retention_time_in_days);
|
||||||
|
assert_eq!(Some(10), max_data_extension_time_in_days);
|
||||||
|
assert_eq!(
|
||||||
|
Some("WITH ROW ACCESS POLICY policy_name ON (a)".to_string()),
|
||||||
|
with_row_access_policy.map(|policy| policy.to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Some("policy_name".to_string()),
|
||||||
|
with_aggregation_policy.map(|name| name.to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(Some(vec![
|
||||||
|
Tag::new("A".into(), "TAG A".into()),
|
||||||
|
Tag::new("B".into(), "TAG B".into()),
|
||||||
|
]), with_tags);
|
||||||
|
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snowflake_create_iceberg_table() {
|
||||||
|
match snowflake()
|
||||||
|
.verified_stmt("CREATE ICEBERG TABLE my_table (a INT) BASE_LOCATION = 'relative_path'")
|
||||||
|
{
|
||||||
|
Statement::CreateTable(CreateTable {
|
||||||
|
name,
|
||||||
|
base_location,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
assert_eq!("my_table", name.to_string());
|
||||||
|
assert_eq!("relative_path", base_location.unwrap());
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_snowflake_create_iceberg_table_without_location() {
|
||||||
|
let res = snowflake().parse_sql_statements("CREATE ICEBERG TABLE my_table (a INT)");
|
||||||
|
assert_eq!(
|
||||||
|
ParserError::ParserError("BASE_LOCATION is required for ICEBERG tables".to_string()),
|
||||||
|
res.unwrap_err()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[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