You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2025/12/01 15:10:17 UTC
(datafusion-sqlparser-rs) 01/01: Added support for `DROP OPERATOR CLASS` syntax (#2109)
This is an automated email from the ASF dual-hosted git repository.
github-bot pushed a commit to branch gh-readonly-queue/main/pr-2109-89938b9fcbbb983ea4ad7b35213cbc7285ddfb8f
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
commit 367aa6e8d0fe696c16dbeb9a89f7305860e2e7c2
Author: Luca Cappelletti <ca...@gmail.com>
AuthorDate: Mon Dec 1 16:09:52 2025 +0100
Added support for `DROP OPERATOR CLASS` syntax (#2109)
---
src/ast/ddl.rs | 37 ++++++++++++++++
src/ast/mod.rs | 11 ++++-
src/ast/spans.rs | 1 +
src/parser/mod.rs | 21 ++++++++-
tests/sqlparser_postgres.rs | 103 +++++++++++++++++++++++++++++++++++++++-----
5 files changed, 160 insertions(+), 13 deletions(-)
diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs
index 1719c6c0..0df53c14 100644
--- a/src/ast/ddl.rs
+++ b/src/ast/ddl.rs
@@ -4288,3 +4288,40 @@ impl Spanned for DropOperatorFamily {
Span::empty()
}
}
+
+/// `DROP OPERATOR CLASS` statement
+/// See <https://www.postgresql.org/docs/current/sql-dropopclass.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 DropOperatorClass {
+ /// `IF EXISTS` clause
+ pub if_exists: bool,
+ /// One or more operator classes to drop
+ pub names: Vec<ObjectName>,
+ /// Index method (btree, hash, gist, gin, etc.)
+ pub using: Ident,
+ /// `CASCADE or RESTRICT`
+ pub drop_behavior: Option<DropBehavior>,
+}
+
+impl fmt::Display for DropOperatorClass {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "DROP OPERATOR CLASS")?;
+ if self.if_exists {
+ write!(f, " IF EXISTS")?;
+ }
+ write!(f, " {}", display_comma_separated(&self.names))?;
+ write!(f, " USING {}", self.using)?;
+ if let Some(drop_behavior) = &self.drop_behavior {
+ write!(f, " {}", drop_behavior)?;
+ }
+ Ok(())
+ }
+}
+
+impl Spanned for DropOperatorClass {
+ fn span(&self) -> Span {
+ Span::empty()
+ }
+}
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 63a7bebc..44d50c13 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -67,7 +67,7 @@ pub use self::ddl::{
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,
CreateExtension, CreateFunction, CreateIndex, CreateOperator, CreateOperatorClass,
CreateOperatorFamily, CreateTable, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
- DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorFamily,
+ DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass, DropOperatorFamily,
DropOperatorSignature, DropTrigger, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption,
@@ -3586,6 +3586,12 @@ pub enum Statement {
/// <https://www.postgresql.org/docs/current/sql-dropopfamily.html>
DropOperatorFamily(DropOperatorFamily),
/// ```sql
+ /// DROP OPERATOR CLASS [ IF EXISTS ] name USING index_method [ CASCADE | RESTRICT ]
+ /// ```
+ /// Note: this is a PostgreSQL-specific statement.
+ /// <https://www.postgresql.org/docs/current/sql-dropopclass.html>
+ DropOperatorClass(DropOperatorClass),
+ /// ```sql
/// FETCH
/// ```
/// Retrieve rows from a query using a cursor
@@ -4853,6 +4859,9 @@ impl fmt::Display for Statement {
Statement::DropOperatorFamily(drop_operator_family) => {
write!(f, "{drop_operator_family}")
}
+ Statement::DropOperatorClass(drop_operator_class) => {
+ write!(f, "{drop_operator_class}")
+ }
Statement::CreateRole(create_role) => write!(f, "{create_role}"),
Statement::CreateSecret {
or_replace,
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index 994cee97..684cc5b0 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -377,6 +377,7 @@ impl Spanned for Statement {
Statement::DropExtension(drop_extension) => drop_extension.span(),
Statement::DropOperator(drop_operator) => drop_operator.span(),
Statement::DropOperatorFamily(drop_operator_family) => drop_operator_family.span(),
+ Statement::DropOperatorClass(drop_operator_class) => drop_operator_class.span(),
Statement::CreateSecret { .. } => Span::empty(),
Statement::CreateServer { .. } => Span::empty(),
Statement::CreateConnector { .. } => Span::empty(),
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index f3daf628..13c3c5b3 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -6773,9 +6773,11 @@ impl<'a> Parser<'a> {
} else if self.parse_keyword(Keyword::EXTENSION) {
return self.parse_drop_extension();
} else if self.parse_keyword(Keyword::OPERATOR) {
- // Check if this is DROP OPERATOR FAMILY
+ // Check if this is DROP OPERATOR FAMILY or DROP OPERATOR CLASS
return if self.parse_keyword(Keyword::FAMILY) {
self.parse_drop_operator_family()
+ } else if self.parse_keyword(Keyword::CLASS) {
+ self.parse_drop_operator_class()
} else {
self.parse_drop_operator()
};
@@ -7594,6 +7596,23 @@ impl<'a> Parser<'a> {
}))
}
+ /// Parse a [Statement::DropOperatorClass]
+ ///
+ /// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-dropopclass.html)
+ pub fn parse_drop_operator_class(&mut self) -> Result<Statement, ParserError> {
+ let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
+ let names = self.parse_comma_separated(|p| p.parse_object_name(false))?;
+ self.expect_keyword(Keyword::USING)?;
+ let using = self.parse_identifier()?;
+ let drop_behavior = self.parse_optional_drop_behavior();
+ Ok(Statement::DropOperatorClass(DropOperatorClass {
+ if_exists,
+ names,
+ using,
+ drop_behavior,
+ }))
+ }
+
//TODO: Implement parsing for Skewed
pub fn parse_hive_distribution(&mut self) -> Result<HiveDistributionStyle, ParserError> {
if self.parse_keywords(&[Keyword::PARTITIONED, Keyword::BY]) {
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index 14b11052..96e04145 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -23,7 +23,9 @@
mod test_utils;
use helpers::attached_token::AttachedToken;
-use sqlparser::ast::{DataType, DropBehavior, DropOperator, DropOperatorSignature};
+use sqlparser::ast::{
+ DataType, DropBehavior, DropOperator, DropOperatorClass, DropOperatorSignature,
+};
use sqlparser::tokenizer::Span;
use test_utils::*;
@@ -6908,6 +6910,14 @@ fn parse_drop_operator() {
drop_behavior: Some(DropBehavior::Cascade),
})
);
+
+ // Test error: DROP OPERATOR with no operators
+ let sql = "DROP OPERATOR (INTEGER, INTEGER)";
+ assert!(pg().parse_sql_statements(sql).is_err());
+
+ // Test error: DROP OPERATOR IF EXISTS with no operators
+ let sql = "DROP OPERATOR IF EXISTS (INTEGER, INTEGER)";
+ assert!(pg().parse_sql_statements(sql).is_err());
}
#[test]
@@ -6963,13 +6973,84 @@ fn parse_drop_operator_family() {
}
}
}
+
+ // Test error: DROP OPERATOR FAMILY with no names
+ let sql = "DROP OPERATOR FAMILY USING btree";
+ assert!(pg_and_generic().parse_sql_statements(sql).is_err());
+
+ // Test error: DROP OPERATOR FAMILY IF EXISTS with no names
+ let sql = "DROP OPERATOR FAMILY IF EXISTS USING btree";
+ assert!(pg_and_generic().parse_sql_statements(sql).is_err());
+}
+
+#[test]
+fn parse_drop_operator_class() {
+ for if_exists in [true, false] {
+ for drop_behavior in [
+ None,
+ Some(DropBehavior::Cascade),
+ Some(DropBehavior::Restrict),
+ ] {
+ for index_method in &["btree", "hash", "gist", "gin", "spgist", "brin"] {
+ for (names_str, names_vec) in [
+ (
+ "widget_ops",
+ vec![ObjectName::from(vec![Ident::new("widget_ops")])],
+ ),
+ (
+ "myschema.int4_ops",
+ vec![ObjectName::from(vec![
+ Ident::new("myschema"),
+ Ident::new("int4_ops"),
+ ])],
+ ),
+ (
+ "ops1, ops2, schema.ops3",
+ vec![
+ ObjectName::from(vec![Ident::new("ops1")]),
+ ObjectName::from(vec![Ident::new("ops2")]),
+ ObjectName::from(vec![Ident::new("schema"), Ident::new("ops3")]),
+ ],
+ ),
+ ] {
+ let sql = format!(
+ "DROP OPERATOR CLASS{} {} USING {}{}",
+ if if_exists { " IF EXISTS" } else { "" },
+ names_str,
+ index_method,
+ match drop_behavior {
+ Some(behavior) => format!(" {}", behavior),
+ None => String::new(),
+ }
+ );
+ assert_eq!(
+ pg_and_generic().verified_stmt(&sql),
+ Statement::DropOperatorClass(DropOperatorClass {
+ if_exists,
+ names: names_vec.clone(),
+ using: Ident::new(*index_method),
+ drop_behavior,
+ })
+ );
+ }
+ }
+ }
+ }
+
+ // Test error: DROP OPERATOR CLASS with no names
+ let sql = "DROP OPERATOR CLASS USING btree";
+ assert!(pg_and_generic().parse_sql_statements(sql).is_err());
+
+ // Test error: DROP OPERATOR CLASS IF EXISTS with no names
+ let sql = "DROP OPERATOR CLASS IF EXISTS USING btree";
+ assert!(pg_and_generic().parse_sql_statements(sql).is_err());
}
#[test]
fn parse_create_operator_family() {
for index_method in &["btree", "hash", "gist", "gin", "spgist", "brin"] {
assert_eq!(
- pg().verified_stmt(&format!(
+ pg_and_generic().verified_stmt(&format!(
"CREATE OPERATOR FAMILY my_family USING {index_method}"
)),
Statement::CreateOperatorFamily(CreateOperatorFamily {
@@ -6978,7 +7059,7 @@ fn parse_create_operator_family() {
})
);
assert_eq!(
- pg().verified_stmt(&format!(
+ pg_and_generic().verified_stmt(&format!(
"CREATE OPERATOR FAMILY myschema.test_family USING {index_method}"
)),
Statement::CreateOperatorFamily(CreateOperatorFamily {
@@ -7004,7 +7085,7 @@ fn parse_create_operator_class() {
let sql = format!(
"CREATE OPERATOR CLASS {class_name} {default_clause}FOR TYPE INT4 USING btree{family_clause} AS OPERATOR 1 <"
);
- match pg().verified_stmt(&sql) {
+ match pg_and_generic().verified_stmt(&sql) {
Statement::CreateOperatorClass(CreateOperatorClass {
name,
default,
@@ -7034,7 +7115,7 @@ fn parse_create_operator_class() {
}
// Test comprehensive operator class with all fields
- 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)") {
+ 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)") {
Statement::CreateOperatorClass(CreateOperatorClass {
name,
default: true,
@@ -7053,7 +7134,7 @@ fn parse_create_operator_class() {
}
// Test operator with argument types
- match pg().verified_stmt(
+ match pg_and_generic().verified_stmt(
"CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING gist AS OPERATOR 1 < (INT4, INT4)",
) {
Statement::CreateOperatorClass(CreateOperatorClass { ref items, .. }) => {
@@ -7078,7 +7159,7 @@ fn parse_create_operator_class() {
}
// Test operator FOR SEARCH
- match pg().verified_stmt(
+ match pg_and_generic().verified_stmt(
"CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING gist AS OPERATOR 1 < FOR SEARCH",
) {
Statement::CreateOperatorClass(CreateOperatorClass { ref items, .. }) => {
@@ -7122,7 +7203,7 @@ fn parse_create_operator_class() {
}
// Test function with operator class arg types
- match pg().verified_stmt("CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING btree AS FUNCTION 1 (INT4, INT4) btcmp(INT4, INT4)") {
+ match pg_and_generic().verified_stmt("CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING btree AS FUNCTION 1 (INT4, INT4) btcmp(INT4, INT4)") {
Statement::CreateOperatorClass(CreateOperatorClass {
ref items,
..
@@ -7145,11 +7226,11 @@ fn parse_create_operator_class() {
}
// Test function with no arguments (empty parentheses normalizes to no parentheses)
- pg().one_statement_parses_to(
+ pg_and_generic().one_statement_parses_to(
"CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING btree AS FUNCTION 1 my_func()",
"CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING btree AS FUNCTION 1 my_func",
);
- match pg().verified_stmt(
+ match pg_and_generic().verified_stmt(
"CREATE OPERATOR CLASS test_ops FOR TYPE INT4 USING btree AS FUNCTION 1 my_func",
) {
Statement::CreateOperatorClass(CreateOperatorClass { ref items, .. }) => {
@@ -7174,7 +7255,7 @@ fn parse_create_operator_class() {
}
// Test multiple items including STORAGE
- 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") {
+ 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") {
Statement::CreateOperatorClass(CreateOperatorClass {
ref items,
..
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org