Skip to content
Open
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
46 changes: 46 additions & 0 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6041,6 +6041,46 @@ impl From<AlterPolicy> for crate::ast::Statement {
}
}

/// DROP RULE statement.
///
/// Its source span covers `name` and `table_name`.
///
/// ```sql
/// DROP RULE [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ]
/// ```
///
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-droprule.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct DropRule {
/// `true` when `IF EXISTS` was present.
pub if_exists: bool,
/// Name of the rule to drop.
pub name: Ident,
/// Name of the table the rule applies to.
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub table_name: ObjectName,
/// Optional drop behavior (`CASCADE` or `RESTRICT`).
pub drop_behavior: Option<DropBehavior>,
}

impl fmt::Display for DropRule {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"DROP RULE {if_exists}{name} ON {table_name}",
if_exists = if self.if_exists { "IF EXISTS " } else { "" },
name = self.name,
table_name = self.table_name
)?;
if let Some(behavior) = &self.drop_behavior {
write!(f, " {behavior}")?;
}
Ok(())
}
}

/// A `CREATE FOREIGN TABLE` statement.
///
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createforeigntable.html)
Expand Down Expand Up @@ -6090,6 +6130,12 @@ impl fmt::Display for CreateForeignTable {
}
}

impl From<DropRule> for crate::ast::Statement {
fn from(v: DropRule) -> Self {
crate::ast::Statement::DropRule(v)
}
}

