Skip to content

Commit 3037ddf

Browse files
Added support for ALTER OPERATOR CLASS syntax
1 parent 7e6d09b commit 3037ddf

File tree

5 files changed

+261
-16
lines changed

5 files changed

+261
-16
lines changed

src/ast/ddl.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4599,3 +4599,59 @@ impl Spanned for AlterOperatorFamily {
45994599
Span::empty()
46004600
}
46014601
}
4602+
4603+
/// `ALTER OPERATOR CLASS` statement
4604+
/// See <https://www.postgresql.org/docs/current/sql-alteropclass.html>
4605+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
4606+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4607+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
4608+
pub struct AlterOperatorClass {
4609+
/// Operator class name (can be schema-qualified)
4610+
pub name: ObjectName,
4611+
/// Index method (btree, hash, gist, gin, etc.)
4612+
pub using: Ident,
4613+
/// The operation to perform
4614+
pub operation: AlterOperatorClassOperation,
4615+
}
4616+
4617+
/// An [AlterOperatorClass] operation
4618+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
4619+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4620+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
4621+
pub enum AlterOperatorClassOperation {
4622+
/// `RENAME TO new_name`
4623+
RenameTo { new_name: ObjectName },
4624+
/// `OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER }`
4625+
OwnerTo(Owner),
4626+
/// `SET SCHEMA new_schema`
4627+
SetSchema { schema_name: ObjectName },
4628+
}
4629+
4630+
impl fmt::Display for AlterOperatorClass {
4631+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4632+
write!(f, "ALTER OPERATOR CLASS {} USING {}", self.name, self.using)?;
4633+
write!(f, " {}", self.operation)
4634+
}
4635+
}
4636+
4637+
impl fmt::Display for AlterOperatorClassOperation {
4638+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4639+
match self {
4640+
AlterOperatorClassOperation::RenameTo { new_name } => {
4641+
write!(f, "RENAME TO {new_name}")
4642+
}
4643+
AlterOperatorClassOperation::OwnerTo(owner) => {
4644+
write!(f, "OWNER TO {owner}")
4645+
}
4646+
AlterOperatorClassOperation::SetSchema { schema_name } => {
4647+
write!(f, "SET SCHEMA {schema_name}")
4648+
}
4649+
}
4650+
}
4651+
}
4652+
4653+
impl Spanned for AlterOperatorClass {
4654+
fn span(&self) -> Span {
4655+
Span::empty()
4656+
}
4657+
}

