diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index f998143c..4c145f91 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -1911,6 +1911,13 @@ pub enum ColumnOption { /// ``` /// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/creating-spatial-indexes.html Srid(Box), + /// MySQL specific: Column is invisible via SELECT * + /// Syntax: + /// ```sql + /// CREATE TABLE t (foo INT, bar INT INVISIBLE); + /// ``` + /// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/invisible-columns.html + Invisible, } impl fmt::Display for ColumnOption { @@ -2029,6 +2036,9 @@ impl fmt::Display for ColumnOption { Srid(srid) => { write!(f, "SRID {srid}") } + Invisible => { + write!(f, "INVISIBLE") + } } } } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 0c205b82..5913fe16 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -918,6 +918,7 @@ impl Spanned for ColumnOption { ColumnOption::Policy(..) => Span::empty(), ColumnOption::Tags(..) => Span::empty(), ColumnOption::Srid(..) => Span::empty(), + ColumnOption::Invisible => Span::empty(), } } } diff --git a/src/keywords.rs b/src/keywords.rs index 3358e284..e0590b69 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -499,6 +499,7 @@ define_keywords!( INTERSECTION, INTERVAL, INTO, + INVISIBLE, INVOKER, IO, IS, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d661efd4..f47d4b9b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8144,6 +8144,8 @@ impl<'a> Parser<'a> { Keyword::REPLACE, ])?, ))) + } else if self.parse_keyword(Keyword::INVISIBLE) { + Ok(Some(ColumnOption::Invisible)) } else { Ok(None) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 720c1e49..f46abc7d 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -17138,7 +17138,7 @@ fn test_parse_semantic_view_table_factor() { } let ast_sql = r#"SELECT * FROM SEMANTIC_VIEW( - my_model + my_model DIMENSIONS DATE_PART('year', date_col), region_name METRICS orders.revenue, orders.count WHERE active = true @@ -17193,3 +17193,56 @@ fn parse_adjacent_string_literal_concatenation() { let sql = "SELECT * FROM t WHERE col = 'Hello' \n ' ' \t 'World!'"; dialects.one_statement_parses_to(sql, r"SELECT * FROM t WHERE col = 'Hello World!'"); } + +#[test] +fn parse_invisible_column() { + let sql = r#"CREATE TABLE t (foo INT, bar INT INVISIBLE)"#; + let stmt = verified_stmt(sql); + match stmt { + Statement::CreateTable(CreateTable { columns, .. }) => { + assert_eq!( + columns, + vec![ + ColumnDef { + name: "foo".into(), + data_type: DataType::Int(None), + options: vec![] + }, + ColumnDef { + name: "bar".into(), + data_type: DataType::Int(None), + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::Invisible + }] + } + ] + ); + } + _ => panic!("Unexpected statement {stmt}"), + } + + let sql = r#"ALTER TABLE t ADD COLUMN bar INT INVISIBLE"#; + let stmt = verified_stmt(sql); + match stmt { + Statement::AlterTable { operations, .. } => { + assert_eq!( + operations, + vec![AlterTableOperation::AddColumn { + column_keyword: true, + if_not_exists: false, + column_def: ColumnDef { + name: "bar".into(), + data_type: DataType::Int(None), + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::Invisible + }] + }, + column_position: None + }] + ); + } + _ => panic!("Unexpected statement {stmt}"), + } +}