diff --git a/.tool-versions b/.tool-versions index d090dbab8..6c98ab3f5 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -rust 1.73.0 \ No newline at end of file +rust 1.75.0 \ No newline at end of file diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 961bbef79..cd0cf1797 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -45,6 +45,18 @@ pub enum AlterTableOperation { /// . column_def: ColumnDef, }, + /// `DISABLE ROW LEVEL SECURITY` + /// + /// Note: this is a PostgreSQL-specific operation. + DisableRowLevelSecurity, + /// `DISABLE RULE rewrite_rule_name` + /// + /// Note: this is a PostgreSQL-specific operation. + DisableRule { name: Ident }, + /// `DISABLE TRIGGER [ trigger_name | ALL | USER ]` + /// + /// Note: this is a PostgreSQL-specific operation. + DisableTrigger { name: Ident }, /// `DROP CONSTRAINT [ IF EXISTS ] ` DropConstraint { if_exists: bool, @@ -61,6 +73,34 @@ pub enum AlterTableOperation { /// /// Note: this is a MySQL-specific operation. DropPrimaryKey, + /// `ENABLE ALWAYS RULE rewrite_rule_name` + /// + /// Note: this is a PostgreSQL-specific operation. + EnableAlwaysRule { name: Ident }, + /// `ENABLE ALWAYS TRIGGER trigger_name` + /// + /// Note: this is a PostgreSQL-specific operation. + EnableAlwaysTrigger { name: Ident }, + /// `ENABLE REPLICA RULE rewrite_rule_name` + /// + /// Note: this is a PostgreSQL-specific operation. + EnableReplicaRule { name: Ident }, + /// `ENABLE REPLICA TRIGGER trigger_name` + /// + /// Note: this is a PostgreSQL-specific operation. + EnableReplicaTrigger { name: Ident }, + /// `ENABLE ROW LEVEL SECURITY` + /// + /// Note: this is a PostgreSQL-specific operation. + EnableRowLevelSecurity, + /// `ENABLE RULE rewrite_rule_name` + /// + /// Note: this is a PostgreSQL-specific operation. + EnableRule { name: Ident }, + /// `ENABLE TRIGGER [ trigger_name | ALL | USER ]` + /// + /// Note: this is a PostgreSQL-specific operation. + EnableTrigger { name: Ident }, /// `RENAME TO PARTITION (partition=val)` RenamePartitions { old_partitions: Vec, @@ -143,6 +183,15 @@ impl fmt::Display for AlterTableOperation { AlterTableOperation::AlterColumn { column_name, op } => { write!(f, "ALTER COLUMN {column_name} {op}") } + AlterTableOperation::DisableRowLevelSecurity => { + write!(f, "DISABLE ROW LEVEL SECURITY") + } + AlterTableOperation::DisableRule { name } => { + write!(f, "DISABLE RULE {name}") + } + AlterTableOperation::DisableTrigger { name } => { + write!(f, "DISABLE TRIGGER {name}") + } AlterTableOperation::DropPartitions { partitions, if_exists, @@ -177,6 +226,27 @@ impl fmt::Display for AlterTableOperation { column_name, if *cascade { " CASCADE" } else { "" } ), + AlterTableOperation::EnableAlwaysRule { name } => { + write!(f, "ENABLE ALWAYS RULE {name}") + } + AlterTableOperation::EnableAlwaysTrigger { name } => { + write!(f, "ENABLE ALWAYS TRIGGER {name}") + } + AlterTableOperation::EnableReplicaRule { name } => { + write!(f, "ENABLE REPLICA RULE {name}") + } + AlterTableOperation::EnableReplicaTrigger { name } => { + write!(f, "ENABLE REPLICA TRIGGER {name}") + } + AlterTableOperation::EnableRowLevelSecurity => { + write!(f, "ENABLE ROW LEVEL SECURITY") + } + AlterTableOperation::EnableRule { name } => { + write!(f, "ENABLE RULE {name}") + } + AlterTableOperation::EnableTrigger { name } => { + write!(f, "ENABLE TRIGGER {name}") + } AlterTableOperation::RenamePartitions { old_partitions, new_partitions, diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index eab30ccd2..5167be249 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -349,6 +349,7 @@ mod tests { } } + #[allow(clippy::needless_raw_string_hashes)] let statement = r#"SELECT 'Wayne\'s World'"#; let res1 = Parser::parse_sql(&MySqlDialect {}, statement); let res2 = Parser::parse_sql(&WrappedDialect(MySqlDialect {}), statement); diff --git a/src/keywords.rs b/src/keywords.rs index a6a29159e..eb3d74f67 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -222,6 +222,7 @@ define_keywords!( DETAIL, DETERMINISTIC, DIRECTORY, + DISABLE, DISCARD, DISCONNECT, DISTINCT, @@ -240,6 +241,7 @@ define_keywords!( ELEMENTS, ELSE, EMPTY, + ENABLE, ENCODING, ENCRYPTION, END, @@ -537,6 +539,7 @@ define_keywords!( REPAIR, REPEATABLE, REPLACE, + REPLICA, REPLICATION, RESET, RESPECT, @@ -557,6 +560,7 @@ define_keywords!( ROWID, ROWS, ROW_NUMBER, + RULE, RUN, SAFE_CAST, SAVEPOINT, @@ -565,6 +569,7 @@ define_keywords!( SCROLL, SEARCH, SECOND, + SECURITY, SELECT, SEMI, SENSITIVE, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 853ab3d17..df3370c73 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4636,6 +4636,48 @@ impl<'a> Parser<'a> { new_column_name, } } + } else if self.parse_keyword(Keyword::DISABLE) { + if self.parse_keywords(&[Keyword::ROW, Keyword::LEVEL, Keyword::SECURITY]) { + AlterTableOperation::DisableRowLevelSecurity {} + } else if self.parse_keyword(Keyword::RULE) { + let name = self.parse_identifier()?; + AlterTableOperation::DisableRule { name } + } else if self.parse_keyword(Keyword::TRIGGER) { + let name = self.parse_identifier()?; + AlterTableOperation::DisableTrigger { name } + } else { + return self.expected( + "ROW LEVEL SECURITY, RULE, or TRIGGER after DISABLE", + self.peek_token(), + ); + } + } else if self.parse_keyword(Keyword::ENABLE) { + if self.parse_keywords(&[Keyword::ALWAYS, Keyword::RULE]) { + let name = self.parse_identifier()?; + AlterTableOperation::EnableAlwaysRule { name } + } else if self.parse_keywords(&[Keyword::ALWAYS, Keyword::TRIGGER]) { + let name = self.parse_identifier()?; + AlterTableOperation::EnableAlwaysTrigger { name } + } else if self.parse_keywords(&[Keyword::ROW, Keyword::LEVEL, Keyword::SECURITY]) { + AlterTableOperation::EnableRowLevelSecurity {} + } else if self.parse_keywords(&[Keyword::REPLICA, Keyword::RULE]) { + let name = self.parse_identifier()?; + AlterTableOperation::EnableReplicaRule { name } + } else if self.parse_keywords(&[Keyword::REPLICA, Keyword::TRIGGER]) { + let name = self.parse_identifier()?; + AlterTableOperation::EnableReplicaTrigger { name } + } else if self.parse_keyword(Keyword::RULE) { + let name = self.parse_identifier()?; + AlterTableOperation::EnableRule { name } + } else if self.parse_keyword(Keyword::TRIGGER) { + let name = self.parse_identifier()?; + AlterTableOperation::EnableTrigger { name } + } else { + return self.expected( + "ALWAYS, REPLICA, ROW LEVEL SECURITY, RULE, or TRIGGER after ENABLE", + self.peek_token(), + ); + } } else if self.parse_keyword(Keyword::DROP) { if self.parse_keywords(&[Keyword::IF, Keyword::EXISTS, Keyword::PARTITION]) { self.expect_token(&Token::LParen)?; diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 0400b21c5..e2d1843bc 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -727,10 +727,7 @@ impl<'a> Tokenizer<'a> { // match binary literal that starts with 0x if s == "0" && chars.peek() == Some(&'x') { chars.next(); - let s2 = peeking_take_while( - chars, - |ch| matches!(ch, '0'..='9' | 'A'..='F' | 'a'..='f'), - ); + let s2 = peeking_take_while(chars, |ch| ch.is_ascii_hexdigit()); return Ok(Some(Token::HexStringLiteral(s2))); } @@ -1077,7 +1074,7 @@ impl<'a> Tokenizer<'a> { match chars.peek() { Some('$') => { chars.next(); - for (_, c) in value.chars().enumerate() { + for c in value.chars() { let next_char = chars.next(); if Some(c) != next_char { return self.tokenizer_error( diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index b075a9b4d..0c1463f92 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -563,6 +563,27 @@ fn parse_alter_table_constraints_rename() { } } +#[test] +fn parse_alter_table_disable() { + pg_and_generic().verified_stmt("ALTER TABLE tab DISABLE ROW LEVEL SECURITY"); + pg_and_generic().verified_stmt("ALTER TABLE tab DISABLE RULE rule_name"); + pg_and_generic().verified_stmt("ALTER TABLE tab DISABLE TRIGGER ALL"); + pg_and_generic().verified_stmt("ALTER TABLE tab DISABLE TRIGGER USER"); + pg_and_generic().verified_stmt("ALTER TABLE tab DISABLE TRIGGER trigger_name"); +} + +#[test] +fn parse_alter_table_enable() { + pg_and_generic().verified_stmt("ALTER TABLE tab ENABLE ALWAYS RULE rule_name"); + pg_and_generic().verified_stmt("ALTER TABLE tab ENABLE ALWAYS TRIGGER trigger_name"); + pg_and_generic().verified_stmt("ALTER TABLE tab ENABLE REPLICA TRIGGER trigger_name"); + pg_and_generic().verified_stmt("ALTER TABLE tab ENABLE REPLICA RULE rule_name"); + pg_and_generic().verified_stmt("ALTER TABLE tab ENABLE ROW LEVEL SECURITY"); + pg_and_generic().verified_stmt("ALTER TABLE tab ENABLE RULE rule_name"); + pg_and_generic().verified_stmt("ALTER TABLE tab ENABLE TRIGGER ALL"); + pg_and_generic().verified_stmt("ALTER TABLE tab ENABLE TRIGGER USER"); + pg_and_generic().verified_stmt("ALTER TABLE tab ENABLE TRIGGER trigger_name"); +} #[test] fn parse_alter_table_alter_column() { pg().one_statement_parses_to( @@ -3256,7 +3277,7 @@ fn parse_dollar_quoted_string() { let stmt = pg().parse_sql_statements(sql).unwrap(); - let projection = match stmt.get(0).unwrap() { + let projection = match stmt.first().unwrap() { Statement::Query(query) => match &*query.body { SetExpr::Select(select) => &select.projection, _ => unreachable!(),