Skip to content

Commit 367aa6e

Browse files
Added support for DROP OPERATOR CLASS syntax (#2109)
1 parent 89938b9 commit 367aa6e

File tree

5 files changed

+160
-13
lines changed

5 files changed

+160
-13
lines changed

src/ast/ddl.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4288,3 +4288,40 @@ impl Spanned for DropOperatorFamily {
42884288
Span::empty()
42894289
}
42904290
}
4291+
4292+
/// `DROP OPERATOR CLASS` statement
4293+
/// See <https://www.postgresql.org/docs/current/sql-dropopclass.html>
4294+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
4295+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4296+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
4297+
pub struct DropOperatorClass {
4298+
/// `IF EXISTS` clause
4299+
pub if_exists: bool,
4300+
/// One or more operator classes to drop
4301+
pub names: Vec<ObjectName>,
4302+
/// Index method (btree, hash, gist, gin, etc.)
4303+
pub using: Ident,
4304+
/// `CASCADE or RESTRICT`
4305+
pub drop_behavior: Option<DropBehavior>,
4306+
}
4307+
4308+
impl fmt::Display for DropOperatorClass {
4309+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4310+
write!(f, "DROP OPERATOR CLASS")?;
4311+
if self.if_exists {
4312+
write!(f, " IF EXISTS")?;
4313+
}
4314+
write!(f, " {}", display_comma_separated(&self.names))?;
4315+
write!(f, " USING {}", self.using)?;
4316+
if let Some(drop_behavior) = &self.drop_behavior {
4317+
write!(f, " {}", drop_behavior)?;
4318+
}
4319+
Ok(())
4320+
}
4321+
}
4322+
4323+
impl Spanned for DropOperatorClass {
4324+
fn span(&self) -> Span {
4325+
Span::empty()
4326+
}
4327+
}

