Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
56 changes: 56 additions & 0 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4599,3 +4599,59 @@ impl Spanned for AlterOperatorFamily {
Span::empty()
}
}

/// `ALTER OPERATOR CLASS` statement
/// See <https://www.postgresql.org/docs/current/sql-alteropclass.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 AlterOperatorClass {
/// Operator class name (can be schema-qualified)
pub name: ObjectName,
/// Index method (btree, hash, gist, gin, etc.)
pub using: Ident,
/// The operation to perform
pub operation: AlterOperatorClassOperation,
}

/// An [AlterOperatorClass] operation
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AlterOperatorClassOperation {
/// `RENAME TO new_name`
RenameTo { new_name: ObjectName },
/// `OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER }`
OwnerTo(Owner),
/// `SET SCHEMA new_schema`
SetSchema { schema_name: ObjectName },
}

impl fmt::Display for AlterOperatorClass {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ALTER OPERATOR CLASS {} USING {}", self.name, self.using)?;
write!(f, " {}", self.operation)
}
}

impl fmt::Display for AlterOperatorClassOperation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AlterOperatorClassOperation::RenameTo { new_name } => {
write!(f, "RENAME TO {new_name}")
}
AlterOperatorClassOperation::OwnerTo(owner) => {
write!(f, "OWNER TO {owner}")
}
AlterOperatorClassOperation::SetSchema { schema_name } => {
write!(f, "SET SCHEMA {schema_name}")
}
}
}
}

impl Spanned for AlterOperatorClass {
fn span(&self) -> Span {
Span::empty()
}
}
40 changes: 24 additions & 16 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,22 @@ pub use self::dcl::{
};
pub use self::ddl::{
Alignment, AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterOperator,
AlterOperatorFamily, AlterOperatorFamilyOperation, AlterOperatorOperation,
AlterPolicyOperation, AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm,
AlterTableLock, AlterTableOperation, AlterTableType, AlterType, AlterTypeAddValue,
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,
CreateExtension, CreateFunction, CreateIndex, CreateOperator, CreateOperatorClass,
CreateOperatorFamily, CreateTable, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass, DropOperatorFamily,
DropOperatorSignature, DropTrigger, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption,
OperatorArgTypes, OperatorClassItem, OperatorFamilyDropItem, OperatorFamilyItem,
OperatorOption, OperatorPurpose, Owner, Partition, ProcedureParam, ReferentialAction,
RenameTableNameKind, ReplicaIdentity, TagsColumnOption, TriggerObjectKind, Truncate,
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeInternalLength,
AlterOperatorClass, AlterOperatorClassOperation, AlterOperatorFamily,
AlterOperatorFamilyOperation, AlterOperatorOperation, AlterPolicyOperation, AlterSchema,
AlterSchemaOperation, AlterTable, AlterTableAlgorithm, AlterTableLock, AlterTableOperation,
AlterTableType, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition, AlterTypeOperation,
AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef,
ColumnOptions, ColumnPolicy, ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector,
CreateDomain, CreateExtension, CreateFunction, CreateIndex, CreateOperator,
CreateOperatorClass, CreateOperatorFamily, CreateTable, CreateTrigger, CreateView, Deduplicate,
DeferrableInitial, DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass,
DropOperatorFamily, DropOperatorSignature, DropTrigger, GeneratedAs, GeneratedExpressionMode,
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
IdentityPropertyOrder, IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck,
NullsDistinctOption, OperatorArgTypes, OperatorClassItem, OperatorFamilyDropItem,
OperatorFamilyItem, OperatorOption, OperatorPurpose, Owner, Partition, ProcedureParam,
ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption, TriggerObjectKind,
Truncate, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeInternalLength,
UserDefinedTypeRangeOption, UserDefinedTypeRepresentation, UserDefinedTypeSqlDefinitionOption,
UserDefinedTypeStorage, ViewColumnDef,
};
Expand Down Expand Up @@ -3418,6 +3418,11 @@ pub enum Statement {
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteropfamily.html)
AlterOperatorFamily(AlterOperatorFamily),
/// ```sql
/// ALTER OPERATOR CLASS
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteropclass.html)
AlterOperatorClass(AlterOperatorClass),
/// ```sql
/// ALTER ROLE
/// ```
AlterRole {
Expand Down Expand Up @@ -4982,6 +4987,9 @@ impl fmt::Display for Statement {
Statement::AlterOperatorFamily(alter_operator_family) => {
write!(f, "{alter_operator_family}")
}
Statement::AlterOperatorClass(alter_operator_class) => {
write!(f, "{alter_operator_class}")
}
Statement::AlterRole { name, operation } => {
write!(f, "ALTER ROLE {name} {operation}")
}
Expand Down
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ impl Spanned for Statement {
Statement::AlterType { .. } => Span::empty(),
Statement::AlterOperator { .. } => Span::empty(),
Statement::AlterOperatorFamily { .. } => Span::empty(),
Statement::AlterOperatorClass { .. } => Span::empty(),
Statement::AlterRole { .. } => Span::empty(),
Statement::AlterSession { .. } => Span::empty(),
Statement::AttachDatabase { .. } => Span::empty(),
Expand Down
30 changes: 30 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9901,6 +9901,8 @@ impl<'a> Parser<'a> {
Keyword::OPERATOR => {
if self.parse_keyword(Keyword::FAMILY) {
self.parse_alter_operator_family()
} else if self.parse_keyword(Keyword::CLASS) {
self.parse_alter_operator_class()
} else {
self.parse_alter_operator()
}
Expand Down Expand Up @@ -10300,6 +10302,34 @@ impl<'a> Parser<'a> {
}))
}

pub fn parse_alter_operator_class(&mut self) -> Result<Statement, ParserError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add documentation for the function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I add the deny missing doc to clippy?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added the deny missing doc, but there is an impressive amount of missing doc. I am trying to add the missing documentation so to make it impossible for missing documentation in future development.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, it was quite an effort but now #![forbid(missing_docs)] is in place and everything public is documented

let name = self.parse_object_name(false)?;
self.expect_keyword(Keyword::USING)?;
let using = self.parse_identifier()?;

let operation = if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) {
let new_name = self.parse_object_name(false)?;
AlterOperatorClassOperation::RenameTo { new_name }
} else if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) {
let owner = self.parse_owner()?;
AlterOperatorClassOperation::OwnerTo(owner)
} else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) {
let schema_name = self.parse_object_name(false)?;
AlterOperatorClassOperation::SetSchema { schema_name }
} else {
return self.expected_ref(
"RENAME TO, OWNER TO, or SET SCHEMA after ALTER OPERATOR CLASS",
self.peek_token_ref(),
);
};

