Skip to content

Commit e5ac170

Browse files
author
aleksei.p
committed
update
1 parent 2f076ed commit e5ac170

File tree

6 files changed

+305
-266
lines changed

6 files changed

+305
-266
lines changed

src/ast/ddl.rs

+62-6
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,13 @@ impl fmt::Display for ColumnOptionDef {
10981098
}
10991099
}
11001100

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

1124+
/// A format of parameters of identity column.
1125+
///
1126+
/// It is [Snowflake] specific.
1127+
/// Syntax
1128+
/// ```sql
1129+
/// (seed , increment) | START num INCREMENT num
1130+
/// ```
1131+
/// [MS SQL Server] uses one way of representing these parameters.
1132+
/// Syntax
1133+
/// ```sql
1134+
/// (seed , increment)
1135+
/// ```
1136+
/// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property
1137+
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
11171138
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
11181139
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11191140
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
@@ -1135,7 +1156,7 @@ pub struct IdentityParameters {
11351156
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
11361157
pub enum IdentityOrder {
11371158
Order,
1138-
Noorder,
1159+
NoOrder,
11391160
}
11401161

11411162
impl fmt::Display for Identity {
@@ -1176,11 +1197,18 @@ impl fmt::Display for IdentityOrder {
11761197
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
11771198
match self {
11781199
IdentityOrder::Order => write!(f, " ORDER"),
1179-
IdentityOrder::Noorder => write!(f, " NOORDER"),
1200+
IdentityOrder::NoOrder => write!(f, " NOORDER"),
11801201
}
11811202
}
11821203
}
11831204

1205+
/// Column policy that identify a security policy of access to a column.
1206+
/// Syntax
1207+
/// ```sql
1208+
/// [ WITH ] MASKING POLICY <policy_name> [ USING ( <col_name> , <cond_col1> , ... ) ]
1209+
/// [ WITH ] PROJECTION POLICY <policy_name>
1210+
/// ```
1211+
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
11841212
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
11851213
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11861214
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
@@ -1193,6 +1221,7 @@ pub enum ColumnPolicy {
11931221
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11941222
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
11951223
pub struct ColumnPolicyProperty {
1224+
pub with: bool,
11961225
pub policy_name: Ident,
11971226
pub using_columns: Option<Vec<Ident>>,
11981227
}
@@ -1203,10 +1232,37 @@ impl fmt::Display for ColumnPolicy {
12031232
ColumnPolicy::MaskingPolicy(property) => ("MASKING POLICY", property),
12041233
ColumnPolicy::ProjectionPolicy(property) => ("PROJECTION POLICY", property),
12051234
};
1206-
write!(f, "WITH {command} {}", property.policy_name)?;
1235+
if property.with {
1236+
write!(f, "WITH ")?;
1237+
}
1238+
write!(f, "{command} {}", property.policy_name)?;
12071239
if let Some(using_columns) = &property.using_columns {
1208-
write!(f, "USING ({})", display_comma_separated(using_columns))?;
1240+
write!(f, " USING ({})", display_comma_separated(using_columns))?;
1241+
}
1242+
Ok(())
1243+
}
1244+
}
1245+
1246+
/// Tags option of column
1247+
/// Syntax
1248+
/// ```sql
1249+
/// [ WITH ] TAG ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] )
1250+
/// ```
1251+
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1252+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1253+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1254+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1255+
pub struct TagsColumnOption {
1256+
pub with: bool,
1257+
pub tags: Vec<Tag>,
1258+
}
1259+
1260+
impl fmt::Display for TagsColumnOption {
1261+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1262+
if self.with {
1263+
write!(f, "WITH ")?;
12091264
}
1265+
write!(f, "TAG ({})", display_comma_separated(&self.tags))?;
12101266
Ok(())
12111267
}
12121268
}
@@ -1303,7 +1359,7 @@ pub enum ColumnOption {
13031359
/// [ WITH ] TAG ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] )
13041360
/// ```
13051361
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1306-
Tags(Vec<Tag>),
1362+
Tags(TagsColumnOption),
13071363
}
13081364