src/ast/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ pub use self::ddl::{
6767
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,
6868
CreateExtension, CreateFunction, CreateIndex, CreateOperator, CreateOperatorClass,
6969
CreateOperatorFamily, CreateTable, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
70-
DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorFamily,
70+
DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass, DropOperatorFamily,
7171
DropOperatorSignature, DropTrigger, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
7272
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
7373
IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption,
@@ -3586,6 +3586,12 @@ pub enum Statement {
35863586
/// <https://www.postgresql.org/docs/current/sql-dropopfamily.html>
35873587
DropOperatorFamily(DropOperatorFamily),
35883588
/// ```sql
3589+
/// DROP OPERATOR CLASS [ IF EXISTS ] name USING index_method [ CASCADE | RESTRICT ]
3590+
/// ```
3591+
/// Note: this is a PostgreSQL-specific statement.
3592+
/// <https://www.postgresql.org/docs/current/sql-dropopclass.html>
3593+
DropOperatorClass(DropOperatorClass),
3594+
/// ```sql
35893595
/// FETCH
35903596
/// ```
35913597
/// Retrieve rows from a query using a cursor
@@ -4853,6 +4859,9 @@ impl fmt::Display for Statement {
48534859
Statement::DropOperatorFamily(drop_operator_family) => {
48544860
write!(f, "{drop_operator_family}")
48554861
}
4862+
Statement::DropOperatorClass(drop_operator_class) => {
4863+
write!(f, "{drop_operator_class}")
4864+
}
48564865
Statement::CreateRole(create_role) => write!(f, "{create_role}"),
48574866
Statement::CreateSecret {
48584867
or_replace,

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ impl Spanned for Statement {
377377
Statement::DropExtension(drop_extension) => drop_extension.span(),
378378
Statement::DropOperator(drop_operator) => drop_operator.span(),
379379
Statement::DropOperatorFamily(drop_operator_family) => drop_operator_family.span(),
380+
Statement::DropOperatorClass(drop_operator_class) => drop_operator_class.span(),
380381
Statement::CreateSecret { .. } => Span::empty(),
381382
Statement::CreateServer { .. } => Span::empty(),
382383
Statement::CreateConnector { .. } => Span::empty(),

src/parser/mod.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6773,9 +6773,11 @@ impl<'a> Parser<'a> {
67736773
} else if self.parse_keyword(Keyword::EXTENSION) {
67746774
return self.parse_drop_extension();
67756775
} else if self.parse_keyword(Keyword::OPERATOR) {
6776-
// Check if this is DROP OPERATOR FAMILY
6776+
// Check if this is DROP OPERATOR FAMILY or DROP OPERATOR CLASS
67776777
return if self.parse_keyword(Keyword::FAMILY) {
67786778
self.parse_drop_operator_family()
6779+
} else if self.parse_keyword(Keyword::CLASS) {
6780+
self.parse_drop_operator_class()
67796781
} else {
67806782
self.parse_drop_operator()
67816783
};
@@ -7594,6 +7596,23 @@ impl<'a> Parser<'a> {
75947596
}))
75957597
}
75967598

7599+
/// Parse a [Statement::DropOperatorClass]
7600+
///
7601+
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-dropopclass.html)
7602+
pub fn parse_drop_operator_class(&mut self) -> Result<Statement, ParserError> {
7603+
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
7604+
let names = self.parse_comma_separated(|p| p.parse_object_name(false))?;
7605+
self.expect_keyword(Keyword::USING)?;
7606+
let using = self.parse_identifier()?;
7607+
let drop_behavior = self.parse_optional_drop_behavior();
7608+
Ok(Statement::DropOperatorClass(DropOperatorClass {
7609+
if_exists,
7610+
names,
7611+
using,
7612+
drop_behavior,
7613+
}))
7614+
}
7615+
75977616
//TODO: Implement parsing for Skewed
75987617
pub fn parse_hive_distribution(&mut self) -> Result<HiveDistributionStyle, ParserError> {
75997618
if self.parse_keywords(&[Keyword::PARTITIONED, Keyword::BY]) {

tests/sqlparser_postgres.rs

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
mod test_utils;
2424

2525
use helpers::attached_token::AttachedToken;
26-
use sqlparser::ast::{DataType, DropBehavior, DropOperator, DropOperatorSignature};
26+
use sqlparser::ast::{
27+
DataType, DropBehavior, DropOperator, DropOperatorClass, DropOperatorSignature,
28+
};
2729
use sqlparser::tokenizer::Span;
2830
use test_utils::*;
2931

@@ -6908,6 +6910,14 @@ fn parse_drop_operator() {
69086910
drop_behavior: Some(DropBehavior::Cascade),
69096911
})
69106912
);
6913+
6914+
// Test error: DROP OPERATOR with no operators
6915+
let sql = "DROP OPERATOR (INTEGER, INTEGER)";
6916+
assert!(pg().parse_sql_statements(sql).is_err());
6917+
6918+
// Test error: DROP OPERATOR IF EXISTS with no operators
6919+
let sql = "DROP OPERATOR IF EXISTS (INTEGER, INTEGER)";
6920+
assert!(pg().parse_sql_statements(sql).is_err());
69116921
}
69126922

69136923
#[test]
@@ -6963,13 +6973,84 @@ fn parse_drop_operator_family() {
69636973
}
69646974
}
69656975
}
6976+
6977+
// Test error: DROP OPERATOR FAMILY with no names
6978+
let sql = "DROP OPERATOR FAMILY USING btree";
6979+
assert!(pg_and_generic().parse_sql_statements(sql).is_err());
6980+
6981+
// Test error: DROP OPERATOR FAMILY IF EXISTS with no names
6982+
let sql = "DROP OPERATOR FAMILY IF EXISTS USING btree";
6983+
assert!(pg_and_generic().parse_sql_statements(sql).is_err());
6984+
}
6985+
6986+
#[test]
6987+
fn parse_drop_operator_class() {
6988+
for if_exists in [true, false] {
6989+
for drop_behavior in [
6990+
None,
6991+
Some(DropBehavior::Cascade),
6992+
Some(DropBehavior::Restrict),
6993+
] {
6994+
for index_method in &["btree", "hash", "gist", "gin", "spgist", "brin"] {
6995+
for (names_str, names_vec) in [
6996+
(
6997+
"widget_ops",
6998+
vec![ObjectName::from(vec![Ident::new("widget_ops")])],
6999+
),
7000+
(
7001+
"myschema.int4_ops",
7002+
vec![ObjectName::from(vec![
7003+
Ident::new("myschema"),
7004+
Ident::new("int4_ops"),
7005+
])],
7006+
),
7007+
(
7008+
"ops1, ops2, schema.ops3",
7009+
vec![
7010+
ObjectName::from(vec![Ident::new("ops1")]),
7011+
ObjectName::from(vec![Ident::new("ops2")]),
7012+
ObjectName::from(vec![Ident::new("schema"), Ident::new("ops3")]),
7013+
],
7014+
),
7015+
] {
7016+
let sql = format!(
7017+
"DROP OPERATOR CLASS{} {} USING {}{}",
7018+
if if_exists { " IF EXISTS" } else { "" },
7019+
names_str,
7020+
index_method,
7021+
match drop_behavior {
7022+
Some(behavior) => format!(" {}", behavior),
7023+
None => String::new(),
7024+
}
7025+
);
7026+
assert_eq!(
7027+
pg_and_generic().verified_stmt(&sql),
7028+
Statement::DropOperatorClass(DropOperatorClass {
7029+
if_exists,
7030+
names: names_vec.clone(),
7031+
using: Ident::new(*index_method),
7032+
drop_behavior,
7033+
})
7034+
);
7035+
}
7036+
}
7037+
}
7038+
}
7039+
7040+
// Test error: DROP OPERATOR CLASS with no names
7041+
let sql = "DROP OPERATOR CLASS USING btree";
7042+
assert!(pg_and_generic().parse_sql_statements(sql).is_err());
7043+
7044+
// Test error: DROP OPERATOR CLASS IF EXISTS with no names
7045+
let sql = "DROP OPERATOR CLASS IF EXISTS USING btree";
7046+
assert!(pg_and_generic().parse_sql_statements(sql).is_err());
69667047
}
69677048

