Skip to content
Draft
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
18 changes: 17 additions & 1 deletion src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use crate::ast::{
};
use crate::display_utils::{DisplayCommaSeparated, Indent, NewLine, SpaceOrNewline};
use crate::keywords::Keyword;
use crate::tokenizer::{Span, Token};
use crate::tokenizer::{Comment, Span, Token};

/// Index column type.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
Expand Down Expand Up @@ -1202,10 +1202,15 @@ pub struct ColumnDef {
pub name: Ident,
pub data_type: DataType,
pub options: Vec<ColumnOptionDef>,
/// Leading comment for the column.
pub leading_comment: Option<Comment>,
}

impl fmt::Display for ColumnDef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(leading_comment) = &self.leading_comment {
write!(f, "{leading_comment}")?;
}
if self.data_type == DataType::Unspecified {
write!(f, "{}", self.name)?;
} else {
Expand Down Expand Up @@ -2286,6 +2291,8 @@ pub struct CreateTable {
/// Snowflake "REQUIRE USER" clause for dybamic tables
/// <https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table>
pub require_user: bool,
/// Leading comment for the table.
pub leading_comment: Option<Comment>,
}

impl fmt::Display for CreateTable {
Expand All @@ -2297,6 +2304,9 @@ impl fmt::Display for CreateTable {
// `CREATE TABLE t AS SELECT a from t2`
// Columns provided for CREATE TABLE AS:
// `CREATE TABLE t (a INT) AS SELECT a from t2`
if let Some(leading_comment) = &self.leading_comment {
write!(f, "{leading_comment}")?;
}
write!(
f,
"CREATE {or_replace}{external}{global}{temporary}{transient}{volatile}{dynamic}{iceberg}TABLE {if_not_exists}{name}",
Expand Down Expand Up @@ -3515,10 +3525,16 @@ pub struct AlterTable {
pub iceberg: bool,
/// Token that represents the end of the statement (semicolon or EOF)
pub end_token: AttachedToken,
/// Leading comment which appears before the `ALTER` keyword
pub leading_comment: Option<Comment>,
}

impl fmt::Display for AlterTable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(comment) = &self.leading_comment {
write!(f, "{comment}")?;
}

if self.iceberg {
write!(f, "ALTER ICEBERG TABLE ")?;
} else {
Expand Down
12 changes: 12 additions & 0 deletions src/ast/helpers/stmt_create_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use crate::ast::{
};

use crate::parser::ParserError;
use crate::tokenizer::Comment;

/// Builder for create table statement variant ([1]).
///
Expand All @@ -49,6 +50,7 @@ use crate::parser::ParserError;
/// name: Ident::new("c1"),
/// data_type: DataType::Int(None),
/// options: vec![],
/// leading_comment: None,
/// }]);
/// // You can access internal elements with ease
/// assert!(builder.if_not_exists);
Expand Down Expand Up @@ -115,6 +117,7 @@ pub struct CreateTableBuilder {
pub refresh_mode: Option<RefreshModeKind>,
pub initialize: Option<InitializeKind>,
pub require_user: bool,
pub leading_comment: Option<Comment>,
}

impl CreateTableBuilder {
Expand Down Expand Up @@ -171,6 +174,7 @@ impl CreateTableBuilder {
refresh_mode: None,
initialize: None,
require_user: false,
leading_comment: None,
}
}
pub fn or_replace(mut self, or_replace: bool) -> Self {
Expand Down Expand Up @@ -431,6 +435,11 @@ impl CreateTableBuilder {
self
}

pub fn leading_comment(mut self, leading_comment: Option<Comment>) -> Self {
self.leading_comment = leading_comment;
self
}

pub fn build(self) -> Statement {
CreateTable {
or_replace: self.or_replace,
Expand Down Expand Up @@ -484,6 +493,7 @@ impl CreateTableBuilder {
refresh_mode: self.refresh_mode,
initialize: self.initialize,
require_user: self.require_user,
leading_comment: self.leading_comment,
}
.into()
}
Expand Down Expand Up @@ -548,6 +558,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
refresh_mode,
initialize,
require_user,
leading_comment,
}) => Ok(Self {
or_replace,
temporary,
Expand Down Expand Up @@ -600,6 +611,7 @@ impl TryFrom<Statement> for CreateTableBuilder {
refresh_mode,
initialize,
require_user,
leading_comment,
}),
_ => Err(ParserError::ParserError(format!(
"Expected create table statement, but received: {stmt}"
Expand Down
2 changes: 2 additions & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,7 @@ impl Spanned for CreateTable {
refresh_mode: _,
initialize: _,
require_user: _,
leading_comment: _, // Option<Comment>
} = self;

union_spans(
Expand All @@ -572,6 +573,7 @@ impl Spanned for ColumnDef {
name,
data_type: _, // enum
options,
leading_comment: _,
} = self;

union_spans(core::iter::once(name.span).chain(options.iter().map(|i| i.span())))
Expand Down
19 changes: 16 additions & 3 deletions src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use crate::ast::{
use crate::dialect::{Dialect, Precedence};
use crate::keywords::Keyword;
use crate::parser::{IsOptional, Parser, ParserError};
use crate::tokenizer::Token;
use crate::tokenizer::{Comment, Token};
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
#[cfg(not(feature = "std"))]
Expand Down Expand Up @@ -210,6 +210,7 @@ impl Dialect for SnowflakeDialect {
}

fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
let leading_comment = parser.parse_leading_comment();
if parser.parse_keyword(Keyword::BEGIN) {
return Some(parser.parse_begin_exception_end());
}
Expand Down Expand Up @@ -261,7 +262,15 @@ impl Dialect for SnowflakeDialect {
return Some(parse_create_stage(or_replace, temporary, parser));
} else if parser.parse_keyword(Keyword::TABLE) {
return Some(parse_create_table(
or_replace, global, temporary, volatile, transient, iceberg, dynamic, parser,
or_replace,
global,
temporary,
volatile,
transient,
iceberg,
dynamic,
parser,
leading_comment,
));
} else if parser.parse_keyword(Keyword::DATABASE) {
return Some(parse_create_database(or_replace, transient, parser));
Expand Down Expand Up @@ -305,7 +314,9 @@ impl Dialect for SnowflakeDialect {
//Give back Keyword::SHOW
parser.prev_token();
}

if leading_comment.is_some() {
parser.prev_token();
}
None
}

Expand Down Expand Up @@ -630,6 +641,7 @@ pub fn parse_create_table(
iceberg: bool,
dynamic: bool,
parser: &mut Parser,
leading_comment: Option<Comment>,
) -> Result<Statement, ParserError> {
let if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
let table_name = parser.parse_object_name(false)?;
Expand All @@ -643,6 +655,7 @@ pub fn parse_create_table(
.iceberg(iceberg)
.global(global)
.dynamic(dynamic)
.leading_comment(leading_comment)
.hive_formats(Some(Default::default()));

// Snowflake does not enforce order of the parameters in the statement. The parser needs to
Expand Down
Loading