13091365
impl fmt::Display for ColumnOption {
@@ -1412,7 +1468,7 @@ impl fmt::Display for ColumnOption {
14121468
write!(f, "{parameters}")
14131469
}
14141470
Tags(tags) => {
1415-
write!(f, "WITH TAG ({})", display_comma_separated(tags))
1471+
write!(f, "{tags}")
14161472
}
14171473
}
14181474
}

src/ast/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub use self::ddl::{
4444
ConstraintCharacteristics, Deduplicate, DeferrableInitial, GeneratedAs,
4545
GeneratedExpressionMode, Identity, IdentityFormat, IdentityOrder, IdentityParameters,
4646
IdentityProperty, IndexOption, IndexType, KeyOrIndexDisplay, Owner, Partition, ProcedureParam,
47-
ReferentialAction, TableConstraint, UserDefinedTypeCompositeAttributeDef,
47+
ReferentialAction, TableConstraint, TagsColumnOption, UserDefinedTypeCompositeAttributeDef,
4848
UserDefinedTypeRepresentation, ViewColumnDef,
4949
};
5050
pub use self::dml::{CreateIndex, CreateTable, Delete, Insert};

src/dialect/mod.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub use self::postgresql::PostgreSqlDialect;
4949
pub use self::redshift::RedshiftSqlDialect;
5050
pub use self::snowflake::SnowflakeDialect;
5151
pub use self::sqlite::SQLiteDialect;
52-
use crate::ast::{Expr, Statement};
52+
use crate::ast::{ColumnOption, Expr, Statement};
5353
pub use crate::keywords;
5454
use crate::keywords::Keyword;
5555
use crate::parser::{Parser, ParserError};
@@ -478,6 +478,19 @@ pub trait Dialect: Debug + Any {
478478
None
479479
}
480480

481+
/// Dialect-specific column option parser override
482+
///
483+
/// This method is called to parse the next column option.
484+
///
485+
/// If `None` is returned, falls back to the default behavior.
486+
fn parse_column_option(
487+
&self,
488+
_parser: &mut Parser,
489+
) -> Option<Result<Option<ColumnOption>, ParserError>> {
490+
// return None to fall back to the default behavior
491+
None
492+
}
493+
481494
/// Decide the lexical Precedence of operators.
482495
///
483496
/// Uses (APPROXIMATELY) <https://www.postgresql.org/docs/7.0/operators.htm#AEN2026> as a reference

src/dialect/snowflake.rs

+104-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ use crate::ast::helpers::stmt_data_loading::{
2222
DataLoadingOption, DataLoadingOptionType, DataLoadingOptions, StageLoadSelectItem,
2323
StageParamsObject,
2424
};
25-
use crate::ast::{CommentDef, Ident, ObjectName, RowAccessPolicy, Statement, WrappedCollection};
25+
use crate::ast::{
26+
ColumnOption, ColumnPolicy, ColumnPolicyProperty, CommentDef, Ident, Identity, IdentityFormat,
27+
IdentityOrder, IdentityParameters, IdentityProperty, ObjectName, RowAccessPolicy, Statement,
28+
WrappedCollection,
29+
};
2630
use crate::dialect::{Dialect, Precedence};
2731
use crate::keywords::Keyword;
2832
use crate::parser::{Parser, ParserError};
@@ -149,6 +153,41 @@ impl Dialect for SnowflakeDialect {
149153
None
150154
}
151155