69687049
#[test]
69697050
fn parse_create_operator_family() {
69707051
for index_method in &["btree", "hash", "gist", "gin", "spgist", "brin"] {
69717052
assert_eq!(
6972-
pg().verified_stmt(&format!(
7053+
pg_and_generic().verified_stmt(&format!(
69737054
"CREATE OPERATOR FAMILY my_family USING {index_method}"
69747055
)),
69757056
Statement::CreateOperatorFamily(CreateOperatorFamily {
@@ -6978,7 +7059,7 @@ fn parse_create_operator_family() {
69787059
})
69797060
);
69807061
assert_eq!(
6981-
pg().verified_stmt(&format!(
7062+
pg_and_generic().verified_stmt(&format!(
69827063
"CREATE OPERATOR FAMILY myschema.test_family USING {index_method}"
69837064
)),
69847065
Statement::CreateOperatorFamily(CreateOperatorFamily {
@@ -7004,7 +7085,7 @@ fn parse_create_operator_class() {
70047085
let sql = format!(
70057086
"CREATE OPERATOR CLASS {class_name} {default_clause}FOR TYPE INT4 USING btree{family_clause} AS OPERATOR 1 <"
70067087
);
7007-
match pg().verified_stmt(&sql) {
7088+
match pg_and_generic().verified_stmt(&sql) {
70087089
Statement::CreateOperatorClass(CreateOperatorClass {
70097090
name,
70107091
default,
@@ -7034,7 +7115,7 @@ fn parse_create_operator_class() {
70347115
}
70357116

70367117
// Test comprehensive operator class with all fields
7037-
match pg().verified_stmt("CREATE OPERATOR CLASS CAS_btree_ops DEFAULT FOR TYPE CAS USING btree FAMILY CAS_btree_ops AS OPERATOR 1 <, OPERATOR 2 <=, OPERATOR 3 =, OPERATOR 4 >=, OPERATOR 5 >, FUNCTION 1 cas_cmp(CAS, CAS)") {
7118+
match pg_and_generic().verified_stmt("CREATE OPERATOR CLASS CAS_btree_ops DEFAULT FOR TYPE CAS USING btree FAMILY CAS_btree_ops AS OPERATOR 1 <, OPERATOR 2 <=, OPERATOR 3 =, OPERATOR 4 >=, OPERATOR 5 >, FUNCTION 1 cas_cmp(CAS, CAS)") {
70387119
Statement::CreateOperatorClass(CreateOperatorClass {
70397120
name,
70407121
default: true,
@@ -7053,7 +7134,7 @@ fn parse_create_operator_class() {
70537134
}
70547135

70557136
// Test operator with argument types
7056-
match pg().verified_stmt(
7137+
match pg_and_generic().verified_stmt(
70577138
"CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING gist AS OPERATOR 1 < (INT4, INT4)",
70587139
) {
70597140
Statement::CreateOperatorClass(CreateOperatorClass { ref items, .. }) => {
@@ -7078,7 +7159,7 @@ fn parse_create_operator_class() {
70787159
}
70797160

70807161
// Test operator FOR SEARCH
7081-
match pg().verified_stmt(
7162+
match pg_and_generic().verified_stmt(
70827163
"CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING gist AS OPERATOR 1 < FOR SEARCH",
70837164
) {
70847165
Statement::CreateOperatorClass(CreateOperatorClass { ref items, .. }) => {
@@ -7122,7 +7203,7 @@ fn parse_create_operator_class() {
71227203
}
71237204

71247205
// Test function with operator class arg types
7125-
match pg().verified_stmt("CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING btree AS FUNCTION 1 (INT4, INT4) btcmp(INT4, INT4)") {
7206+
match pg_and_generic().verified_stmt("CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING btree AS FUNCTION 1 (INT4, INT4) btcmp(INT4, INT4)") {
71267207
Statement::CreateOperatorClass(CreateOperatorClass {
71277208
ref items,
71287209
..
@@ -7145,11 +7226,11 @@ fn parse_create_operator_class() {
71457226
}
71467227

71477228
// Test function with no arguments (empty parentheses normalizes to no parentheses)
7148-
pg().one_statement_parses_to(
7229+
pg_and_generic().one_statement_parses_to(
71497230
"CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING btree AS FUNCTION 1 my_func()",
71507231
"CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING btree AS FUNCTION 1 my_func",
71517232
);
7152-
match pg().verified_stmt(
7233+
match pg_and_generic().verified_stmt(
71537234
"CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING btree AS FUNCTION 1 my_func",
71547235
) {
71557236
Statement::CreateOperatorClass(CreateOperatorClass { ref items, .. }) => {
@@ -7174,7 +7255,7 @@ fn parse_create_operator_class() {
71747255
}
71757256

71767257
// Test multiple items including STORAGE
7177-
match pg().verified_stmt("CREATE OPERATOR CLASS gist_ops FOR TYPE geometry USING gist AS OPERATOR 1 <<, FUNCTION 1 gist_consistent(internal, geometry, INT4), STORAGE box") {
7258+
match pg_and_generic().verified_stmt("CREATE OPERATOR CLASS gist_ops FOR TYPE geometry USING gist AS OPERATOR 1 <<, FUNCTION 1 gist_consistent(internal, geometry, INT4), STORAGE box") {
71787259
Statement::CreateOperatorClass(CreateOperatorClass {
71797260
ref items,
71807261
..

0 commit comments

Comments
 (0)