Ok(Statement::AlterOperatorClass(AlterOperatorClass {
name,
using,
operation,
}))
}

// Parse a [Statement::AlterSchema]
// ALTER SCHEMA [ IF EXISTS ] schema_name
pub fn parse_alter_schema(&mut self) -> Result<Statement, ParserError> {
Expand Down
150 changes: 150 additions & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7531,6 +7531,156 @@ fn parse_alter_operator_family() {
.is_err());
}

#[test]
fn parse_alter_operator_class() {
// Test ALTER OPERATOR CLASS ... RENAME TO
let sql = "ALTER OPERATOR CLASS int_ops USING btree RENAME TO integer_ops";
assert_eq!(
pg_and_generic().verified_stmt(sql),
Statement::AlterOperatorClass(AlterOperatorClass {
name: ObjectName::from(vec![Ident::new("int_ops")]),
using: Ident::new("btree"),
operation: AlterOperatorClassOperation::RenameTo {
new_name: ObjectName::from(vec![Ident::new("integer_ops")]),
},
})
);

// Test ALTER OPERATOR CLASS ... OWNER TO
let sql = "ALTER OPERATOR CLASS int_ops USING btree OWNER TO joe";
assert_eq!(
pg_and_generic().verified_stmt(sql),
Statement::AlterOperatorClass(AlterOperatorClass {
name: ObjectName::from(vec![Ident::new("int_ops")]),
using: Ident::new("btree"),
operation: AlterOperatorClassOperation::OwnerTo(Owner::Ident(Ident::new("joe"))),
})
);

// Test ALTER OPERATOR CLASS ... OWNER TO CURRENT_USER
let sql = "ALTER OPERATOR CLASS int_ops USING btree OWNER TO CURRENT_USER";
assert_eq!(
pg_and_generic().verified_stmt(sql),
Statement::AlterOperatorClass(AlterOperatorClass {
name: ObjectName::from(vec![Ident::new("int_ops")]),
using: Ident::new("btree"),
operation: AlterOperatorClassOperation::OwnerTo(Owner::CurrentUser),
})
);

// Test ALTER OPERATOR CLASS ... SET SCHEMA
let sql = "ALTER OPERATOR CLASS int_ops USING btree SET SCHEMA new_schema";
assert_eq!(
pg_and_generic().verified_stmt(sql),
Statement::AlterOperatorClass(AlterOperatorClass {
name: ObjectName::from(vec![Ident::new("int_ops")]),
using: Ident::new("btree"),
operation: AlterOperatorClassOperation::SetSchema {
schema_name: ObjectName::from(vec![Ident::new("new_schema")]),
},
})
);

// Test with schema-qualified operator class name
let sql = "ALTER OPERATOR CLASS myschema.int_ops USING btree RENAME TO integer_ops";
assert_eq!(
pg_and_generic().verified_stmt(sql),
Statement::AlterOperatorClass(AlterOperatorClass {
name: ObjectName::from(vec![Ident::new("myschema"), Ident::new("int_ops")]),
using: Ident::new("btree"),
operation: AlterOperatorClassOperation::RenameTo {
new_name: ObjectName::from(vec![Ident::new("integer_ops")]),
},
})
);

// Test with different index methods
for index_method in &["hash", "gist", "gin", "spgist", "brin"] {
let sql = format!(
"ALTER OPERATOR CLASS int_ops USING {} RENAME TO integer_ops",
index_method
);
pg_and_generic().verified_stmt(&sql);
}

// Test error cases
// Missing USING clause
assert!(pg()
.parse_sql_statements("ALTER OPERATOR CLASS int_ops RENAME TO integer_ops")
.is_err());

// Invalid operation
assert!(pg()
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree INVALID_OPERATION")
.is_err());

// Missing new name for RENAME TO
assert!(pg()
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree RENAME TO")
.is_err());

// Missing owner for OWNER TO
assert!(pg()
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree OWNER TO")
.is_err());

// Missing schema for SET SCHEMA
assert!(pg()
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree SET SCHEMA")
.is_err());

// Invalid new name
assert!(pg()
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree RENAME TO 123invalid")
.is_err());

// Invalid owner
assert!(pg()
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree OWNER TO 123invalid")
.is_err());

// Invalid schema name
assert!(pg()
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING btree SET SCHEMA 123invalid")
.is_err());

// Missing operator class name
assert!(pg()
.parse_sql_statements("ALTER OPERATOR CLASS USING btree RENAME TO integer_ops")
.is_err());

// Extra tokens at end
assert!(pg()
.parse_sql_statements(
"ALTER OPERATOR CLASS int_ops USING btree RENAME TO integer_ops EXTRA"
)
.is_err());

// Missing index method
assert!(pg()
.parse_sql_statements("ALTER OPERATOR CLASS int_ops RENAME TO integer_ops")
.is_err());

// Invalid index method
assert!(pg()
.parse_sql_statements("ALTER OPERATOR CLASS int_ops USING 123invalid RENAME TO integer_ops")
.is_err());

// Trying to use ADD operation (only valid for OPERATOR FAMILY)
assert!(pg()
.parse_sql_statements(
"ALTER OPERATOR CLASS int_ops USING btree ADD OPERATOR 1 < (INT4, INT2)"
)
.is_err());

// Trying to use DROP operation (only valid for OPERATOR FAMILY)
assert!(pg()
.parse_sql_statements(
"ALTER OPERATOR CLASS int_ops USING btree DROP OPERATOR 1 (INT4, INT2)"
)
.is_err());
}

#[test]
fn parse_drop_operator_family() {
for if_exists in [true, false] {
Expand Down
Loading