src/ast/mod.rs

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,22 @@ pub use self::dcl::{
6060
};
6161
pub use self::ddl::{
6262
Alignment, AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterOperator,
63-
AlterOperatorFamily, AlterOperatorFamilyOperation, AlterOperatorOperation,
64-
AlterPolicyOperation, AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm,
65-
AlterTableLock, AlterTableOperation, AlterTableType, AlterType, AlterTypeAddValue,
66-
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
67-
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
68-
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,
69-
CreateExtension, CreateFunction, CreateIndex, CreateOperator, CreateOperatorClass,
70-
CreateOperatorFamily, CreateTable, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
71-
DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass, DropOperatorFamily,
72-
DropOperatorSignature, DropTrigger, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
73-
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
74-
IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption,
75-
OperatorArgTypes, OperatorClassItem, OperatorFamilyDropItem, OperatorFamilyItem,
76-
OperatorOption, OperatorPurpose, Owner, Partition, ProcedureParam, ReferentialAction,
77-
RenameTableNameKind, ReplicaIdentity, TagsColumnOption, TriggerObjectKind, Truncate,
78-
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeInternalLength,
63+
AlterOperatorClass, AlterOperatorClassOperation, AlterOperatorFamily,
64+
AlterOperatorFamilyOperation, AlterOperatorOperation, AlterPolicyOperation, AlterSchema,
65+
AlterSchemaOperation, AlterTable, AlterTableAlgorithm, AlterTableLock, AlterTableOperation,
66+
AlterTableType, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition, AlterTypeOperation,
67+
AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef,
68+
ColumnOptions, ColumnPolicy, ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector,
69+
CreateDomain, CreateExtension, CreateFunction, CreateIndex, CreateOperator,
70+
CreateOperatorClass, CreateOperatorFamily, CreateTable, CreateTrigger, CreateView, Deduplicate,
71+
DeferrableInitial, DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass,
72+
DropOperatorFamily, DropOperatorSignature, DropTrigger, GeneratedAs, GeneratedExpressionMode,
73+
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
74+
IdentityPropertyOrder, IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck,
75+
NullsDistinctOption, OperatorArgTypes, OperatorClassItem, OperatorFamilyDropItem,
76+
OperatorFamilyItem, OperatorOption, OperatorPurpose, Owner, Partition, ProcedureParam,
77+
ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption, TriggerObjectKind,
78+
Truncate, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeInternalLength,
7979
UserDefinedTypeRangeOption, UserDefinedTypeRepresentation, UserDefinedTypeSqlDefinitionOption,
8080
UserDefinedTypeStorage, ViewColumnDef,
8181
};
@@ -3418,6 +3418,11 @@ pub enum Statement {
34183418
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteropfamily.html)
34193419
AlterOperatorFamily(AlterOperatorFamily),
34203420
/// ```sql
3421+
/// ALTER OPERATOR CLASS
3422+
/// ```
3423+
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteropclass.html)
3424+
AlterOperatorClass(AlterOperatorClass),
3425+
/// ```sql
34213426
/// ALTER ROLE
34223427
/// ```
34233428
AlterRole {
@@ -4982,6 +4987,9 @@ impl fmt::Display for Statement {
49824987
Statement::AlterOperatorFamily(alter_operator_family) => {
49834988
write!(f, "{alter_operator_family}")
49844989
}
4990+
Statement::AlterOperatorClass(alter_operator_class) => {
4991+
write!(f, "{alter_operator_class}")
4992+
}
49854993
Statement::AlterRole { name, operation } => {
49864994
write!(f, "ALTER ROLE {name} {operation}")
49874995
}

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ impl Spanned for Statement {
404404
Statement::AlterType { .. } => Span::empty(),
405405
Statement::AlterOperator { .. } => Span::empty(),
406406
Statement::AlterOperatorFamily { .. } => Span::empty(),
407+
Statement::AlterOperatorClass { .. } => Span::empty(),
407408
Statement::AlterRole { .. } => Span::empty(),
408409
Statement::AlterSession { .. } => Span::empty(),
409410
Statement::AttachDatabase { .. } => Span::empty(),

src/parser/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9901,6 +9901,8 @@ impl<'a> Parser<'a> {
99019901
Keyword::OPERATOR => {
99029902
if self.parse_keyword(Keyword::FAMILY) {
99039903
self.parse_alter_operator_family()
9904+
} else if self.parse_keyword(Keyword::CLASS) {
9905+
self.parse_alter_operator_class()
99049906
} else {
99059907
self.parse_alter_operator()
99069908
}
@@ -10300,6 +10302,34 @@ impl<'a> Parser<'a> {
1030010302
}))
1030110303
}
1030210304

10305+
pub fn parse_alter_operator_class(&mut self) -> Result<Statement, ParserError> {
10306+
let name = self.parse_object_name(false)?;
10307+
self.expect_keyword(Keyword::USING)?;
10308+
let using = self.parse_identifier()?;
10309+
10310+
let operation = if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) {
10311+
let new_name = self.parse_object_name(false)?;
10312+
AlterOperatorClassOperation::RenameTo { new_name }
10313+
} else if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) {
10314+
let owner = self.parse_owner()?;
10315+
AlterOperatorClassOperation::OwnerTo(owner)
10316+
} else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) {
10317+
let schema_name = self.parse_object_name(false)?;
10318+
AlterOperatorClassOperation::SetSchema { schema_name }
10319+
} else {
10320+
return self.expected_ref(
10321+
"RENAME TO, OWNER TO, or SET SCHEMA after ALTER OPERATOR CLASS",
10322+
self.peek_token_ref(),
10323+
);
10324+
};
10325+
10326+
Ok(Statement::AlterOperatorClass(AlterOperatorClass {
10327+
name,
10328+
using,
10329+
operation,
10330+
}))
10331+
}
10332+
1030310333
// Parse a [Statement::AlterSchema]
1030410334
// ALTER SCHEMA [ IF EXISTS ] schema_name
1030510335
pub fn parse_alter_schema(&mut self) -> Result<Statement, ParserError> {

tests/sqlparser_postgres.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7531,6 +7531,156 @@ fn parse_alter_operator_family() {
75317531
.is_err());
75327532
}
75337533