156+
fn parse_column_option(
157+
&self,
158+
parser: &mut Parser,
159+
) -> Option<Result<Option<ColumnOption>, ParserError>> {
160+
let with = parser.parse_keyword(Keyword::WITH);
161+
162+
if parser.parse_keyword(Keyword::IDENTITY) {
163+
Some(
164+
parse_identity_property(parser)
165+
.map(|p| Some(ColumnOption::Identity(Identity::Identity(p)))),
166+
)
167+
} else if parser.parse_keyword(Keyword::AUTOINCREMENT) {
168+
Some(
169+
parse_identity_property(parser)
170+
.map(|p| Some(ColumnOption::Identity(Identity::Autoincrement(p)))),
171+
)
172+
} else if parser.parse_keywords(&[Keyword::MASKING, Keyword::POLICY]) {
173+
Some(
174+
parse_column_policy_property(parser, with)
175+
.map(|p| Some(ColumnOption::Policy(ColumnPolicy::MaskingPolicy(p)))),
176+
)
177+
} else if parser.parse_keywords(&[Keyword::PROJECTION, Keyword::POLICY]) {
178+
Some(
179+
parse_column_policy_property(parser, with)
180+
.map(|p| Some(ColumnOption::Policy(ColumnPolicy::ProjectionPolicy(p)))),
181+
)
182+
} else {
183+
// needs to revert initial state of parser if dialect finds any matching
184+
if with {
185+
parser.prev_token();
186+
}
187+
None
188+
}
189+
}
190+
152191
fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
153192
let token = parser.peek_token();
154193
// Snowflake supports the `:` cast operator unlike other dialects
@@ -772,3 +811,67 @@ fn parse_parentheses_options(parser: &mut Parser) -> Result<Vec<DataLoadingOptio
772811
}
773812
Ok(options)
774813
}
814+
815+
/// Parsing a property of identity or autoincrement column option
816+
/// Syntax:
817+
/// ```sql
818+
/// [ (seed , increment) | START num INCREMENT num ] [ ORDER | NOORDER ]
819+
/// ```
820+
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
821+
fn parse_identity_property(parser: &mut Parser) -> Result<IdentityProperty, ParserError> {
822+
let parameters = if parser.consume_token(&Token::LParen) {
823+
let seed = parser.parse_number()?;
824+
parser.expect_token(&Token::Comma)?;
825+
let increment = parser.parse_number()?;
826+
parser.expect_token(&Token::RParen)?;
827+
828+
Some(IdentityFormat::FunctionCall(IdentityParameters {
829+
seed,
830+
increment,
831+
}))
832+
} else if parser.parse_keyword(Keyword::START) {
833+
let seed = parser.parse_number()?;
834+
parser.expect_keyword(Keyword::INCREMENT)?;
835+
let increment = parser.parse_number()?;
836+
837+
Some(IdentityFormat::StartAndIncrement(IdentityParameters {
838+
seed,
839+
increment,
840+
}))
841+
} else {
842+
None
843+
};
844+
let order = match parser.parse_one_of_keywords(&[Keyword::ORDER, Keyword::NOORDER]) {
845+
Some(Keyword::ORDER) => Some(IdentityOrder::Order),
846+
Some(Keyword::NOORDER) => Some(IdentityOrder::NoOrder),
847+
_ => None,
848+
};
849+
Ok(IdentityProperty { parameters, order })
850+
}
851+
852+
/// Parsing a policy property of column option
853+
/// Syntax:
854+
/// ```sql
855+
/// <policy_name> [ USING ( <col_name> , <cond_col1> , ... )
856+
/// ```
857+
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
858+
fn parse_column_policy_property(
859+
parser: &mut Parser,
860+
with: bool,
861+
) -> Result<ColumnPolicyProperty, ParserError> {
862+
let policy_name = parser.parse_identifier(false)?;
863+
let using_columns = if parser.parse_keyword(Keyword::USING) {
864+
parser.expect_token(&Token::LParen)?;
865+
let columns = parser.parse_comma_separated(|p| p.parse_identifier(false))?;
866+
parser.expect_token(&Token::RParen)?;
867+
Some(columns)
868+
} else {
869+
None
870+
};
871+
872+
Ok(ColumnPolicyProperty {
873+
with,
874+
policy_name,
875+
using_columns,
876+
})
877+
}

0 commit comments

Comments
 (0)