impl From<CreateForeignTable> for crate::ast::Statement {
fn from(v: CreateForeignTable) -> Self {
crate::ast::Statement::CreateForeignTable(v)
Expand Down
8 changes: 7 additions & 1 deletion src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub use self::ddl::{
CreateOperatorClass, CreateOperatorFamily, CreatePolicy, CreatePolicyCommand, CreatePolicyType,
CreateTable, CreateTextSearch, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
DistStyle, DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass,
DropOperatorFamily, DropOperatorSignature, DropPolicy, DropTrigger, ForValues,
DropOperatorFamily, DropOperatorSignature, DropPolicy, DropRule, DropTrigger, ForValues,
FunctionReturnType, GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty,
IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexColumn,
IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption, OperatorArgTypes,
Expand Down Expand Up @@ -4065,6 +4065,11 @@ pub enum Statement {
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-droppolicy.html)
DropPolicy(DropPolicy),
/// ```sql
/// DROP RULE
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-droprule.html)
DropRule(DropRule),
/// ```sql
/// DROP CONNECTOR
/// ```
/// See [Hive](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362034#LanguageManualDDL-DropConnector)
Expand Down Expand Up @@ -5853,6 +5858,7 @@ impl fmt::Display for Statement {
Ok(())
}
Statement::DropPolicy(policy) => write!(f, "{policy}"),
Statement::DropRule(rule) => write!(f, "{rule}"),
Statement::DropConnector { if_exists, name } => {
write!(
f,
Expand Down
29 changes: 18 additions & 11 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ use super::{
AttachedToken, BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef,
ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements,
ConflictTarget, ConnectByKind, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable,
CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeConstraintElement,
ExcludeSelectItem, Expr, ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg,
FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr,
HavingBound, IfStatement, IlikeSelectItem, InTarget, IndexColumn, Insert, Interpolate,
InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView,
LimitClause, MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr,
MergeInsertKind, MergeUpdateExpr, MergeUpdateKind, NamedParenthesizedList,
NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction,
OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause, Parens, Partition,
PartitionBoundValue, PivotValueSource, ProjectionSelect, Query, RaiseStatement,
RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement,
CreateTableOptions, Cte, Delete, DoUpdate, DropRule, ExceptSelectItem,
ExcludeConstraintElement, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, ForValues, FromTable,
Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList,
FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, InTarget,
IndexColumn, Insert, Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator,
JsonPath, JsonPathElem, LateralView, LimitClause, MatchRecognizePattern, Measure, Merge,
MergeAction, MergeClause, MergeInsertExpr, MergeInsertKind, MergeUpdateExpr, MergeUpdateKind,
NamedParenthesizedList, NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict,
OnConflictAction, OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause,
Parens, Partition, PartitionBoundValue, PivotValueSource, ProjectionSelect, Query,
RaiseStatement, RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement,
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, Setting, SqlOption, Statement,
Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor,
TableObject, TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Values,
Expand Down Expand Up @@ -490,6 +490,7 @@ impl Spanned for Statement {
Statement::AlterPolicy { .. } => Span::empty(),
Statement::AlterConnector { .. } => Span::empty(),
Statement::DropPolicy { .. } => Span::empty(),
Statement::DropRule(rule) => rule.span(),
Statement::DropConnector { .. } => Span::empty(),
Statement::ShowCatalogs { .. } => Span::empty(),
Statement::ShowDatabases { .. } => Span::empty(),
Expand Down Expand Up @@ -528,6 +529,12 @@ impl Spanned for Statement {
}
}

impl Spanned for DropRule {
fn span(&self) -> Span {
self.name.span.union(&self.table_name.span())
}
}

impl Spanned for Use {
fn span(&self) -> Span {
match self {
Expand Down
24 changes: 23 additions & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7776,6 +7776,8 @@ impl<'a> Parser<'a> {
return self.parse_drop_function().map(Into::into);
} else if self.parse_keyword(Keyword::POLICY) {
return self.parse_drop_policy().map(Into::into);
} else if self.parse_keyword(Keyword::RULE) {
return self.parse_drop_rule().map(Into::into);
} else if self.parse_keyword(Keyword::CONNECTOR) {
return self.parse_drop_connector();
} else if self.parse_keyword(Keyword::DOMAIN) {
Expand All @@ -7799,7 +7801,7 @@ impl<'a> Parser<'a> {
};
} else {
return self.expected_ref(
"COLLATION, CONNECTOR, DATABASE, EXTENSION, FUNCTION, INDEX, OPERATOR, POLICY, PROCEDURE, ROLE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW, USER or WAREHOUSE after DROP",
"COLLATION, CONNECTOR, DATABASE, EXTENSION, FUNCTION, INDEX, OPERATOR, POLICY, PROCEDURE, ROLE, RULE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW, USER or WAREHOUSE after DROP",
self.peek_token_ref(),
);
};
Expand Down Expand Up @@ -7879,6 +7881,26 @@ impl<'a> Parser<'a> {
drop_behavior,
})
}

/// ```sql
/// DROP RULE [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ]
/// ```
///
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-droprule.html)
fn parse_drop_rule(&mut self) -> Result<DropRule, ParserError> {
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
let name = self.parse_identifier()?;
self.expect_keyword_is(Keyword::ON)?;
let table_name = self.parse_object_name(false)?;
let drop_behavior = self.parse_optional_drop_behavior();
Ok(DropRule {
if_exists,
name,
table_name,
drop_behavior,
})
}

/// ```sql
/// DROP CONNECTOR [IF EXISTS] name
/// ```
Expand Down
84 changes: 84 additions & 0 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20496,6 +20496,90 @@ fn parse_stage_table_factor() {
);
}

#[test]
fn parse_drop_rule() {
let sql = "DROP RULE r ON t";
match all_dialects().verified_stmt(sql) {
Statement::DropRule(DropRule {
if_exists,
name,
table_name,
drop_behavior,
}) => {
assert!(!if_exists);
assert_eq!(name.to_string(), "r");
assert_eq!(table_name.to_string(), "t");
assert_eq!(drop_behavior, None);
}
_ => unreachable!("Expected DROP RULE"),
}

all_dialects().verified_stmt("DROP RULE IF EXISTS r ON s1.t CASCADE");
all_dialects().verified_stmt("DROP RULE r ON t RESTRICT");

// the table name is mandatory
assert_eq!(
all_dialects()
.parse_sql_statements("DROP RULE r")
.unwrap_err()
.to_string(),
"sql parser error: Expected: ON, found: EOF"
);
}

#[test]
fn parse_drop_rule_rejects_missing_names() {
for (sql, expected) in [
("DROP RULE", "Expected: identifier, found: EOF"),
("DROP RULE r ON", "Expected: identifier, found: EOF"),
("DROP 1", "Expected: COLLATION, CONNECTOR, DATABASE, EXTENSION, FUNCTION, INDEX, OPERATOR, POLICY, PROCEDURE, ROLE, RULE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW, USER or WAREHOUSE after DROP, found: 1"),
] {
assert_eq!(
all_dialects().parse_sql_statements(sql).unwrap_err(),
ParserError::ParserError(expected.to_string()),
);
}
}

#[test]
fn display_drop_rule_propagates_write_errors() {
use core::fmt::{self, Write};

struct RejectingWriter<'a>(&'a str);

impl Write for RejectingWriter<'_> {
fn write_str(&mut self, value: &str) -> fmt::Result {
if value.contains(self.0) {
Err(fmt::Error)
} else {
Ok(())
}
}
}

let statement = all_dialects().verified_stmt("DROP RULE r ON t");
let mut writer = RejectingWriter("DROP");
assert_eq!(write!(writer, "{statement}"), Err(fmt::Error));

let statement = all_dialects().verified_stmt("DROP RULE r ON t CASCADE");
let mut writer = RejectingWriter("CASCADE");
assert_eq!(write!(writer, "{statement}"), Err(fmt::Error));
}

#[test]
fn parse_drop_rule_spans() {
let dialects = all_dialects();
let sql = "DROP RULE r ON t CASCADE";
for dialect in &dialects.dialects {
let statements = Parser::parse_sql(dialect.as_ref(), sql).unwrap();
assert_eq!(
statements[0].span(),
Span::new(Location::new(1, 11), Location::new(1, 17)),
"{dialect:?} {sql}"
);
}
}

#[test]
fn parse_alter_table_column_position() {
let dialects = all_dialects_where(|d| d.supports_alter_column_position());
Expand Down
Loading