7534+
#[test]
7535+
fn parse_alter_operator_class() {
7536+
// Test ALTER OPERATOR CLASS ... RENAME TO
7537+
let sql = "ALTER OPERATOR CLASS int_ops USING btree RENAME TO integer_ops";
7538+
assert_eq!(
7539+
pg_and_generic().verified_stmt(sql),
7540+
Statement::AlterOperatorClass(AlterOperatorClass {
7541+
name: ObjectName::from(vec![Ident::new("int_ops")]),
7542+
using: Ident::new("btree"),
7543+
operation: AlterOperatorClassOperation::RenameTo {
7544+
new_name: ObjectName::from(vec![Ident::new("integer_ops")]),
7545+
},
7546+
})
7547+
);
7548+
7549+
// Test ALTER OPERATOR CLASS ... OWNER TO
7550+
let sql = "ALTER OPERATOR CLASS int_ops USING btree OWNER TO joe";
7551+
assert_eq!(
7552+
pg_and_generic().verified_stmt(sql),
7553+
Statement::AlterOperatorClass(AlterOperatorClass {
7554+
name: ObjectName::from(vec![Ident::new("int_ops")]),
7555+
using: Ident::new("btree"),
7556+
operation: AlterOperatorClassOperation::OwnerTo(Owner::Ident(Ident::new("joe"))),
7557+
})
7558+
);
7559+
7560+
// Test ALTER OPERATOR CLASS ... OWNER TO CURRENT_USER
7561+
let sql = "ALTER OPERATOR CLASS int_ops USING btree OWNER TO CURRENT_USER";
7562+
assert_eq!(
7563+
pg_and_generic().verified_stmt(sql),
7564+
Statement::AlterOperatorClass(AlterOperatorClass {
7565+
name: ObjectName::from(vec![Ident::new("int_ops")]),
7566+
using: Ident::new("btree"),
7567+
operation: AlterOperatorClassOperation::OwnerTo(Owner::CurrentUser),
7568+
})
7569+
);
7570+
7571+
// Test ALTER OPERATOR CLASS ... SET SCHEMA
7572+
let sql = "ALTER OPERATOR CLASS int_ops USING btree SET SCHEMA new_schema";
7573+
assert_eq!(
7574+
pg_and_generic().verified_stmt(sql),
7575+
Statement::AlterOperatorClass(AlterOperatorClass {
7576+
name: ObjectName::from(vec![Ident::new("int_ops")]),
7577+
using: Ident::new("btree"),
7578+
operation: AlterOperatorClassOperation::SetSchema {
7579+
schema_name: ObjectName::from(vec![Ident::new("new_schema")]),
7580+
},
7581+
})
7582+
);
7583+
7584+
// Test with schema-qualified operator class name
7585+
let sql = "ALTER OPERATOR CLASS myschema.int_ops USING btree RENAME TO integer_ops";
7586+
assert_eq!(
7587+
pg_and_generic().verified_stmt(sql),
7588+
Statement::AlterOperatorClass(AlterOperatorClass {
7589+
name: ObjectName::from(vec![Ident::new("myschema"), Ident::new("int_ops")]),
7590+
using: Ident::new("btree"),
7591+
operation: AlterOperatorClassOperation::RenameTo {
7592+
new_name: ObjectName::from(vec![Ident::new("integer_ops")]),
7593+
},
7594+
})
7595+
);
7596+
7597+
// Test with different index methods
7598+
for index_method in &["hash", "gist", "gin", "spgist", "brin"] {
7599+
let sql = format!(
7600+
"ALTER OPERATOR CLASS int_ops USING {} RENAME TO integer_ops",
7601+
index_method
7602+
);
7603+
pg_and_generic().verified_stmt(&sql);
7604+
}
7605+
7606+
// Test error cases
7607+
// Missing USING clause
7608+
assert!(pg()
7609+
.parse_sql_statements("ALTER OPERATOR CLASS int_ops RENAME TO integer_ops")
7610+
.is_err());
7611+
7612+
// Invalid operation
7613+
assert!(pg()
7614+
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree INVALID_OPERATION")
7615+
.is_err());
7616+
7617+
// Missing new name for RENAME TO
7618+
assert!(pg()
7619+
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree RENAME TO")
7620+
.is_err());
7621+
7622+
// Missing owner for OWNER TO
7623+
assert!(pg()
7624+
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree OWNER TO")
7625+
.is_err());
7626+
7627+
// Missing schema for SET SCHEMA
7628+
assert!(pg()
7629+
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree SET SCHEMA")
7630+
.is_err());
7631+
7632+
// Invalid new name
7633+
assert!(pg()
7634+
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree RENAME TO 123invalid")
7635+
.is_err());
7636+
7637+
// Invalid owner
7638+
assert!(pg()
7639+
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree OWNER TO 123invalid")
7640+
.is_err());
7641+
7642+
// Invalid schema name
7643+
assert!(pg()
7644+
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree SET SCHEMA 123invalid")
7645+
.is_err());
7646+
7647+
// Missing operator class name
7648+
assert!(pg()
7649+
.parse_sql_statements("ALTER OPERATOR CLASS USING btree RENAME TO integer_ops")
7650+
.is_err());
7651+
7652+
// Extra tokens at end
7653+
assert!(pg()
7654+
.parse_sql_statements(
7655+
"ALTER OPERATOR CLASS int_ops USING btree RENAME TO integer_ops EXTRA"
7656+
)
7657+
.is_err());
7658+
7659+
// Missing index method
7660+
assert!(pg()
7661+
.parse_sql_statements("ALTER OPERATOR CLASS int_ops RENAME TO integer_ops")
7662+
.is_err());
7663+
7664+
// Invalid index method
7665+
assert!(pg()
7666+
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING 123invalid RENAME TO integer_ops")
7667+
.is_err());
7668+
7669+
// Trying to use ADD operation (only valid for OPERATOR FAMILY)
7670+
assert!(pg()
7671+
.parse_sql_statements(
7672+
"ALTER OPERATOR CLASS int_ops USING btree ADD OPERATOR 1 < (INT4, INT2)"
7673+
)
7674+
.is_err());
7675+
7676+
// Trying to use DROP operation (only valid for OPERATOR FAMILY)
7677+
assert!(pg()
7678+
.parse_sql_statements(
7679+
"ALTER OPERATOR CLASS int_ops USING btree DROP OPERATOR 1 (INT4, INT2)"
7680+
)
7681+
.is_err());
7682+
}
7683+
75347684
#[test]
75357685
fn parse_drop_operator_family() {
75367686
for if_exists in [true, false] {

0 commit comments

Comments
 (0)